ranger-compiler 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,95 @@
1
+ // SPDX-License-Identifier: MIT
2
+ //
3
+ // THE REQUEST WAS ALREADY MADE.
4
+ //
5
+ // A page built with `inline-assets.mjs` starts every asset it knows it will
6
+ // need — its fonts, its catalogues, the document it opens on — in a few
7
+ // synchronous lines in the head, before the body is parsed and long before
8
+ // the application's module has arrived. This is the other half: a module that
9
+ // picks up those responses by name instead of asking for them again.
10
+ //
11
+ // What it is worth, on the PowerPoint editor: fetching them in a queue after
12
+ // the engine had loaded put the first frame at 4813 ms; starting them in the
13
+ // head put it at 2786. Nothing about the old order was necessary — none of
14
+ // those requests needs any code to be running.
15
+ //
16
+ // A page that has no head-started assets (a source directory opened directly,
17
+ // an editor's preview) still works: it asks, as it always did.
18
+
19
+ /** The window's asset table, or an empty one. */
20
+ const table = () => (typeof window === "undefined" ? {} : window.__evgAssets || {});
21
+
22
+ /**
23
+ * The response for an asset, started in the head if it was.
24
+ *
25
+ * A CLONE, never the stored response itself: a body can be read once, and a
26
+ * page that reads one twice — an editor opening the deck it boots on, and
27
+ * again to prove it can reopen what it saved — gets "body stream already
28
+ * read" for the second. The stored response is never consumed, so the clone
29
+ * always succeeds.
30
+ */
31
+ export function responseFor(path) {
32
+ const name = String(path).replace(/^\.\//, "");
33
+ const started = table()[name];
34
+ if (started) {
35
+ return Promise.resolve(started).then((r) => (r instanceof Error ? r : r.clone()));
36
+ }
37
+ return fetch("./" + name);
38
+ }
39
+
40
+ /** An ArrayBuffer the Ranger runtime can read: it wants a DataView beside it. */
41
+ export function asRangerBuffer(ab) {
42
+ ab._view = new DataView(ab);
43
+ return ab;
44
+ }
45
+
46
+ /** An asset as bytes, throwing what the fetch threw rather than a 200 with no body. */
47
+ export async function bytesOf(path) {
48
+ const res = await responseFor(path);
49
+ if (res instanceof Error) throw res;
50
+ if (!res.ok) throw new Error(path + " → " + res.status);
51
+ return asRangerBuffer(await res.arrayBuffer());
52
+ }
53
+
54
+ /** An asset as text, for a catalogue or a stylesheet. */
55
+ export async function textOf(path) {
56
+ const res = await responseFor(path);
57
+ if (res instanceof Error) throw res;
58
+ if (!res.ok) throw new Error(path + " → " + res.status);
59
+ return res.text();
60
+ }
61
+
62
+ /**
63
+ * The same faces again, this time to the BROWSER — and it is the half that is
64
+ * easy to leave out.
65
+ *
66
+ * An EVG page loads its fonts into the app's own font manager, which is what
67
+ * the LAYOUT measures with: how wide a title is, where a line breaks, where a
68
+ * caret goes. Nothing tells `document.fonts` about them, so when the painter
69
+ * rasterises a run through a 2D canvas the browser has never heard of the
70
+ * family and draws its own sans instead — same string, same pixel size,
71
+ * different face, different width, and a caret most of a letter past the end
72
+ * of the word.
73
+ *
74
+ * A page can declare the same files in CSS instead, and that works — but the
75
+ * browser then LOADS THEM AGAIN, because a CSS font request and a `fetch()`
76
+ * are not the same request. Registering the bytes the page already has is the
77
+ * same result without the second download.
78
+ *
79
+ * `faces` is `{ bytes, family, weight, style }`; a face the browser refuses
80
+ * to parse is one family drawn in a substitute, which is bad but is not a
81
+ * reason to leave the page blank.
82
+ */
83
+ export async function registerFaces(faces) {
84
+ if (typeof FontFace !== "function" || typeof document === "undefined" || !document.fonts) return;
85
+ await Promise.all(faces.map(async (f) => {
86
+ if (!f || !f.family) return;
87
+ try {
88
+ const face = new FontFace(f.family, f.bytes, { weight: f.weight || "400", style: f.style || "normal" });
89
+ await face.load();
90
+ document.fonts.add(face);
91
+ } catch (e) {
92
+ console.warn("could not register " + f.family + " with the browser:", e);
93
+ }
94
+ }));
95
+ }
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // WHEN THE VISITOR SEES SOMETHING, and when they can use it. Any EVG page.
5
+ //
6
+ // node lib/evg/web/tools/boot-bench.mjs --url <path> --ready <expr> [...]
7
+ // npm run rt:boot RealTrainer, both engines
8
+ // npm run pptx:boot the PowerPoint editor
9
+ // … -- --fast no throttling, for a local number
10
+ //
11
+ // Options:
12
+ // --url <path> the page, as a path under the repository root
13
+ // --ready <expr> a JavaScript expression that becomes true when the app
14
+ // has PAINTED. Every page has one; it is what its own
15
+ // checks already wait for.
16
+ // --variant <name>=<query> run the same page more than once, with a query
17
+ // each time — how the two engine arrangements are
18
+ // compared. Repeatable; the default is one plain run.
19
+ // --viewport WxH default 390x844
20
+ // --mbps N default 4; --fast turns the throttle off
21
+ // --runs N default 3, and the median is what is printed
22
+ //
23
+ // The ladder in PLAN_WEB_LOADING.md S2 is stated in milliseconds, and a plan
24
+ // stated in milliseconds that nobody measures is a plan stated in adjectives.
25
+ // This drives the real page in Chromium over a throttled connection and takes
26
+ // two times, both of them from the browser's own clock:
27
+ //
28
+ // first paint — anything at all on the screen. With the baked picture in
29
+ // the document (web/snapshot.mjs) this is one round trip and
30
+ // owes nothing to the bundle.
31
+ // first frame — the app has PAINTED. `window.__lastStats` is written by a
32
+ // draw, so it is the first moment the picture on screen is
33
+ // the app's own rather than the build's.
34
+ //
35
+ // It runs the engine on the main thread and in a Worker, because which of
36
+ // those the deployed page should default to is a question with a number.
37
+
38
+ import fs from "node:fs";
39
+ import path from "node:path";
40
+ import { fileURLToPath } from "node:url";
41
+ import { createServer } from "node:http";
42
+ import zlib from "node:zlib";
43
+ import { requireDom, findChromium } from "../../../../gallery/ui/conformance/dom-adapter.mjs";
44
+
45
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
46
+ const ROOT = path.resolve(HERE, "..", "..", "..", "..");
47
+
48
+ const argv = process.argv.slice(2);
49
+ const flag = (name, fallback) => {
50
+ const at = argv.indexOf(name);
51
+ return at >= 0 ? argv[at + 1] : fallback;
52
+ };
53
+ const PAGE = flag("--url");
54
+ const READY = flag("--ready", "window.__lastStats !== undefined");
55
+ if (!PAGE) {
56
+ console.error("usage: boot-bench.mjs --url <path under the repo> [--ready <expr>] …");
57
+ process.exit(2);
58
+ }
59
+ if (!fs.existsSync(path.join(ROOT, PAGE.replace(/^\//, "").split("?")[0]))) {
60
+ console.error(`no page at ${PAGE} — build it first`);
61
+ process.exit(3);
62
+ }
63
+ const VARIANTS = [];
64
+ for (let i = 0; i < argv.length; i += 1) {
65
+ if (argv[i] === "--variant") {
66
+ const [name, query] = (argv[i + 1] || "").split("=");
67
+ VARIANTS.push([name, query || ""]);
68
+ }
69
+ }
70
+ if (!VARIANTS.length) VARIANTS.push(["the page", ""]);
71
+ const [VW, VH] = (flag("--viewport", "390x844").split("x").map(Number));
72
+ const FAST = argv.includes("--fast");
73
+ const RUNS = Number(flag("--runs")) || 3;
74
+ // A mid connection rather than a bad one: enough that bytes are visible in the
75
+ // number, not so little that everything is dominated by the same stall.
76
+ const MBPS = Number(flag("--mbps")) || 4;
77
+ const BYTES_PER_SEC = (MBPS * 1024 * 1024) / 8;
78
+ const LATENCY_MS = 60;
79
+ // And a phone's CPU. Parse and boot are most of what is being compared, and a
80
+ // desktop hides them.
81
+ const CPU_SLOWDOWN = 4;
82
+
83
+ const TYPES = {
84
+ ".html": "text/html", ".js": "text/javascript", ".mjs": "text/javascript",
85
+ ".cjs": "text/javascript", ".css": "text/css", ".json": "application/json",
86
+ };
87
+ // THE THROTTLE IS HERE, not in the browser. Chromium's own network emulation
88
+ // is attached to the page's target and a dedicated Worker's requests go around
89
+ // it — which made the Worker variant look four times faster than it is, on the
90
+ // same bytes. A server that hands out its bytes slowly cannot be gone around.
91
+ //
92
+ // Responses are gzipped, because that is what a real host serves and half of
93
+ // the point of these measurements is how many bytes are on the wire.
94
+ const CHUNK = 16 * 1024;
95
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
96
+ const server = createServer(async (req, res) => {
97
+ const rel = decodeURIComponent(new URL(req.url, "http://x").pathname);
98
+ const file = path.join(ROOT, rel.slice(1));
99
+ if (!file.startsWith(ROOT) || !fs.existsSync(file) || fs.statSync(file).isDirectory()) {
100
+ res.writeHead(404).end("not found");
101
+ return;
102
+ }
103
+ const type = TYPES[path.extname(file)] || "application/octet-stream";
104
+ const raw = fs.readFileSync(file);
105
+ const body = /text|json|javascript/.test(type) ? zlib.gzipSync(raw, { level: 6 }) : raw;
106
+ const headers = { "content-type": type, "content-length": String(body.length) };
107
+ if (body !== raw) headers["content-encoding"] = "gzip";
108
+ if (!FAST) await sleep(LATENCY_MS);
109
+ res.writeHead(200, headers);
110
+ if (FAST) {
111
+ res.end(body);
112
+ return;
113
+ }
114
+ const perChunkMs = (CHUNK / BYTES_PER_SEC) * 1000;
115
+ for (let i = 0; i < body.length; i += CHUNK) {
116
+ res.write(body.subarray(i, i + CHUNK));
117
+ await sleep(perChunkMs);
118
+ }
119
+ res.end();
120
+ });
121
+ await new Promise((r) => server.listen(0, r));
122
+ const port = server.address().port;
123
+
124
+ const { chromium } = requireDom("playwright-core");
125
+ const browser = await chromium.launch({ executablePath: findChromium() });
126
+
127
+ /** One cold load, in a context of its own so nothing is cached from the last. */
128
+ async function once(query) {
129
+ const ctx = await browser.newContext({ viewport: { width: VW, height: VH } });
130
+ const page = await ctx.newPage();
131
+ const cdp = await ctx.newCDPSession(page);
132
+ // The CPU throttle DOES reach the worker (it is a renderer-wide setting);
133
+ // the network one does not, which is why the server holds the bytes back.
134
+ if (!FAST) await cdp.send("Emulation.setCPUThrottlingRate", { rate: CPU_SLOWDOWN });
135
+ const bytes = { total: 0 };
136
+ page.on("response", async (r) => {
137
+ const len = Number(r.headers()["content-length"] || 0);
138
+ bytes.total += len;
139
+ });
140
+ const t0 = Date.now();
141
+ await page.goto(`http://127.0.0.1:${port}${PAGE.startsWith("/") ? "" : "/"}${PAGE}${query}`, { waitUntil: "commit" });
142
+ await page.waitForFunction(READY, null, { timeout: 180000 });
143
+ const wall = Date.now() - t0;
144
+ const paint = await page.evaluate(() => {
145
+ const at = (name) => {
146
+ const e = performance.getEntriesByType("paint").find((x) => x.name === name);
147
+ return e ? Math.round(e.startTime) : null;
148
+ };
149
+ return {
150
+ // `first-paint` is the baked picture: boxes of colour, which is a paint
151
+ // and not "contentful". `first-contentful-paint` waits for the canvas.
152
+ fp: at("first-paint"),
153
+ fcp: at("first-contentful-paint"),
154
+ frame: Math.round(performance.now()),
155
+ timeline: performance.getEntriesByType("resource").map((r) => ({
156
+ name: r.name.split("/").pop(),
157
+ end: Math.round(r.responseEnd),
158
+ })),
159
+ };
160
+ });
161
+ await ctx.close();
162
+ return { ...paint, wall, bytes: bytes.total };
163
+ }
164
+
165
+ const median = (xs) => xs.slice().sort((a, b) => a - b)[Math.floor(xs.length / 2)];
166
+
167
+ console.log(
168
+ FAST ? "no throttling" : `${MBPS} Mbps (served slowly, gzipped), ${LATENCY_MS} ms per request, ${CPU_SLOWDOWN}x CPU`,
169
+ `— ${RUNS} runs each, ${VW}x${VH}`,
170
+ );
171
+ console.log("");
172
+ console.log(" variant first paint contentful app painted bytes");
173
+ for (const [name, query] of VARIANTS) {
174
+ const runs = [];
175
+ for (let i = 0; i < RUNS; i += 1) runs.push(await once(query));
176
+ const fp = median(runs.map((r) => r.fp ?? r.frame));
177
+ const fcp = median(runs.map((r) => r.fcp ?? r.frame));
178
+ const frame = median(runs.map((r) => r.frame));
179
+ const kb = Math.round(median(runs.map((r) => r.bytes)) / 1024);
180
+ console.log(
181
+ ` ${name.padEnd(22)} ${String(fp + " ms").padStart(9)} ${String(fcp + " ms").padStart(10)} ${String(frame + " ms").padStart(9)} ${kb} KB`,
182
+ );
183
+ if (argv.includes("--timeline")) {
184
+ for (const r of runs[0].timeline) console.log(` ${String(r.end + " ms").padStart(8)} ${r.name}`);
185
+ }
186
+ }
187
+
188
+ await browser.close();
189
+ server.close();
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // WRITE THE HEAD THAT STARTS EVERYTHING.
5
+ //
6
+ // node lib/evg/web/tools/inline-assets.mjs \
7
+ // --html DIR/index.html \
8
+ // --start fonts/A.ttf,fonts/B.ttf,presets.txt,deck.pptx \
9
+ // --preload-stamped standalone.mjs,gl/evg-webgl.js \
10
+ // --preload host/app-host.mjs,evg/assets-client.mjs \
11
+ // --stamp <build id> [--cache no-store] [--asset-query v]
12
+ //
13
+ // Every gallery page fetched its assets the same way and in the same wrong
14
+ // order: after its engine had loaded and its module had run, one await at a
15
+ // time. The requests need no code, so they can go out while the code is still
16
+ // coming — and then a visitor waits for the LONGEST of the downloads rather
17
+ // than for their sum.
18
+ //
19
+ // This writes that head, from a list, so the pattern is one build line per
20
+ // page rather than a paragraph of hand-written script in each. The module side
21
+ // is `assets-client.mjs`, which picks the responses up by name.
22
+ //
23
+ // The list is the build's, taken from wherever the page's own font table
24
+ // lives, so it cannot drift from what the module asks for — an asset in one
25
+ // and not the other is either fetched twice or waited for and never started,
26
+ // and neither shows up as an error.
27
+
28
+ import fs from "node:fs";
29
+
30
+ const argv = process.argv.slice(2);
31
+ const flag = (name, fallback) => {
32
+ const at = argv.indexOf(name);
33
+ return at >= 0 ? argv[at + 1] : fallback;
34
+ };
35
+ const list = (name) => (flag(name, "") ? flag(name, "").split(",").filter(Boolean) : []);
36
+
37
+ const html = flag("--html");
38
+ const start = list("--start");
39
+ // TWO LISTS, AND THE DIFFERENCE MATTERS. A preload whose URL is not the URL
40
+ // the module actually imports is not a preload: it is a second download of the
41
+ // same file. Builds stamp some of their module URLs with the build id and
42
+ // leave others alone, so which list a module belongs in is the build's answer,
43
+ // not a guess this tool can make.
44
+ const preloadStamped = list("--preload-stamped");
45
+ const preload = list("--preload");
46
+ const stamp = flag("--stamp", "");
47
+ const openParam = flag("--open-param");
48
+ // A page that fetches its assets with a cache mode, or that carries its own
49
+ // `?v=` on to them, must have the head start THOSE requests and not merely
50
+ // similar ones — a different mode or a different query is a different request,
51
+ // and starting the wrong one leaves the module waiting for a response nobody
52
+ // began while paying for one nobody wanted.
53
+ const cacheMode = flag("--cache");
54
+ const assetQuery = flag("--asset-query");
55
+
56
+ if (!html || !fs.existsSync(html)) {
57
+ console.error("usage: inline-assets.mjs --html <file> --start a,b [--preload-stamped c] [--preload d] [--stamp id] [--open-param open]");
58
+ process.exit(2);
59
+ }
60
+
61
+ const MARK = "<!-- evg:assets -->";
62
+ let src = fs.readFileSync(html, "utf8");
63
+ if (src.includes(MARK)) {
64
+ console.error(`${html} already carries an evg:assets head`);
65
+ process.exit(1);
66
+ }
67
+
68
+ const q = stamp ? `?v=${stamp}` : "";
69
+ const lines = [];
70
+ lines.push(` ${MARK}`);
71
+ lines.push(" <!-- Generated by lib/evg/web/tools/inline-assets.mjs.");
72
+ lines.push(" * Every asset this page needs, started before the body is parsed:");
73
+ lines.push(" * these requests need no code, so they must not wait for any. The");
74
+ lines.push(" * module picks the responses up by name (assets-client.mjs). -->");
75
+ lines.push(" <script>");
76
+ lines.push(" (function () {");
77
+ lines.push(` var files = ${JSON.stringify(start)};`);
78
+ if (openParam) {
79
+ lines.push(" // `?" + openParam + "=` names a file to open instead of the default, and is");
80
+ lines.push(" // restricted to a plain name so a query string cannot make the page");
81
+ lines.push(" // fetch somewhere else — the same rule the module applies.");
82
+ lines.push(` var wanted = new URLSearchParams(location.search).get(${JSON.stringify(openParam)});`);
83
+ lines.push(" if (wanted && /^[\\w.-]+$/.test(wanted)) files.push(wanted);");
84
+ }
85
+ if (assetQuery) {
86
+ lines.push(" // The page's own stamp, carried on to every asset, because that is");
87
+ lines.push(" // the URL the module will ask for.");
88
+ lines.push(` var stamp = new URLSearchParams(location.search).get(${JSON.stringify(assetQuery)});`);
89
+ lines.push(` var q = stamp ? '?${assetQuery}=' + encodeURIComponent(stamp) : '';`);
90
+ } else {
91
+ lines.push(" var q = '';");
92
+ }
93
+ lines.push(` var opts = ${cacheMode ? JSON.stringify({ cache: cacheMode }) : "undefined"};`);
94
+ lines.push(" window.__evgAssets = {};");
95
+ lines.push(" files.forEach(function (f) {");
96
+ lines.push(" window.__evgAssets[f] = fetch('./' + f + q, opts).catch(function (e) { return e; });");
97
+ lines.push(" });");
98
+ lines.push(" })();");
99
+ lines.push(" </script>");
100
+ for (const mod of preloadStamped) {
101
+ lines.push(` <link rel="modulepreload" href="./${mod}${q}" />`);
102
+ }
103
+ for (const mod of preload) {
104
+ lines.push(` <link rel="modulepreload" href="./${mod}" />`);
105
+ }
106
+
107
+ // After the charset, which must stay first, and before anything that could
108
+ // start a download of its own.
109
+ const anchor = /<meta charset="utf-8"\s*\/?>/i;
110
+ if (!anchor.test(src)) {
111
+ console.error(`${html} has no <meta charset="utf-8"> to write after`);
112
+ process.exit(1);
113
+ }
114
+ src = src.replace(anchor, (m) => `${m}\n${lines.join("\n")}`);
115
+ fs.writeFileSync(html, src);
116
+ console.log(
117
+ ` head starts ${start.length} assets${openParam ? " (+?" + openParam + ")" : ""}, ` +
118
+ `preloads ${preloadStamped.length + preload.length}`,
119
+ );
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: MIT
3
+ //
4
+ // MINIFY A COMPILED RANGER BUNDLE.
5
+ //
6
+ // node lib/evg/web/tools/minify.mjs --file DIR/app.js --keep AppWeb
7
+ //
8
+ // The Ranger backend writes for a reader that does not exist: long
9
+ // identifiers, one statement per line, every temporary named. None of it
10
+ // survives to the browser usefully — the source these pages are compiled from
11
+ // is a .rgr file in this repository, and a stack trace through a generated
12
+ // bundle was never the way anyone debugged one. On the PowerPoint editor it
13
+ // is 3.49 MB against 2.27, and 761 KB gzipped against 639.
14
+ //
15
+ // `--keep` names the one string the page needs out of the file: the global its
16
+ // scope publishes. A minifier renames identifiers and cannot rename a
17
+ // property, so the assignment survives — but a bundle that lost it would load
18
+ // silently and fail at the first line that used it, so it is checked.
19
+ //
20
+ // esbuild comes with the conformance host's dependencies, which these builds
21
+ // do not otherwise need. A tree without them still builds: it ships the
22
+ // larger file and says so.
23
+
24
+ import fs from "node:fs";
25
+ import path from "node:path";
26
+ import { createRequire } from "node:module";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
30
+ const ROOT = path.resolve(HERE, "..", "..", "..", "..");
31
+
32
+ const argv = process.argv.slice(2);
33
+ const flag = (name) => {
34
+ const at = argv.indexOf(name);
35
+ return at >= 0 ? argv[at + 1] : undefined;
36
+ };
37
+ const file = flag("--file");
38
+ const keep = flag("--keep");
39
+ if (!file || !fs.existsSync(file)) {
40
+ console.error("usage: minify.mjs --file <bundle.js> [--keep <global>]");
41
+ process.exit(2);
42
+ }
43
+
44
+ let esbuild;
45
+ try {
46
+ esbuild = createRequire(path.join(ROOT, "gallery/ui/conformance/dom/package.json"))("esbuild");
47
+ } catch {
48
+ console.log(" (no esbuild — shipping the unminified bundle; npm run ui:conformance:install)");
49
+ process.exit(0);
50
+ }
51
+
52
+ const src = fs.readFileSync(file, "utf8");
53
+ const out = esbuild.transformSync(src, { minify: true }).code;
54
+ if (keep && !out.includes(keep)) {
55
+ console.error(`minifying lost ${keep} from ${file}`);
56
+ process.exit(1);
57
+ }
58
+ fs.writeFileSync(file, out);
59
+ const kb = (s) => Math.round(s.length / 1024);
60
+ console.log(` minified ${path.basename(file)} ${kb(src)} KB -> ${kb(out)} KB`);
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Build the live bitmap-tracer page.
4
+ *
5
+ * node lib/evg/web/tracer/build.mjs
6
+ * node lib/evg/web/tracer/build.mjs --out path/to/evg/tracer
7
+ *
8
+ * Compiles EvgBitmapTracer to a browser IIFE (no require) and copies the HTML
9
+ * + sample image beside it. The EVG showcase build calls the same steps for
10
+ * /evg/tracer/ on GitHub Pages.
11
+ */
12
+ import { execFileSync } from "node:child_process";
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
18
+ const ROOT = path.resolve(HERE, "../../../..");
19
+ const argv = process.argv.slice(2);
20
+ const outFlag = argv.indexOf("--out");
21
+ const OUT = outFlag >= 0 ? path.resolve(argv[outFlag + 1]) : path.join(HERE, "dist");
22
+ const STAGE = path.join(HERE, ".stage");
23
+
24
+ fs.mkdirSync(STAGE, { recursive: true });
25
+ fs.mkdirSync(OUT, { recursive: true });
26
+
27
+ const env = {
28
+ ...process.env,
29
+ RANGER_LIB: "./compiler/Lang.rgr:./lib/stdops.rgr",
30
+ };
31
+
32
+ const log = execFileSync(
33
+ process.execPath,
34
+ [
35
+ "dist/rgrc.js",
36
+ "-es6",
37
+ "lib/evg/EvgBitmapTracer.rgr",
38
+ `-d=${path.relative(ROOT, STAGE)}`,
39
+ "-o=evg_bitmap_tracer.js",
40
+ "-nodecli",
41
+ ],
42
+ { cwd: ROOT, env, encoding: "utf8" }
43
+ );
44
+
45
+ if (log.includes("Compilation FAILED")) {
46
+ process.stderr.write(log + "\n");
47
+ process.exit(1);
48
+ }
49
+
50
+ const rawPath = path.join(STAGE, "evg_bitmap_tracer.js");
51
+ if (!fs.existsSync(rawPath)) {
52
+ process.stderr.write("compiler wrote no " + rawPath + "\n");
53
+ process.exit(1);
54
+ }
55
+
56
+ let bundle = fs.readFileSync(rawPath, "utf8").replace(/^#![^\n]*\n/, "");
57
+
58
+ {
59
+ const previous = globalThis.require;
60
+ globalThis.require = undefined;
61
+ const found = (0, eval)(
62
+ bundle + "; typeof EvgBitmapTracer + '|' + typeof ImageBuffer + '|' + typeof EvgTraceOptions"
63
+ );
64
+ globalThis.require = previous;
65
+ if (found !== "function|function|function") {
66
+ throw new Error(
67
+ "evg_bitmap_tracer.js missing browser exports (got " + found + ")"
68
+ );
69
+ }
70
+ }
71
+
72
+ const scoped =
73
+ "// GENERATED from lib/evg/EvgBitmapTracer.rgr — do not edit.\n" +
74
+ "(function () {\n" +
75
+ bundle +
76
+ "\n;globalThis.EvgBitmapTracer = EvgBitmapTracer;" +
77
+ "\n;globalThis.EvgTraceOptions = EvgTraceOptions;" +
78
+ "\n;globalThis.EvgTraceLayer = EvgTraceLayer;" +
79
+ "\n;globalThis.ImageBuffer = ImageBuffer;" +
80
+ "\n})();\n";
81
+
82
+ fs.writeFileSync(path.join(OUT, "evg_bitmap_tracer.js"), scoped);
83
+ fs.copyFileSync(path.join(HERE, "index.html"), path.join(OUT, "index.html"));
84
+ fs.copyFileSync(path.join(HERE, "sample.png"), path.join(OUT, "sample.png"));
85
+
86
+ fs.rmSync(STAGE, { recursive: true, force: true });
87
+ process.stdout.write("Wrote tracer page to " + path.relative(ROOT, OUT) + "\n");