Prepare applications for deployment via CI

A common approach to deploying applications is via docker containers. Some applications can be built into bundles that are environment agnostic, while others depend on OS-specific packages being installed. For these situations, having just bundled code is not enough, we also need to have package.json.

Nx supports the generation of the project's package.json by identifying all the project's dependencies. The generated package.json is created next to the built artifacts (usually at dist/apps/name-of-the-app).

Additionally, we should generate pruned lock file according to the generated package.json. This makes the installation in the container significantly faster as we only need to install a subset of the packages.

Nx offers two varieties of Webpack plugin which can be used to generate package.json.

Basic Plugin Configuration

@nx/webpack/plugin plugin is compatible with a conventional webpack configuration setup which offers a smooth integration with the Webpack CLI. It is configured in the plugins array in nx.json.

nx.json
1{ 2 "plugins": [ 3 { 4 "plugin": "@nx/webpack/plugin", 5 "options": { 6 "buildTargetName": "build", 7 "serveTargetName": "serve", 8 "serveStaticTargetName": "serve-static", 9 "previewStaticTargetName": "preview" 10 } 11 } 12 ] 13} 14

Where build, serve, serve-static and preview in conjunction with your webpack.config.js are the names of the targets that are used to build, serve, and preview the application respectively.

NxWebpackPlugin

The NxWebpackPlugin plugin takes a main entry file and produces a bundle in the output directory as defined in output.path. You can also pass the index option if it is a web app, which will handle outputting scripts and stylesheets in the output file.

To generate a package.json we would declare it in the plugin options.

apps/acme/app/webpack.config.js
1const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); 2const { join } = require('path'); 3 4module.exports = { 5 output: { 6 path: join(__dirname, '../../dist/apps/acme'), 7 }, 8 devServer: { 9 port: 4200, 10 }, 11 plugins: [ 12 new NxAppWebpackPlugin({ 13 tsConfig: './tsconfig.app.json', 14 compiler: 'swc', 15 main: './src/main.tsx', 16 index: '.src/index.html', 17 styles: ['./src/styles.css'], 18 generatePackageJson: true, 19 }), 20 ], 21}; 22

Programmatic usage

If you are using a custom setup that does not support the creation of a package.json or a lock file, you can still use Nx to generate them via The createPackageJson and createLockFile functions which are exported from @nx/js:

If you need to use a custom script, to build your application it should look similar to the following:

scripts/create-package-json.js
1const { 2 createProjectGraphAsync, 3 readCachedProjectGraph, 4 detectPackageManager, 5 writeJsonFile, 6} = require('@nx/devkit'); 7const { 8 createLockFile, 9 createPackageJson, 10 getLockFileName, 11} = require('@nx/js'); 12const { writeFileSync } = require('fs'); 13 14async function main() { 15 const outputDir = 'dist'; // You can replace this with the output directory you want to use 16 // Detect the package manager you are using (npm, yarn, pnpm) 17 const pm = detectPackageManager(); 18 let projectGraph = readCachedProjectGraph(); 19 if (!projectGraph) { 20 projectGraph = await createProjectGraphAsync(); 21 } 22 // You can replace <NX_TASK_TARGET_PROJECT> with the name of the project if you want. 23 const projectName = process.env.NX_TASK_TARGET_PROJECT; 24 const packageJson = createPackageJson(projectName, projectGraph, { 25 isProduction: true, // Used to strip any non-prod dependencies 26 root: projectGraph.nodes[projectName].data.root, 27 }); 28 29 const lockFile = createLockFile( 30 packageJson, 31 projectGraph, 32 detectPackageManager() 33 ); 34 35 const lockFileName = getLockFileName(pm); 36 37 writeJsonFile(`${outputDir}/package.json`, packageJson); 38 writeFileSync(`${outputDir}/${lockFileName}`, lockFile, { 39 encoding: 'utf8', 40 }); 41 42 //... Any additional steps you want to run 43} 44 45main(); 46

Then to run the script, update your package.json to include the following:

package.json
1{ 2 "scripts": { 3 "copy-package-json": "node scripts/create-package-json.js", 4 "custom-build": "nx build && npm run copy-package-json" 5 } 6} 7

Now, you can run npm run custom-build to build your application and generate the package.json and lock file.

You can replace npm with yarn or pnpm if you are using those package managers.

What about Vite?

Vite is a build tool that is great for development, and we want to make sure that it is also great for production. We are working on an NxVitePlugin plugin for Vite that will have parity with the NxWebpackPlugin. Stay tuned for updates.