Passer des variables dépendantes de l'environnement dans webpack

306

J'essaie de convertir une application angulaire de gulp en webpack. dans gulp, j'utilise gulp-preprocess pour remplacer certaines variables dans la page html (par exemple le nom de la base de données) en fonction du NODE_ENV. Quelle est la meilleure façon d'obtenir un résultat similaire avec webpack?

kpg
la source
1
L'alias a-t-il fonctionné pour vous?
Juho Vepsäläinen
1
@bebraw: avant de pouvoir me familiariser avec les alias, j'ai implémenté l'autre solution que vous avez suggérée basée sur DefinePlugin (). Je vois maintenant que l'alias serait une meilleure solution et sera probablement refactorisé un jour - merci. Si vous souhaitez inclure vos deux solutions dans une réponse, je l'accepterai avec plaisir.
kpg
2
A été dirigé ici via le message de la console. Comment résoudre ce problème dans Browserify?
GN.
2
Cette question essaie-t-elle de configurer le SPA au moment de la construction ou du chargement? Je note deux types de configuration pour les SPA: 1) le mode de développement ou de production, et 2) l'environnement de déploiement, par exemple le développement, le transfert, la production. Je pense que NODE_ENV peut être utilisé pour configurer pour (1) au moment de la construction, mais comment pouvons-nous configurer pour (2) au déploiement, par exemple en configurant un mode de production pour différents environnements de déploiement. J'espère que cela est pertinent pour cette question.
Ashley Aitken
1
@AshleyAitken Grande question à laquelle je n'ai pas trouvé de réponse sur ce sujet (peut-être que je l'ai manqué), mais j'ai posté ce nouveau sujet: stackoverflow.com/questions/44464504/…
David Tesar

Réponses:

427

Il existe deux moyens de base pour y parvenir.

DefinePlugin

new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),

Notez que cela remplacera simplement les correspondances "telles quelles". C'est pourquoi la chaîne est dans le format qu'elle est. Vous pourriez avoir une structure plus complexe, comme un objet là-bas, mais vous avez l'idée.

EnvironmentPlugin

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPluginutilise en DefinePlugininterne et mappe les valeurs d'environnement pour les coder. Syntaxe du terser.

Alias

Vous pouvez également utiliser la configuration via un module avec alias . Du côté des consommateurs, cela ressemblerait à ceci:

var config = require('config');

La configuration elle-même pourrait ressembler à ceci:

resolve: {
    alias: {
        config: path.join(__dirname, 'config', process.env.NODE_ENV)
    }
}

Disons que process.env.NODE_ENVc'est development. Cela correspondrait ./config/development.jsalors. Le module auquel il est mappé peut exporter une configuration comme celle-ci:

module.exports = {
    testing: 'something',
    ...
};
Juho Vepsäläinen
la source
3
Merci d'avoir souligné le fait qu'il remplace les correspondances "en l'état". J'avais du mal pendant un certain temps à comprendre pourquoi mon code lançait une erreur et c'était parce que je JSON.stringify()
n'emballais
4
Si vous utilisez ES2015, vous pouvez également utiliser l'interpolation de chaînes -'process.env.NODE_ENV': `"${process.env.NODE_ENV || 'development'}"`
user2688473
1
@ tybro0103 JSON.stringify('development')tel quel pourrait ne pas être vraiment utile. Au lieu de cela JSON.stringify(someVariable)peut être tout à fait!
superjos
1
Vous devriez vous y mettre NODE_ENV. Comment définir cela dépend de votre plate-forme.
Juho Vepsäläinen
1
@AnyulRivas Ouais. React utilise le process.env.NODE_ENVmodèle et cela fonctionne.
Juho Vepsäläinen
109

Juste une autre option, si vous souhaitez utiliser uniquement une interface cli, utilisez simplement l' defineoption de webpack. J'ajoute le script suivant dans mon package.json:

"build-production": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors"

Je dois donc juste courir npm run build-production.

zer0chain
la source
2
Existe-t-il une documentation à ce sujet? Je ne peux pas Google - définir :(
Richard
5
Pour webpack @ 2, "-p" est déjà un raccourci pour --optimize-minimiser --define process.env.NODE_ENV = "production"
okm
@okm Docs mentionne -p Est égal à --optimize-minimise --optimize-occurrenceence-order, donc aucune mention de --define process.env.NODE_ENV = "production". Est-ce quelque chose qui a été supprimé?
Nader Ghanbari
1
@NaderHadjiGhanbari C'est dans la version 2 de webpack webpack.js.org/api/cli/#shortcuts
okm
73

J'ai étudié quelques options sur la façon de définir des variables spécifiques à l'environnement et je me suis retrouvé avec ceci:

J'ai actuellement 2 configurations webpack:

webpack.production.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('production'),
    'API_URL': JSON.stringify('http://localhost:8080/bands')
  }
}),

webpack.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('development'),
    'API_URL': JSON.stringify('http://10.10.10.10:8080/bands')
  }
}),

Dans mon code, j'obtiens la valeur de API_URL de cette manière (brève):

const apiUrl = process.env.API_URL;

EDIT 3 novembre 2016

Les documents Webpack ont ​​un exemple: https://webpack.js.org/plugins/define-plugin/#usage

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify("5fa3b9"),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: "1+1",
    "typeof window": JSON.stringify("object")
})

Avec ESLint, vous devez autoriser spécifiquement les variables non définies dans le code, si vous avez une no-undefrègle. http://eslint.org/docs/rules/no-undef comme ceci:

/*global TWO*/
console.log('Running App version ' + TWO);

EDIT 7 septembre 2017 (spécifique à Create-React-App)

Si vous ne souhaitez pas trop configurer, consultez Create-React-App: Create-React-App - Ajout de variables d'environnement personnalisées . Sous le capot, CRA utilise quand même Webpack.

thevangelist
la source
2
Avez-vous constaté que cela empêchait la transmission de variables d'environnement au moment de l'exécution? Si vous remplacez l'ensemble de process.envne pas alors process.env.PORTpar exemple à résoudre undefinedlors de la construction de webpack qui signifie que vous ne pouvez plus passer outre le port de l'environnement?
djskinner
Merci beaucoup. Enfin une réponse compréhensible sur cette question!
Dave Sag
qu'est-ce que le processus? d'où vient-il? s'il s'agit d'un objet nœud, comment peut-il entrer dans le navigateur?
Daniel Birowsky Popeski
Ceci est une solution terrible, vous avez deux webpack.configs presque entièrement identiques à l'exception des paramètres NODE_ENV et API_URL
Brian Ogden
1
@BrianOgden Oui, c'est vrai, vous devriez utiliser quelque chose comme webpack-merge pour cela: npmjs.com/package/webpack-merge - C'est un peu hors de portée pour cette question OMI.
thevangelist
24

Vous pouvez passer n'importe quel argument de ligne de commande sans plugins supplémentaires en utilisant --envdepuis webpack 2:

webpack --config webpack.config.js --env.foo=bar

Utilisation de la variable dans webpack.config.js:

module.exports = function(env) {
    if (env.foo === 'bar') {
        // do something
    }
}

La source

andruso
la source
22

Vous pouvez utiliser directement EnvironmentPlugindans disponiblewebpack pour avoir accès à n'importe quelle variable d'environnement pendant la transpilation.

Il vous suffit de déclarer le plugin dans votre webpack.config.jsfichier:

var webpack = require('webpack');

module.exports = {
    /* ... */
    plugins = [
        new webpack.EnvironmentPlugin(['NODE_ENV'])
    ]
};

Notez que vous devez déclarer explicitement le nom des variables d'environnement que vous souhaitez utiliser.

Kuhess
la source
4
Il y a un exemple dans les documents webpack avec ce cas d'utilisation très. github.com/webpack/docs/wiki/list-of-plugins#environmentplugin
Technetium
1
Si vous souhaitez placer vos variables d'environnement dans un fichier .env, vous pouvez utiliser le package dotenv et l'initialiser dans webpack.config.js. npmjs.com/package/dotenv
Justin McCandless
13

Pour ajouter personnellement au tas de réponses, je préfère ce qui suit:

const webpack = require('webpack');
const prod = process.argv.indexOf('-p') !== -1;

module.exports = {
  ...
  plugins: [
    new webpack.DefinePlugin({
      process: {
        env: {
          NODE_ENV: prod? `"production"`: '"development"'
        }
      }
    }),
    ...
  ]
};

En utilisant ceci, il n'y a pas de problème de variable env funky ou de problème multiplateforme (avec les vars env). Tout ce que vous faites est d'exécuter la normale webpackou webpack -ppour le développement ou la production respectivement.

Référence: problème Github

Goblinlord
la source
Lors de la définition des valeurs de processus préfèrent 'process.env.NODE_ENV': JSON.stringify('production')plus process: { env: { NODE_ENV: JSON.stringify('production') } }. L'utilisation de ce dernier écrasera l'objet de processus, ce qui peut rompre la compatibilité avec certains modules qui s'attendent à ce que d'autres valeurs de l'objet de processus soient définies.
slorenzo
13

Puisque ma modification sur le post ci - dessus par l'évangéliste n'a pas été approuvée , la publication d'informations supplémentaires

Si vous souhaitez choisir la valeur de package.json comme un numéro de version défini et y accéder via DefinePlugin dans Javascript.

{"version": "0.0.1"}

Ensuite, importez package.json dans le webpack.config respectif , accédez à l'attribut à l'aide de la variable d'importation, puis utilisez l'attribut dans DefinePlugin .

const PACKAGE = require('../package.json');
const _version = PACKAGE.version;//Picks the version number from package.json

Par exemple, certaines configurations sur webpack.config utilisent METADATA pour DefinePlugin:

const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, {
  host: HOST,
  port: PORT,
  ENV: ENV,
  HMR: HMR,
  RELEASE_VERSION:_version//Version attribute retrieved from package.json
});

new DefinePlugin({
        'ENV': JSON.stringify(METADATA.ENV),
        'HMR': METADATA.HMR,
        'process.env': {
          'ENV': JSON.stringify(METADATA.ENV),
          'NODE_ENV': JSON.stringify(METADATA.ENV),
          'HMR': METADATA.HMR,
          'VERSION': JSON.stringify(METADATA.RELEASE_VERSION)//Setting it for the Scripts usage.
        }
      }),

Accédez à cela dans n'importe quel fichier dactylographié:

this.versionNumber = process.env.VERSION;

La façon la plus intelligente serait la suivante:

// webpack.config.js
plugins: [
    new webpack.DefinePlugin({
      VERSION: JSON.stringify(require("./package.json").version)
    })
  ]

Merci à Ross Allen

Abhijeet
la source
11

Juste une autre réponse similaire à la réponse de @ zer0chain. Cependant, avec une distinction.

Le réglage webpack -pest suffisant.

C'est la même chose que:

--define process.env.NODE_ENV="production"

Et c'est la même chose que

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  //...

  plugins:[
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
};

Vous n'aurez donc besoin que de quelque chose comme ça dans le package.jsonfichier Node:

{
  "name": "projectname",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "webpack -d",
    "production": "webpack -p"
  },
  "author": "prosti",
  "license": "ISC",
  "dependencies": {    
    "webpack": "^2.2.1",
    ...
  }
}

Quelques conseils du DefinePlugin :

Le DefinePlugin vous permet de créer des constantes globales qui peuvent être configurées au moment de la compilation. Cela peut être utile pour permettre un comportement différent entre les versions de développement et les versions de version. Par exemple, vous pouvez utiliser une constante globale pour déterminer si la journalisation a lieu; vous effectuez peut-être la journalisation dans votre build de développement mais pas dans la build de la version. C'est le genre de scénario que le DefinePlugin facilite.


Que ce soit pour que vous puissiez vérifier si vous tapez webpack --help

Config options:
  --config  Path to the config file
                         [string] [default: webpack.config.js or webpackfile.js]
  --env     Enviroment passed to the config, when it is a function

Basic options:
  --context    The root directory for resolving entry point and stats
                                       [string] [default: The current directory]
  --entry      The entry point                                          [string]
  --watch, -w  Watch the filesystem for changes                        [boolean]
  --debug      Switch loaders to debug mode                            [boolean]
  --devtool    Enable devtool for better debugging experience (Example:
               --devtool eval-cheap-module-source-map)                  [string]
  -d           shortcut for --debug --devtool eval-cheap-module-source-map
               --output-pathinfo                                       [boolean]
  -p           shortcut for --optimize-minimize --define
               process.env.NODE_ENV="production" 

                      [boolean]
  --progress   Print compilation progress in percentage                [boolean]
prosti
la source
3

Pour ajouter au tas de réponses:

Utilisez ExtendedDefinePlugin au lieu de DefinePlugin

npm install extended-define-webpack-plugin --save-dev.

ExtendedDefinePlugin est beaucoup plus simple à utiliser et est documenté :-) lien

Parce que DefinePlugin manque de bonne documentation, je veux aider, en disant qu'il fonctionne réellement comme #DEFINE en c # .

#if (DEBUG)
        Console.WriteLine("Debugging is enabled.");
#endif

Ainsi, si vous souhaitez comprendre le fonctionnement de DefinePlugin, lisez la doucmentation c # #define. lien

hannes neukermans
la source
2

Je préfère utiliser le fichier .env pour un environnement différent.

  1. Utilisez webpack.dev.config pour copier env.dev dans .env dans le dossier racine
  2. Utilisez webpack.prod.config pour copier env.prodvers .env

et en code

utilisation

require('dotenv').config(); const API = process.env.API ## which will store the value from .env file

Siva Kandaraj
la source
2

J'ai trouvé que la solution suivante était la plus facile à configurer la variable d'environnement pour Webpack 2:

Par exemple, nous avons des paramètres de webpack:

var webpack = require('webpack')

let webpackConfig = (env) => { // Passing envirmonment through
                                // function is important here
    return {
        entry: {
        // entries
        },

        output: {
        // outputs
        },

        plugins: [
        // plugins
        ],

        module: {
        // modules
        },

        resolve: {
        // resolves
        }

    }
};

module.exports = webpackConfig;

Ajouter une variable d'environnement dans Webpack:

plugins: [
    new webpack.EnvironmentPlugin({
       NODE_ENV: 'development',
       }),
]

Définissez la variable du plugin et ajoutez-la à plugins:

    new webpack.DefinePlugin({
        'NODE_ENV': JSON.stringify(env.NODE_ENV || 'development')
    }),

Maintenant, lors de l'exécution de la commande webpack, passez env.NODE_ENVcomme argument:

webpack --env.NODE_ENV=development

// OR

webpack --env.NODE_ENV development

Vous pouvez désormais accéder à des NODE_ENVvariables n'importe où dans votre code.

Ruddra
la source
1

Depuis Webpack v4, la simple configuration modede votre configuration Webpack définira le NODE_ENVpour vous (via DefinePlugin). Documents ici.

ericsoco
la source
1

Voici une méthode qui a fonctionné pour moi et m'a permis de garder mes variables d'environnement SEC en réutilisant un fichier json.

const webpack = require('webpack');
let config = require('./settings.json');
if (__PROD__) {
    config = require('./settings-prod.json');
}

const envVars = {};
Object.keys(config).forEach((key) => {
    envVars[key] = JSON.stringify(config[key]);
});

new webpack.DefinePlugin({
    'process.env': envVars
}),
cbaigorri
la source
0

Je ne suis pas un grand fan de ...

new webpack.DefinePlugin({
  'process.env': envVars
}),

... car il n'offre aucun type de sécurité. à la place, vous finissez par booster vos trucs secrets, à moins que vous n'ajoutiez un webpack à gitignore 🤷‍♀️ il y a une meilleure solution.

Fondamentalement, avec cette configuration une fois que vous avez compilé votre code, toutes les variables env du processus seront supprimées de tout le code, il n'y aura pas un seul process.env.VAR grâce au plugin babel transform-inline-environment-variables PS si vous ne voulez pas finir avec tout un tas d'indéfinis, assurez-vous d'appeler env.js avant que webpack appelle babel-loader, c'est pourquoi c'est la première chose que webpack appelle. le tableau de variables dans le fichier babel.config.js doit correspondre à l'objet sur env.js. maintenant il n'y a qu'une seule chose à faire. ajoutez un .envfichier, mettez toutes vos variables env là-bas, le fichier doit être à la racine du projet ou n'hésitez pas à l'ajouter où vous le souhaitez, assurez-vous de définir le même emplacement sur le fichier env.js et de l'ajouter également à gitignore

const dotFiles = ['.env'].filter(Boolean);

if (existsSync(dotFiles)) {
    require("dotenv-expand")(require("dotenv").config((dotFiles)));
}

Si vous voulez voir l'ensemble babel + webpack + ts l'obtenir de heaw https://github.com/EnetoJara/Node-typescript-babel-webpack.git

et la même logique s'applique pour réagir et tous les autres 💩

config
---webpack.js
---env.js
src
---source code world
.env
bunch of dotFiles

env.js

"use strict";
/***
I took the main idea from CRA, but mine is more cooler xD
*/
const {realpathSync, existsSync} = require('fs');
const {resolve, isAbsolute, delimiter} = require('path');

const NODE_ENV = process.env.NODE_ENV || "development";

const appDirectory = realpathSync(process.cwd());

if (typeof NODE_ENV !== "string") {
    throw new Error("falle and stuff");
}

const dotFiles = ['.env'].filter(Boolean);

if (existsSync(dotFiles)) {
    require("dotenv-expand")(require("dotenv").config((dotFiles)));
}

process.env.NODE_PATH = (process.env.NODE_PATH || "")
    .split(delimiter)
    .filter(folder => folder && isAbsolute(folder))
    .map(folder => resolve(appDirectory, folder))
    .join(delimiter);

const ENETO_APP = /^ENETO_APP_/i;

module.exports = (function () {
    const raw = Object.keys ( process.env )
        .filter ( key => ENETO_APP.test ( key ) )
        .reduce ( ( env, key ) => {
                env[ key ] = process.env[ key ];
                return env;
            },
            {
                BABEL_ENV: process.env.ENETO_APP_BABEL_ENV,
                ENETO_APP_DB_NAME: process.env.ENETO_APP_DB_NAME,
                ENETO_APP_DB_PASSWORD: process.env.ENETO_APP_DB_PASSWORD,
                ENETO_APP_DB_USER: process.env.ENETO_APP_DB_USER,
                GENERATE_SOURCEMAP: process.env.ENETO_APP_GENERATE_SOURCEMAP,
                NODE_ENV: process.env.ENETO_APP_NODE_ENV,
                PORT: process.env.ENETO_APP_PORT,
                PUBLIC_URL: "/"
            } );

    const stringyField = {
        "process.env": Object.keys(raw).reduce((env, key)=> {
            env[key]=JSON.stringify(raw[key]);
            return env;
        },{}),

    };

    return {
        raw, stringyField
    }
})();

fichier webpack sans plugins troll

"use strict";

require("core-js");
require("./env.js");

const path = require("path");
const nodeExternals = require("webpack-node-externals");

module.exports = env => {
    return {
        devtool: "source-map",
        entry: path.join(__dirname, '../src/dev.ts'),
        externals: [nodeExternals()],
        module: {
            rules: [
                {
                    exclude: /node_modules/,
                    test: /\.ts$/,
                    use: [
                        {
                            loader: "babel-loader",
                        },
                        {
                            loader: "ts-loader"
                        }
                    ],
                },
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: "file-loader",
                        },
                    ],
                },
            ],
        },
        node: {
            __dirname: false,
            __filename: false,
        },
        optimization: {
            splitChunks: {
                automaticNameDelimiter: "_",
                cacheGroups: {
                    vendor: {
                        chunks: "initial",
                        minChunks: 2,
                        name: "vendor",
                        test: /[\\/]node_modules[\\/]/,
                    },
                },
            },
        },
        output: {
            chunkFilename: "main.chunk.js",
            filename: "name-bundle.js",
            libraryTarget: "commonjs2",
        },
        plugins: [],
        resolve: {
            extensions: ['.ts', '.js']
        }   ,
        target: "node"
    };
};

babel.config.js

module.exports = api => {

    api.cache(() => process.env.NODE_ENV);

    return {

        plugins: [
            ["@babel/plugin-proposal-decorators", { legacy: true }],
            ["@babel/plugin-transform-classes", {loose: true}],
            ["@babel/plugin-external-helpers"],
            ["@babel/plugin-transform-runtime"],
            ["@babel/plugin-transform-modules-commonjs"],
            ["transform-member-expression-literals"],
            ["transform-property-literals"],
            ["@babel/plugin-transform-reserved-words"],
            ["@babel/plugin-transform-property-mutators"],
            ["@babel/plugin-transform-arrow-functions"],
            ["@babel/plugin-transform-block-scoped-functions"],
            [
                "@babel/plugin-transform-async-to-generator",
                {
                    method: "coroutine",
                    module: "bluebird",
                },
            ],
            ["@babel/plugin-proposal-async-generator-functions"],
            ["@babel/plugin-transform-block-scoping"],
            ["@babel/plugin-transform-computed-properties"],
            ["@babel/plugin-transform-destructuring"],
            ["@babel/plugin-transform-duplicate-keys"],
            ["@babel/plugin-transform-for-of"],
            ["@babel/plugin-transform-function-name"],
            ["@babel/plugin-transform-literals"],
            ["@babel/plugin-transform-object-super"],
            ["@babel/plugin-transform-shorthand-properties"],
            ["@babel/plugin-transform-spread"],
            ["@babel/plugin-transform-template-literals"],
            ["@babel/plugin-transform-exponentiation-operator"],
            ["@babel/plugin-proposal-object-rest-spread"],
            ["@babel/plugin-proposal-do-expressions"],
            ["@babel/plugin-proposal-export-default-from"],
            ["@babel/plugin-proposal-export-namespace-from"],
            ["@babel/plugin-proposal-logical-assignment-operators"],
            ["@babel/plugin-proposal-throw-expressions"],
            [
                "transform-inline-environment-variables",
                {
                    include: [
                        "ENETO_APP_PORT",
                        "ENETO_APP_NODE_ENV",
                        "ENETO_APP_BABEL_ENV",
                        "ENETO_APP_DB_NAME",
                        "ENETO_APP_DB_USER",
                        "ENETO_APP_DB_PASSWORD",
                    ],
                },
            ],
        ],
        presets: [["@babel/preset-env",{
            targets: {
                node: "current",
                esmodules: true
            },
            useBuiltIns: 'entry',
            corejs: 2,
            modules: "cjs"
        }],"@babel/preset-typescript"],
    };
};
Ernesto
la source
"Vous finissez par booster vos trucs secrets, sauf si vous ajoutez un webpack à gitignore." @Ernesto pouvez-vous développer cela?
Katie Byers
Fondamentalement, votre bundle se retrouve sans processus.env.BLAHBLAH et met la valeur réelle. Par exemple, au lieu d'avoir process.env.NODE_ENV u se retrouver avec "production", je veux dire que ce n'est pas le meilleur exemple mais imaginez une clé secrète. Votre bundle aura la valeur réelle et qui sait ce que cette chaîne câblée signifie 🤷‍♀️
Ernesto
Hmmm - oui, ces valeurs seront interpolées dans la version construite , mais vous ne
Katie Byers
-4

Je ne sais pas pourquoi mais personne ne mentionne vraiment la solution la plus simple. Cela fonctionne pour moi pour nodejs et grunt. Comme pour beaucoup de gens, le webpack peut être déroutant, vous pouvez simplement utiliser la ligne ci-dessous:

process.env.NODE_ENV = 'production';

Avec la solution ci-dessus, vous n'avez pas vraiment besoin d'utiliser envify ou webpack. Parfois, la solution simple codée en dur peut fonctionner pour certaines personnes.

John Skoumbourdis
la source