Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Built-in __VERSION__ Import for Package Version Detection #56828

Open
aqeelat opened this issue Jan 30, 2025 · 5 comments
Open

Proposal: Built-in __VERSION__ Import for Package Version Detection #56828

aqeelat opened this issue Jan 30, 2025 · 5 comments
Labels
feature request Issues that request new features to be added to Node.js.

Comments

@aqeelat
Copy link

aqeelat commented Jan 30, 2025

What is the problem this feature will solve?

Many packages need to adapt their behavior based on the installed version of a dependency. Currently, there is no built-in way in Node.js to dynamically determine the version of an imported package at runtime. Developers must resort to workarounds such as reading package.json manually, which is cumbersome and inefficient.

For example, the nest-pino package supports both NestJS 10 and 11. However, NestJS 10 uses Express 4, while NestJS 11 uses Express 5. Because of this, a small change is required:

const DEFAULT_ROUTES = [{ path: '*', method: RequestMethod.ALL }];

Should be:

const DEFAULT_ROUTES = [{ path: '/{*splat}', method: RequestMethod.ALL }];

p.s. I'm not affiliated with that package

What is the feature you are proposing to solve the problem?

A convenient way to read the version from package.json. Something like:

import { __VERSION__ } from 'some-package';
console.log(__VERSION__); // e.g. '1.2.3'

This way, 'nest-pino` can adapt to both version by using:

import { __VERSION__ as nestVersion } from '@nestjs/common';

const DEFAULT_ROUTES = [{ path: nestVersion.startsWith('11') ? '/{*splat}' : '*', method: RequestMethod.ALL }];

The beautiful thing is that this will require zero efforts from package maintainers.
If a package already exports a __VERSION__ variable, then that takes precedence over the one supplied by node.

What alternatives have you considered?

  1. node-pino could release a new version to support NestJS 11. However:
    1. this approach does not scale. Consider a scenario where pino releases a new version that is not backwards compatible. Then pino-js will have to either release two new major versions, or drop support for older versions. This introduces a version matrix that's hard to maintain.
    2. it is a hassle especially when the change is very minimal
  2. using a package like pkginfo, but why add another package for something that node can easily provide?
@aqeelat aqeelat added the feature request Issues that request new features to be added to Node.js. label Jan 30, 2025
@github-project-automation github-project-automation bot moved this to Awaiting Triage in Node.js feature requests Jan 30, 2025
@ljharb
Copy link
Member

ljharb commented Jan 30, 2025

Something like this would have to work for both CJS and ESM, and would have to be something the package couldn’t export itself - and what would happen if you tried to import this from something that wasn’t a package specifier?

Reading package.json manually seems like a pretty good approach to me, and #55412 seems like it helps with that.

@RobsonTrasel
Copy link

I think adding __VERSION__ as a built-in import isn’t the best approach. It introduces unnecessary complexity, potential naming conflicts, and doesn’t really work well across both CJS and ESM without modifying how module resolution works. A better approach would be something like getPackageVersion(), which could be part of the node:module API and let us retrieve the version of any installed package without needing to manually read package.json.


Why __VERSION__ is problematic

  1. Naming conflicts – If a package already exports __VERSION__, which one takes precedence?
  2. CJS & ESM inconsistency – Would it be a named export? Would it work with require()? Would it be tree-shakeable?
  3. Performance overhead – This would require modifying module resolution to inject the version dynamically, which isn’t trivial.

How getPackageVersion() would work

Instead of injecting __VERSION__, just expose a function that returns the version when needed:

API usage

ESM

import { getPackageVersion } from 'node:module';

console.log(getPackageVersion('express')); // '4.18.2'
console.log(getPackageVersion('@nestjs/core')); // '10.1.3'
console.log(getPackageVersion('nonexistent-package')); // null

CJS

const { getPackageVersion } = require('node:module');

console.log(getPackageVersion('express')); // '4.18.2'

@RobsonTrasel
Copy link

Instead of modifying the module system to inject __VERSION__, getPackageVersion() would just use the existing module resolution logic to find the package and return its version.

Since findPackageJSON() was already added, this would just be a simple wrapper around it to return the version directly instead of requiring every user to manually read package.json.

Seems like a much cleaner and more flexible solution. Thoughts?

@RobsonTrasel
Copy link

How it could work internally

Something like this:

import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { createRequire } from 'module';

const require = createRequire(import.meta.url);

function getPackageVersion(packageName) {
    try {
        const packagePath = require.resolve(packageName);
        const packageDir = dirname(packagePath.split(`${packageName}/`)[0] + packageName);
        const packageJson = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8'));
        return packageJson.version || null;
    } catch (err) {
        return null;
    }
}

@aduh95
Copy link
Contributor

aduh95 commented Jan 31, 2025

nit: You should be using module.findPackageJSON instead of require.resolve

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js.
Projects
Status: Awaiting Triage
Development

No branches or pull requests

4 participants