web-dev-qa-db-fra.com

Passage de variables dépendantes de l'environnement dans le webpack

J'essaie de convertir une application angular de gulp à Webpack. J'utilise gulp-preprocess pour remplacer certaines variables de la page HTML (nom de la base de données, par exemple) en fonction de NODE_ENV. Quel est le meilleur moyen d'obtenir un résultat similaire avec webpack?

279
kpg

Il y a deux façons de base pour y parvenir.

DefinePlugin

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

Notez que cela ne fera que remplacer les allumettes "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.

EnvironnementPlugin

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPlugin utilise DefinePlugin en interne et mappe les valeurs d'environnement sur lesquelles coder. Syntaxe Terser.

Alias

Sinon, vous pouvez utiliser la configuration via n module aliasé . Du côté du consommateur, 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_ENV est development. Il serait alors mappé dans ./config/development.js. Le module auquel il correspond peut exporter la configuration de la manière suivante:

module.exports = {
    testing: 'something',
    ...
};
402

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

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

Donc, je dois juste exécuter npm run build-production.

106
zer0chain

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 2 config webpack actuellement:

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 du 3 nov 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 la règle no-undef. http://eslint.org/docs/rules/no-undef comme ceci:

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

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

Si vous n’aimez pas trop configurer, consultez Create-React-App: Create-React-App - Ajout de variables d’environnement personnalisées . Sous le capot, l'ARC utilise quand même Webpack.

67
thevangelist

Vous pouvez passer n’importe quel argument de ligne de commande sans plugins supplémentaires en utilisant --env depuis le webpack 2:

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

En utilisant la variable dans webpack.config.js:

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

Source

22
andruso

Vous pouvez directement utiliser le EnvironmentPlugin disponible dans webpack pour avoir accès à n’importe quelle variable d’environnement pendant la transpilation.

Il vous suffit de déclarer le plug-in dans votre fichier webpack.config.js:

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.

22
Kuhess

Depuis mon édition sur le post ci-dessus par thevangelist n'a pas été approuvé , affichant des informations supplémentaires.

Si vous voulez choisir une valeur dans package.json comme un numéro de version défini et y accéder par DefinePlugin dans Javascript.

{"version": "0.0.1"}

Ensuite, Importez package.json à l'intérieur webpack.config respectif, accédez au attribuez en utilisant la variable import, puis utilisez-le 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 à ceci dans n’importe quel fichier TypeScript:

this.versionNumber = process.env.VERSION;

Le moyen le plus intelligent serait comme ceci:

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

Merci à Ross Allen

13
Abhijeet

Pour ajouter personnellement aux 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 cela, il n’ya pas de variable funky env ou de problèmes multi-plateformes (avec env vars). Tout ce que vous faites est d'exécuter le webpack ou webpack -p normal pour le développement ou la production, respectivement.

Référence: numéro de Github

13
Goblinlord

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

Régler webpack -p est suffisant.

C'est la même chose que:

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

Et c'est pareil que

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

module.exports = {
  //...

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

Vous n’avez donc besoin que de quelque chose comme cela dans le fichier package.json 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 astuces de DefinePlugin :

DefinePlugin vous permet de créer des constantes globales pouvant être configurées lors de la compilation. Cela peut être utile pour autoriser 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 ou non. peut-être que vous vous connectez dans votre version de développement, mais pas dans la version de publication. C'est le genre de scénario que DefinePlugin facilite.


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]
11
prosti

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é :-) link

Parce que DefinePlugin manque une bonne documentation, je veux aider en disant que cela fonctionne réellement comme # DEFINE en c # .

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

Ainsi, si vous voulez comprendre le fonctionnement de DefinePlugin, lisez la documentation relative à c # #define. lien

3
hannes neukermans

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

Par exemple, nous avons un paramétrage 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éfinir une variable de plug-in et l'ajouter à 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_ENV en tant qu'argument:

webpack --env.NODE_ENV=development

// OR

webpack --env.NODE_ENV development

Vous pouvez maintenant accéder à la variable NODE_ENV n'importe où dans votre code.

2
ruddra

Depuis Webpack v4, il vous suffit de définir mode dans votre configuration Webpack pour configurer le NODE_ENV (via DefinePlugin). Docs here.

1
ericsoco

Je préfère utiliser le fichier .env pour différents environnements.

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

et en code

utilisation

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

1
Siva Kandaraj

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

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
}),
0
cbaigorri

Je ne suis pas un grand fan de ...

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

... car il ne fournit aucun type de sécurité. Au lieu de cela, vous finissez par renforcer vos données secrètes, à moins d’ajouter un pack Web à gitignore. Il existe une meilleure solution.

En gros, avec cette configuration, une fois que vous avez compilé votre code, toutes les variables d’environnement de processus seront supprimées de tout le code. Il n’y aura pas un seul processus.env.VAR grâce au plugin babel transform-inline-environment-variables PS si vous le faites. Si vous ne voulez pas vous retrouver avec un tas d’indéfinis, assurez-vous d’appeler env.js avant que Webpack appelle babel-loader, c’est pourquoi il s’agit en premier lieu des appels de webpack. le tableau de vars dans le fichier babel.config.js doit correspondre à l'objet sur env.js. maintenant, il n'y a plus qu'une chose à faire. ajoutez un fichier .env mettez toutes vos variables env ici, 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 aussi l'ajouter à gitignore

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

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

Si vous voulez voir la totalité de babel + webpack +, obtenez-le de la façon suivante 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 plugin 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"],
    };
};
0
Ernesto