You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构建后无法访问dotenv-webpack变量,Electron公证脚本变量未定义

Electron构建后公证脚本中process.env变量未定义的问题

我正在开发一个Electron + Webpack + React + TypeScript应用,用electron-builder打包并公证。本地运行时能正常访问process.env变量,但执行构建后的公证脚本时,这些变量却显示未定义。

我的配置文件如下:

webpack.config.mjs

import { fileURLToPath } from 'url';
import CopyPlugin from 'copy-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import Dotenv from 'dotenv-webpack';

/* eslint-disable no-underscore-dangle */
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/* eslint-enable */

const isEnvProduction = process.env.NODE_ENV === 'production';
const isEnvDevelopment = process.env.NODE_ENV === 'development';

const commonConfig = {
  devtool: isEnvDevelopment ? 'eval-cheap-module-source-map' : false,
  mode: isEnvProduction ? 'production' : 'development',
  output: { path: path.join(__dirname, 'dist') },
  node: { __dirname: false, __filename: false },
  plugins: [
    new webpack.NormalModuleReplacementPlugin(/^\S+\/\S+\.js$/, (resource) => {
      // eslint-disable-next-line no-param-reassign
      resource.request = resource.request.replace(/\.js$/, '');
    }),
  ],
  resolve: {
    extensions: ['.js', '.json', '.ts', '.tsx'],
    plugins: [
      new TsconfigPathsPlugin({
        configFile: './tsconfig.json',
        extensions: ['.js', '.json', '.ts', '.tsx'],
      }),
    ],
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'ts-loader',
      },
      {
        test: /\.(scss|css)$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(jpg|png|svg|ico|icns)$/,
        loader: 'file-loader',
        options: {
          name: '[path][name].[ext]',
        },
      },
      {
        test: /\.m?js/,
        type: 'javascript/auto',
      },
      {
        test: /\.m?js/,
        resolve: {
          fullySpecified: false,
        },
      },
      {
        test: /\.wav$|\.mp3$/,
        exclude: /node_modules/,
        loader: 'file-loader',
      },
    ],
  },
};

const mainConfig = merge(commonConfig, {
  entry: './src/main/main.ts',
  target: 'electron-main',
  output: { filename: 'main.bundle.js' },
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: 'package.json',
          to: 'package.json',
          transform: (content, _path) => {
            const jsonContent = JSON.parse(content);
            const electronVersion = jsonContent.devDependencies.electron;

            delete jsonContent.devDependencies;
            delete jsonContent.optionalDependencies;
            delete jsonContent.scripts;
            delete jsonContent.build;

            jsonContent.main = './main.bundle.js';
            jsonContent.scripts = { start: 'electron ./main.bundle.js' };
            jsonContent.devDependencies = { electron: electronVersion };

            return JSON.stringify(jsonContent, undefined, 2);
          },
        },
        { from: '../ableton-js/midi-script', to: './dist/' },
      ],
    }),
    new Dotenv({
     
    }),
  ],
});

const preloadConfig = merge(commonConfig, {
  entry: './src/preload/preload.ts',
  target: 'electron-preload',
  output: { filename: 'preload.bundle.js' },
});

const rendererConfig = merge(commonConfig, {
  entry: './src/renderer/renderer.tsx',
  target: 'electron-renderer',
  output: { filename: 'renderer.bundle.js' },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './public/index.html'),
    }),
  ],
});

export default [mainConfig, preloadConfig, rendererConfig];

package.json相关配置

"build": {
    "afterSign": "./notarize.js",
}
"dependencies": {
    "dotenv-webpack": "^8.0.1",
}

notarize.js

const notarize = require('@electron/notarize');

exports.default = async function notarizing(context) {
  const { electronPlatformName, appOutDir } = context;
  if (electronPlatformName !== 'darwin') {
    return;
  }

  const appName = context.packager.appInfo.productFilename;
  console.log('notarizing', process.env.REACT_APP_APPLEID);

  return await notarize.notarize({
    appBundleId: 'com.yourcompany.yourAppId',
    appPath: `${appOutDir}/${appName}.app`,
    appleId: process.env.REACT_APP_APPLEID,
    appleIdPassword: process.env.APPLEIDPASS,
  });
};

我已经按照dotenv-webpack的官方文档配置了,但还是遇到这个问题。


问题原因及解决方案

核心原因

dotenv-webpack仅负责在Webpack打包过程中把环境变量注入到应用代码(main/renderer/preload),但它不会影响electron-builder的构建流程脚本——notarize.js是electron-builder在打包完成后单独启动的Node脚本,不属于Webpack编译范围,因此无法获取dotenv-webpack注入的变量。

另外,你注释掉了直接调用dotenv.config()的代码,导致Webpack启动前没有将环境变量加载到Node.js的process.env中,而公证脚本依赖Node进程本身的环境变量才能读取到值。

解决方案

方案1:在Webpack配置前加载dotenv

恢复注释的dotenv代码,让Node进程在启动Webpack前就加载环境变量,这样公证脚本就能读取到:

修改webpack.config.mjs开头:

import { fileURLToPath } from 'url';
import path from 'path'; // 补上缺失的path模块导入
import CopyPlugin from 'copy-webpack-plugin';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import webpack from 'webpack';
import { merge } from 'webpack-merge';
import dotenv from 'dotenv';
dotenv.config(); // 恢复这一行

import Dotenv from 'dotenv-webpack';

方案2:构建时直接传入环境变量

如果不想用dotenv,也可以在执行构建命令时直接注入变量:

REACT_APP_APPLEID=你的苹果ID APPLEIDPASS=你的密码 npm run build

这种方式会直接把变量注入到Node进程中,公证脚本可直接读取。

方案3:在公证脚本中单独加载dotenv

仅让公证脚本获取环境变量,可在notarize.js开头添加dotenv加载逻辑:

const dotenv = require('dotenv');
dotenv.config(); // 加载项目根目录的.env文件
const notarize = require('@electron/notarize');

exports.default = async function notarizing(context) {
  // ... 原有代码
};

额外注意事项

  • 确保.env文件在项目根目录,敏感信息(如密码)需加入.gitignore,建议用密钥管理工具而非明文存储。
  • dotenv-webpack仅影响Webpack编译后的应用代码,构建流程相关的脚本依赖Node进程本身的环境变量,需单独处理。

内容的提问来源于stack exchange,提问作者brutus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 08:25:36