Sitelet https://github.com/SeleniumHQ/selenium/pull/17459
Skip to content

[javascript] Create @seleniumhq/atoms npm package from TypeScript atoms - #17459

Open
AutomatedTester wants to merge 2 commits into
trunkfrom
priceless-cannon-76a1b6
Open

AutomatedTester wants to merge 2 commits into
trunkfrom
priceless-cannon-76a1b6

Conversation

@AutomatedTester

@AutomatedTester AutomatedTester commented May 14, 2026 •

Copy link
Copy Markdown
Member

🔗 Related Issues

Builds on the Closure → TypeScript atom migrations (#17458 and siblings), which are already on trunk.

💥 What does this PR do?

Turns the three TypeScript atoms (get-attribute, find-elements, is-displayed) into real ES modules and compiles them down two independent paths from the same sources:

  • IIFE path (language bindings) — ts_project with --module commonjs, then a new cjs-to-iife.js post-processor emits browser-injectable (function(){ … return fn; })(). Python, Ruby, Java and .NET consumers see byte-compatible output and are unchanged.
  • npm path — ts_project with --module esnext, declaration, declarationMap and sourceMap into dist/, packaged as @seleniumhq/atoms with full .d.ts type declarations.

Both paths type-check the same .ts sources, so the published package and the injected atoms can't drift apart.

🔧 Implementation Notes

Atom sources

  • Removed the outer IIFE from get-attribute.ts, find-elements.ts and is-displayed.ts; each now ends in export default <fn>.
  • Added index.ts as the package entry point, re-exporting the three atoms as getAttribute, findElements and isDisplayed.

Build wiring (javascript/atoms/BUILD.bazel)

  • Replaced the three js_run_binary + tsc --module none --outFile invocations with ts_project targets.
  • Replaced the strip_trailing_semicolon binary with cjs_to_iife, which strips the CommonJS boilerplate tsc emits and wraps the remaining body in an IIFE returning the default export.
  • Updated the three wrap-*-as-global.js scripts to parse CJS-compiled input rather than asserting the file starts with (function () {.
  • Added atoms-npm-compiled (ts_project → dist/) and selenium-atoms-package (npm_package, publishable = True, tagged release-artifact).

One tsconfig per ts_project target
Each atom gets its own tsconfig-<atom>.json with a single-entry files list, rather than sharing one tsconfig-cjs.json across all three. A shared tsconfig makes several ts_project targets declare the same file as an input, which Bazel materialises concurrently into the same sandbox path — on Windows that races and fails with EPERM. Per-target configs give each action a disjoint input set. (tsconfig.json at the package root stays shared, because only atoms-npm-compiled uses it.)

Package metadata
javascript/atoms/package.json is promoted to @seleniumhq/atoms with exports entries for the package root and each individual atom.

✅ Verification

  • bazel build //javascript/atoms:selenium-atoms-package //javascript/atoms:atoms-npm-compiled plus the three *-typescript-compiled targets — clean.
  • bazel build //javascript/selenium-webdriver/lib/atoms:all //rb/lib/selenium/webdriver/atoms:all — clean; the generated IIFE output still ends in return get; })();, so the injection path is unchanged for bindings.

💡 Additional Considerations

  • package.json currently declares "main": "./dist/index.cjs" and a "require" condition on every export, but the npm path only emits ESM .js + .d.ts — no .cjs is produced. Either the CJS conditions should be dropped (ESM-only package) or a second ts_project should emit the .cjs outputs before this is published. Flagging for a decision before release.
  • The package version tracks the repo's nightly version string; it will need the usual release-tooling treatment the first time it ships.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude
    • What was generated: Templates
    • I reviewed all AI output and can explain the change

🔄 Types of changes

  • New feature (non-breaking change which adds functionality)
  • Cleanup (build wiring, formatting)

@selenium-ci selenium-ci added B-atoms JavaScript chunks generated by Google closure C-nodejs JavaScript Bindings B-build Includes scripting, bazel and CI integrations labels May 14, 2026
Refactors the three TypeScript atoms (get-attribute, find-elements,
is-displayed) from IIFE closures to proper ES modules with export
default, then produces two compilation targets from the same sources:

- IIFE path (language bindings): ts_project --module commonjs →
  cjs-to-iife.js → browser-injectable (function(){…return fn;})()
  so py/rb/java/dotnet consumers are unchanged.

- npm path: ts_project --module esnext + declaration → dist/ published
  as @seleniumhq/atoms with full TypeScript type declarations.

Key changes:
* Remove outer IIFE from all three atom .ts files; add export default.
* Add cjs-to-iife.js replacing strip-trailing-semicolon.js in the
  IIFE pipeline. Strips CJS boilerplate and wraps the body in an IIFE
  that returns the exported default function.
* Update wrap-as-global scripts to parse CJS-compiled input instead of
  asserting (function () at the start.
* Switch BUILD.bazel from js_run_binary+tsc(--module none --outFile)
  to ts_project for each atom, plus a new npm_package target.
* Add tsconfig.json (esnext, declaration) and tsconfig-cjs.json for
  the two compilation paths.
* Add index.ts as the package entry point.
* Promote javascript/atoms/package.json to @seleniumhq/atoms v4.45.x.
* Add get_attribute_typescript and find-elements-typescript CJS module
  targets to selenium-webdriver/lib/atoms/BUILD.bazel.

All 25 Python unit tests pass; //javascript/atoms:closure-test passes.
…roject targets

When multiple ts_project targets share a single tsconfig with no files/include
restriction, tsc compiles all .ts files for each target. On Windows, Bazel marks
outputs read-only after the first action completes; parallel actions then fail
with TS5033 EPERM trying to overwrite the same files.

Give each per-atom ts_project its own tsconfig that lists only the one .ts file
it is responsible for, eliminating the cross-target output collision.
@AutomatedTester
AutomatedTester force-pushed the priceless-cannon-76a1b6 branch from 0c73842 to 8146e8d Compare October 5, 2026 12:34

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-atoms JavaScript chunks generated by Google closure B-build Includes scripting, bazel and CI integrations C-nodejs JavaScript Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants