Skip to content

Conversation

@LPegasus
Copy link
Contributor

@LPegasus LPegasus commented Jan 6, 2026

Summary

This commit enhances @rushstack/rush-sdk to support named imports when the package is consumed via ESM project.

Resolve #5506

Details

Here's the changes.

libraries/rush-sdk/package.json

  • Added [email protected] as devDependency. Required for extracting named exports during stub generation. Version 2.1.0 is consistent with the built-in version in Node.js.

libraries/rush-sdk/config/jest.config.json

  • Changed test roots and testMatch from "lib-shim" to "lib-commonjs" directory to fix tests.

libraries/rush-sdk/src/generate-stubs.ts

  • Implemented extractNamedExports(source) function to parse CommonJS modules and extract export names.
  • Generated stubs now include named export placeholders (e.g., "exports.foo = exports.bar = undefined").
  • This allows proper named import/destructuring when consuming the package.

libraries/rush-sdk/src/test/snapshots/script.test.ts.snap

  • Updated snapshot to include '_OperationBuildCache' in exported API surface.
  • Removed "install-run" test case snapshot because the stdout now includes local account info which should not be recorded in snapshot. (test remains but assertion changed).

libraries/rush-sdk/src/test/script.test.ts

  • Changed "install-run" test assertion from full snapshot match to partial string match.

libraries/rush-sdk/src/test/build-assets-with-named-exports.test.ts (NEW)

  • Added new test to verify named exports work correctly with ESM imports. Both for lib-shim/ and lib/ assets.

libraries/rush-sdk/webpack.config.js

  • Added webpack.BannerPlugin import from webpack. Reads all export specifiers from @microsoft/rush-lib and inject the dynamic exports placeholder code by using ths BannerPlugin.

The core improvement enables consumers to use both default imports and named destructuring:

// Both styles now work correctly:
const rushSdk = await import('@rushstack/rush-sdk');
rushSdk.RushConfiguration.loadFromConfigurationFile();

const { RushConfiguration } = await import('@rushstack/rush-sdk');
RushConfiguration.loadFromConfigurationFile();

const { default: rushSdk } = await import('@rushstack/rush-sdk');
rushSdk.RushConfiguration.loadFromConfigurationFile();

How it was tested

Unit tests were added.

Impacted documentation

None

This commit enhances @rushstack/rush-sdk to support named imports when
the package is consumed via ESM project.
Comment on lines +61 to +64
{
"name": "cjs-module-lexer",
"allowedCategories": [ "libraries" ]
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go in nonbrowser-approved-packages.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emmm. It's generated automatically. Do you mean that I need to manually move the config into nonbrowser-approved-packages.json?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly.

import * as path from 'node:path';

import { FileSystem, Import, Path } from '@rushstack/node-core-library';
import { initSync, parse } from 'cjs-module-lexer';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already generating types with named exports and ESM modules, so we shouldn't need to try to infer the names from the CJS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does your suggestion involve reading .d.ts files to confirm what named exports are available? Which of the following approaches do you think is better:

  1. ast-grep
  2. TypeScript
  3. RegExp

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at https://www.npmjs.com/package/es-module-lexer and analyzing the ESM modules we already generate in rush-lib.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for using [email protected] here is that Node.js also uses this version, ensuring that the export identifiers we add can remain consistent with the actual Node.js runtime (equivalent to letting Node.js run first to see exactly which export names it can recognize). If different lexer implementations or lexer-executed source code are used, there might be some risk of inconsistency with the actual runtime, potentially leading to runtime issues. However, the risk seems acceptable.

@iclanton iclanton moved this from Needs triage to In Progress in Bug Triage Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[rush] Optimize the export of rush-sdk in Node.js ESModule files.

2 participants