27

I have found this Q&A.

I have tried to get the version from the package.json using:

import { version } from './package.json';

but it results in:

package.json' has unsupported extension. The only supported extensions are '.ts',
'.tsx', '.d.ts'.
Community
  • 1
  • 1
030
  • 10,842
  • 12
  • 78
  • 123

5 Answers5

50

The way I like to do this without requiring webpack or any other dependencies is to add a prebuild script to package.json that writes the version to a TypeScript source file that can be imported elsewhere.

  "scripts": {
    "prebuild": "node -p \"'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
    "build": "tsc",
  }

This runs node.js with the -p/--print flag which evaluates the following code:

'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'

and redirects the output to src/version.ts

This results in src/version.ts containing:

export const LIB_VERSION = "1.0.0";

You can then simply import and use this from other files:

import { LIB_VERSION } from './version';

You probably want to exclude src/version.ts from source control so its changes don't litter your change history!

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Tim
  • 7,746
  • 3
  • 49
  • 83
  • 5
    This should be the accepted answer as it is by far the best Typescript way for today – David Ritchie Aug 04 '21 at 10:13
  • 2
    Very excellent. I only recommend that you put this same script on `preinstall` instead of `prebuild`, that way it runs always even if you have multiple build scripts. Otherwise you have to add the `npm run prebuild &&` before each build script – TetraDev Nov 19 '21 at 17:46
  • What if you change the version and don't install again? Many CI scripts will update the version after install and before build. Put it wherever makes sense for you but for the vast majority it'll be prebuild – Tim Nov 19 '21 at 17:51
  • @Tim that is a fair assessment. For some that may work perfectly. Thanks for putting this script together! – TetraDev Nov 19 '21 at 18:00
13

If you are are in a commonJS environment, why not simply use

const pj = require('./package.json')
console.log(pj.version)

EDIT

Since you seem to be using webpack, just add the appropriate loader

   module: {
    loaders: [
        ...
        {test: /\.json$/, loader: 'json-loader'},
        ....

which obviously you need to install using npm install

Bruno Grieder
  • 28,128
  • 8
  • 69
  • 101
  • Now it returns: `You may need an appropriate loader to handle this file type`. Perhaps it could be caused by [this](http://stackoverflow.com/q/33469929/2777965)? – 030 Dec 13 '16 at 18:21
  • `"json-loader": "0.5.4"` had to be added to the package.json, then `npm install` had to be run indeed and now the version is shown. Thank you +1 – 030 Dec 14 '16 at 09:33
  • 4
    This works, but it also includes you whole package.json into your compiled javascript module and exposes it to the public who use your site. You're adding unnecessary code and exposing your site to potential vulnerabilities by cataloging everything you use and whatever settings you've put in package.json – Antony Booth Jun 11 '19 at 19:53
  • 1
    @Daniel San 1/ this is an answer to a specific question, not a security thesis. If you want to read something, you need access to it. 2/ not all apps are 'sites' and no, this is not a 'total security hole' – Bruno Grieder Oct 08 '20 at 20:42
  • 5
    I expect answers not only to be technically correct but have common sense and good practices. Imagine a novice doing what is stated here. It will be a mess – Daniel San Oct 09 '20 at 00:24
  • 2
    @Daniel San. Sure. I humbly only answer questions I am asked, I am not on an evangelizing mission for newbies. But go ahead. Since you "know", answer the question too. Come up with a professional and detailed reason why this a "security hole" and a better approach. Blanket statement like yours without argumentation are of no help – Bruno Grieder Oct 09 '20 at 07:14
  • 4
    See my answer for a less scary version: https://stackoverflow.com/a/67701490/106623 – Tim May 26 '21 at 14:29
2

There are various methods, but the one I found most useful was using ng-node-environment package. This generates a file ./src/environments/base.ts that you can directly reference in your typescript imports. It contains a const object called sharedEnvironment. Your import line looks like this: import sharedEnvironment from './base';

When you execute the command, it looks for all environment variables beginning with 'NG_' and adds them as properties to sharedEnvironment.

So, I created a package.json script that sets the variable and executes the ng-node-environment. For compatibility, I used cross-env to make it platform agnostic (works on windows and *nix)

Here's my package.json script: -

"scripts": {
    "version": "cross-env-shell NG_APPVERSION=$npm_package_version node ./node_modules/ng-node-environment/index.js",
    "another" : "echo hello world"
}

Then run: npm run version to extract the version and put it in ./src/environments/base.ts

I can now access property appversion and use it in my environment.ts.

Note: If I had called the variable NG_APP_Version the property name would be camel cased to appVersion

import sharedEnvironment from './base';

export const environment = {
  production: false,
  logLevel: LogLevel.Error,
  version : sharedEnvironment.appversion,
};

Of course, this way I can add other values from package.json by setting other variables before execution of ng-node-environment, or can add more properties in advance by creating variables called NG_<Something> e.g: NG_GitBranch, NG_ComputerName, NG_CliVersion etc

When developing in Angular and to ensure it's in sync all the time, I start development with a start script: -

"scripts": {
    "version": "cross-env-shell NG_APPVERSION=$npm_package_version node ./node_modules/ng-node-environment/index.js",
    "start": "npm run version && ng serve"
}

So I type: npm start and it syncs the version number, builds and launches

Antony Booth
  • 413
  • 4
  • 5
1

One more way to do this, very similar to Tim's approach.

I needed some additional fields, like the name and the description, which would be tedious to assemble with node -p.

If you're up for a dependency, you can gain some concision and flexibility:

Add jq to your project:

npm install node-jq -D

Then in your package.json, you can add a one-liner to pull whichever fields you want:

  "scripts": {
    ...
    "prebuild": "jq '{version, name, description}' package.json > ./src/pkgInfo.json",
    ...
  }

Then elsewhere in your project, you can access your curated fields:

import pkgInfo from './pkgInfo.json' assert { type: 'json' };

console.log(`I am ${pkgInfo.name} v${pkgInfo.version}, some say I ${pkgInfo.description}`);

kitschpatrol
  • 454
  • 4
  • 6
-1

In order to avoid including the whole package.json file, I usually create a template named buildData.json and put it in a templates folder. I give it the following contents.

{
    "version": [<%= options.version %>]
}

Then in nuxt.config.js, add the template to the build section:

  build: {
    templates: [
      {
        src: "./templates/buildData.json",
        dst: "../buildData.json",
        options: {
          version: require("./package.json").version
        }
      }
    ]
  }

This will generate a buildData.json file that will contain only the version number. The full package.json file is read only during build time and is not included the files sent to the browser.

Finally, add this code wherever you need the version number:

const version = require("@/buildData.json").version;

Make sure the file you give to the require statement is the generated buildData.json and not the one in the templates folder.

Joe M
  • 1
  • 1