David Smiley created SOLR-18485:
-----------------------------------

             Summary: js-client: build with pure Java, not Node
                 Key: SOLR-18485
                 URL: https://issues.apache.org/jira/browse/SOLR-18485
             Project: Solr
          Issue Type: Improvement
          Components: Build, clients - js
            Reporter: David Smiley


The Admin UI depends on the OpenAPI-generated JS client 
({{libs/solr/index.js}}, global {{solrApi}}) (as of 10.1). Building it is the 
only reason the main Solr build needs Node and npm, notwithstanding the 
ref-guide. This causes trouble:
* Builds download a native Node distribution and run {{npm install}} against 
the npm registry. Both might be blocked or need a proxy in corporate/CI 
environments. Gradle's own dependency handling (mirrors, proxies, verification, 
offline mode) doesn't apply to them.
* The build has {{-PdisableJsClient=true}}. With that flag set, the *AngularJS 
Admin UI silently fails to start* (in 10.1): {{solrApi}} is undefined, so 
{{MainController}} can't be created and the page renders raw {{ \{\{ \}\} }} 
templates. Downstream builds that set the flag to avoid Node end up shipping a 
broken UI.
* *Moreover*: It adds a second, differently-behaving toolchain and dependency 
ecosystem to an otherwise pure-JVM build.

Proposal: produce the bundle with pure-Java tooling only, with no Node, no npm 
registry and no native binaries. [Google Closure 
Compiler|https://github.com/google/closure-compiler] (a pure-Java library) 
compiles the generated client. The one runtime JS dependency, superagent, comes 
from [mvnpm|https://mvnpm.org] (npm packages published as Maven jars).

h2. Current state

In {{solr/webapp/js-client}}, using the node-gradle plugin:
# {{:solr:api}} generates ES6 sources with openapi-generator's {{javascript}} 
generator (Java; unchanged by this proposal).
# {{npm install}} installs the generated {{package.json}} deps: {{superagent}} 
at runtime, plus about 20 Babel packages, mocha and sinon.
# {{npm run build}} uses Babel to transpile {{src}} to {{dist}}.
# {{npx browserify dist/index.js -s solrApi}} writes the single-file bundle 
that {{:solr:webapp}} puts into the war.

h2. Proposed approach

The bundle becomes the concatenation of two parts:
# *superagent's own prebuilt browser build* ({{dist/superagent.min.js}}), taken 
as-is from the {{org.mvnpm:superagent}} jar (non-transitive; a WebJar would 
work too). As a plain script it sets the global {{superagent}}.
# *The generated client, compiled by Closure Compiler* 
({{com.google.javascript:closure-compiler}}, run with {{JavaExec}}). Closure 
only processes the generated ES modules plus two tiny build-generated files:
** a shim that resolves {{import superagent from "superagent"}} to the global: 
{{module.exports = globalThis.superagent;}}
** an entry module that keeps the {{solrApi}} global contract that 
{{services.js}} relies on: {{import * as api from './src/index.js'; 
globalThis.solrApi = api;}}
** flags: {{--module_resolution=NODE --process_common_js_modules 
--package_json_entry_names=main --dependency_mode=PRUNE --entry_point=entry.js 
--compilation_level=SIMPLE --isolation_mode=IIFE 
--language_out=ECMASCRIPT_2017}} (or whatever browser baseline the Admin UI 
assumes)

Also:
* *Gradle wiring*: keep the existing {{jsClientBundle}} outgoing artifact, so 
{{:solr:webapp}} doesn't change.
* *Cleanup*: remove the node-gradle plugin usage and the {{browserify}} version 
from the js-client. {{gradle/node.gradle}} stays for the ref guide (Antora) for 
now. Consider removing {{disableJsClient}}: once the bundle no longer needs 
Node, there's little reason to skip it, and skipping it breaks the (original) 
admin UI.
* *Licensing*: superagent (MIT) ships inside the war, so add it to 
{{solr/licenses}} and NOTICE as usual.  This is an _existing_ policy gap -- 
it's in the war today.

h2. Spike results

A throwaway spike against branch_10x confirmed the approach:
* Closure compiles the generated client in about 2.5s with no errors. The one 
remaining warning is the client's guarded {{require('fs')}}, which is dead code 
in browsers.
* The bundle is 332 KB, compared with 1.6 MB (unminified) from Babel+browserify.
* In headless Chrome, the Admin UI starts with no console errors. The rendered 
page text matches the npm-built bundle exactly. {{solrApi}} exposes the same 
191 exports. Real v2 calls ({{SystemApi.getNodeSystemInfo}}, 
{{CoresApi.getAllCoreStatus}}) return the same results.
* Rejected alternative: having Closure bundle superagent and its npm dependency 
tree from source. Closure ignores object-valued {{browser}} maps and 
{{exports}} fields in {{package.json}}, and its CommonJS rewrite breaks 
superagent's {{exports = module.exports}} pattern. Using superagent's prebuilt 
browser build sidesteps all of this.

h2. Validation

* The existing Selenium Admin UI tests ({{-Ptests.selenium=true}}).
* A clean build with no Node on PATH and no access to the npm registry, with 
Gradle offline after the first resolve.

h2. Open questions / risks

* *superagent version*: the generated client asks for {{^5.3.0}}, but mvnpm has 
no 5.x (it has 3.8.3, 7.1.6, 10.x). The spike used 7.1.6 successfully. Options: 
adopt 7.x or 10.x after the Selenium tests pass, or request that mvnpm sync 
5.3.1. Also consider bumping the version openapi-generator emits.
* The generated client's mocha tests aren't run today. Leave them unrun, and 
rely on the Selenium tests.
* Out of scope: the ref guide's Antora/Node usage.

🤖 This plan was AI-generated.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to