1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-05 03:03:26 +00:00

feat(cli): integrate Nx with CLI application

- Add project.json with build, serve, test, and lint targets
- Support both OSS and Bit configurations via Nx configurations
- Maintain backward compatibility with existing npm build scripts
- Update webpack configs to work with both Nx and direct webpack CLI calls
- Enable nx build cli --configuration=[oss|oss-dev|bit|bit-dev] commands
- Enable nx serve cli for development workflow with watch mode
- Preserve all existing npm run build:* commands for compatibility
This commit is contained in:
addisonbeck
2025-08-25 14:35:08 -04:00
parent 9b0bc38459
commit 2266a17a13
8 changed files with 7057 additions and 2715 deletions

80
apps/cli/project.json Normal file
View File

@@ -0,0 +1,80 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"name": "cli",
"projectType": "application",
"sourceRoot": "apps/cli/src",
"tags": ["scope:cli", "type:app"],
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "oss",
"options": {
"outputPath": "dist/apps/cli",
"webpackConfig": "apps/cli/webpack.nx.config.js",
"tsConfig": "apps/cli/tsconfig.json",
"main": "apps/cli/src/bw.ts",
"target": "node",
"compiler": "tsc"
},
"configurations": {
"oss": {
"mode": "production"
},
"oss-dev": {
"mode": "development"
},
"bit": {
"mode": "production",
"webpackConfig": "bitwarden_license/bit-cli/webpack.config.js",
"main": "bitwarden_license/bit-cli/src/bw.ts",
"tsConfig": "bitwarden_license/bit-cli/tsconfig.json"
},
"bit-dev": {
"mode": "development",
"webpackConfig": "bitwarden_license/bit-cli/webpack.config.js",
"main": "bitwarden_license/bit-cli/src/bw.ts",
"tsConfig": "bitwarden_license/bit-cli/tsconfig.json"
}
}
},
"serve": {
"executor": "@nx/webpack:webpack",
"defaultConfiguration": "oss-dev",
"options": {
"outputPath": "dist/apps/cli",
"webpackConfig": "apps/cli/webpack.nx.config.js",
"tsConfig": "apps/cli/tsconfig.json",
"main": "apps/cli/src/bw.ts",
"target": "node",
"compiler": "tsc",
"watch": true
},
"configurations": {
"oss-dev": {
"mode": "development"
},
"bit-dev": {
"mode": "development",
"webpackConfig": "bitwarden_license/bit-cli/webpack.config.js",
"main": "bitwarden_license/bit-cli/src/bw.ts",
"tsConfig": "bitwarden_license/bit-cli/tsconfig.json"
}
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/cli/jest.config.js"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/cli/**/*.ts"]
}
}
}
}

View File

@@ -1,89 +1,2 @@
const path = require("path");
const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const config = require("./config/config");
if (process.env.NODE_ENV == null) {
process.env.NODE_ENV = "development";
}
const ENV = (process.env.ENV = process.env.NODE_ENV);
const envConfig = config.load(ENV);
config.log(envConfig);
const moduleRules = [
{
test: /\.ts$/,
use: "ts-loader",
exclude: path.resolve(__dirname, "node_modules"),
},
];
const plugins = [
new CopyWebpackPlugin({
patterns: [{ from: "./src/locales", to: "locales" }],
}),
new webpack.DefinePlugin({
"process.env.BWCLI_ENV": JSON.stringify(ENV),
}),
new webpack.BannerPlugin({
banner: "#!/usr/bin/env node",
raw: true,
}),
new webpack.IgnorePlugin({
resourceRegExp: /^encoding$/,
contextRegExp: /node-fetch/,
}),
new webpack.EnvironmentPlugin({
ENV: ENV,
BWCLI_ENV: ENV,
FLAGS: envConfig.flags,
DEV_FLAGS: envConfig.devFlags,
}),
new webpack.IgnorePlugin({
resourceRegExp: /canvas/,
contextRegExp: /jsdom$/,
}),
];
const webpackConfig = {
mode: ENV,
target: "node",
devtool: ENV === "development" ? "eval-source-map" : "source-map",
node: {
__dirname: false,
__filename: false,
},
entry: {
bw: "./src/bw.ts",
},
optimization: {
minimize: false,
},
resolve: {
extensions: [".ts", ".js"],
symlinks: false,
modules: [path.resolve("../../node_modules")],
plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json" })],
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "build"),
clean: true,
},
module: { rules: moduleRules },
plugins: plugins,
externals: [
nodeExternals({
modulesDir: "../../node_modules",
allowlist: [/@bitwarden/],
}),
],
experiments: {
asyncWebAssembly: true,
},
};
module.exports = webpackConfig;
// For backward compatibility with existing npm scripts
module.exports = require("./webpack.npm.config.js");

View File

@@ -0,0 +1,23 @@
const path = require("path");
const { getSharedConfig } = require("./webpack.shared");
// Original npm/webpack CLI logic
if (process.env.NODE_ENV == null) {
process.env.NODE_ENV = "development";
}
const ENV = (process.env.ENV = process.env.NODE_ENV);
const mode = ENV;
// npm-specific path configuration
const options = {
env: ENV,
mode: mode,
entryPoint: "./src/bw.ts",
outputPath: path.resolve(__dirname, "build"),
modulesPath: [path.resolve("../../node_modules")],
tsconfigPath: "./tsconfig.json",
localesPath: "./src/locales",
externalsModulesDir: "../../node_modules",
};
module.exports = getSharedConfig(options);

View File

@@ -0,0 +1,25 @@
const path = require("path");
const { getSharedConfig } = require("./webpack.shared");
module.exports = (webpackConfig, context) => {
// Set environment based on context mode
const mode = context.options.mode || "development";
if (process.env.NODE_ENV == null) {
process.env.NODE_ENV = mode;
}
const ENV = (process.env.ENV = process.env.NODE_ENV);
// Nx-specific path configuration
const options = {
env: ENV,
mode: mode,
entryPoint: context.options.main || "apps/cli/src/bw.ts",
outputPath: path.resolve(context.context.root, context.options.outputPath),
modulesPath: [path.resolve("node_modules")],
tsconfigPath: "tsconfig.base.json",
localesPath: "apps/cli/src/locales",
externalsModulesDir: "node_modules",
};
return getSharedConfig(options);
};

110
apps/cli/webpack.shared.js Normal file
View File

@@ -0,0 +1,110 @@
const path = require("path");
const webpack = require("webpack");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
const config = require("./config/config");
const getSharedConfig = (options) => {
const {
env,
mode,
entryPoint,
outputPath,
modulesPath,
tsconfigPath,
localesPath,
externalsModulesDir,
} = options;
const envConfig = config.load(env);
config.log(envConfig);
return {
mode: mode,
target: "node",
devtool: env === "development" ? "eval-source-map" : "source-map",
node: {
__dirname: false,
__filename: false,
},
entry: {
bw: entryPoint,
},
optimization: {
minimize: false,
},
resolve: {
extensions: [".ts", ".js"],
symlinks: false,
modules: modulesPath,
plugins: [new TsconfigPathsPlugin({ configFile: tsconfigPath })],
},
output: {
filename: "[name].js",
path: outputPath,
clean: true,
},
module: { rules: getModuleRules() },
plugins: getPlugins(env, envConfig, localesPath),
externals: getExternals(externalsModulesDir),
experiments: {
asyncWebAssembly: true,
},
};
};
const getModuleRules = () => {
return [
{
test: /\.ts$/,
use: "ts-loader",
exclude: path.resolve(__dirname, "node_modules"),
},
];
};
const getPlugins = (env, envConfig, localesPath) => {
return [
new CopyWebpackPlugin({
patterns: [{ from: localesPath, to: "locales" }],
}),
new webpack.DefinePlugin({
"process.env.BWCLI_ENV": JSON.stringify(env),
}),
new webpack.BannerPlugin({
banner: "#!/usr/bin/env node",
raw: true,
}),
new webpack.IgnorePlugin({
resourceRegExp: /^encoding$/,
contextRegExp: /node-fetch/,
}),
new webpack.EnvironmentPlugin({
ENV: env,
BWCLI_ENV: env,
FLAGS: envConfig.flags,
DEV_FLAGS: envConfig.devFlags,
}),
new webpack.IgnorePlugin({
resourceRegExp: /canvas/,
contextRegExp: /jsdom$/,
}),
];
};
const getExternals = (externalsModulesDir) => {
return [
nodeExternals({
modulesDir: externalsModulesDir,
allowlist: [/@bitwarden/],
}),
];
};
module.exports = {
getSharedConfig,
getModuleRules,
getPlugins,
getExternals,
};

View File

@@ -1,12 +1,46 @@
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
// Re-use the OSS CLI webpack config
const webpackConfig = require("../../apps/cli/webpack.config");
module.exports = (webpackConfig, context) => {
// Check if this is being called by Nx or directly by webpack CLI
const isNxBuild = !!(context && context.options);
// Update paths to use the bit-cli entrypoint and tsconfig
webpackConfig.entry = { bw: "../../bitwarden_license/bit-cli/src/bw.ts" };
webpackConfig.resolve.plugins = [
new TsconfigPathsPlugin({ configFile: "../../bitwarden_license/bit-cli/tsconfig.json" }),
];
let config;
module.exports = webpackConfig;
if (isNxBuild) {
// Use Nx configuration as base
const nxConfig = require("../../apps/cli/webpack.nx.config.js");
config = nxConfig(webpackConfig, context);
// Apply bit-cli specific modifications for Nx builds
config.entry = { bw: context.options.main || "bitwarden_license/bit-cli/src/bw.ts" };
config.resolve.plugins = [new TsconfigPathsPlugin({ configFile: "tsconfig.base.json" })];
// Update the locales path for bit-cli in Nx context
const copyPlugin = config.plugins.find(
(plugin) => plugin.constructor.name === "CopyWebpackPlugin",
);
if (copyPlugin) {
copyPlugin.patterns = [{ from: "bitwarden_license/bit-cli/src/locales", to: "locales" }];
}
} else {
// Use npm configuration as base
const npmConfig = require("../../apps/cli/webpack.npm.config.js");
config = { ...npmConfig };
// Apply bit-cli specific modifications for npm builds
config.entry = { bw: "../../bitwarden_license/bit-cli/src/bw.ts" };
config.resolve.plugins = [
new TsconfigPathsPlugin({ configFile: "../../bitwarden_license/bit-cli/tsconfig.json" }),
];
// Update the locales path for bit-cli (relative to bit-cli directory)
const copyPlugin = config.plugins.find(
(plugin) => plugin.constructor.name === "CopyWebpackPlugin",
);
if (copyPlugin) {
copyPlugin.patterns = [{ from: "./src/locales", to: "locales" }];
}
}
return config;
};

9392
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -170,6 +170,7 @@
"@nx/eslint": "21.3.11",
"@nx/jest": "21.3.11",
"@nx/js": "21.3.11",
"@nx/webpack": "21.3.11",
"big-integer": "1.6.52",
"braintree-web-drop-in": "1.44.0",
"buffer": "6.0.3",