Skip to content

A webpack loader which prepares the builds to allow env injection later on without building everything again.

License

Notifications You must be signed in to change notification settings

simbathesailor/envvarprep-loader

Repository files navigation

envvarprep-loader

A webpack loader which prepares the frontend builds to allow env injection later on without building everything again.

Problem statement:

So in typical organisation, we generally have many environment for various
stakeholders e.g dev, staging, uat, preproduction and production. We keep secret keys in some kind of param store which varies across environment. When we need to deploy same code on different environment we need to rebuild everything, even if the code remains same, only the secrets might have changed. We need not repeat whole npm install, production build and other processes which are common. we can reuse the built code and inject secrets in later steps(which will only care for changed param values not other things). This usual build process may be okish for some projects, where build process is itself very minute. But in most of the projects the build takes 5-10 minutes. The process I mentioned above can bring the time within 1 minutes.
Think about time and money that can be saved in a organization having multiple projects.



How this will done ?

I will be making use babel ASTs , babel parsers and webpack loaders to do so. These tools can be used to give us an extra step where we can inject secrets.

Usage

yarn add envvarprep-loader --dev
module: {
  rules: [
    {
      test: /\.(js|mjs|jsx|ts|tsx)$/,
      enforce: 'pre',
      include: path.join(__dirname, './src'),
      use: [
        {
          options: {
            exclude: ['NODE_ENV', 'REACT_APP_APPNAME', 'PUBLIC_URL'],
            plugins: ['jsx'],
            enable: true,
            debug: true, // default is false
          },
          loader: "envvarprep-loader",
        }
      ],
      exclude: /(node_modules|dist)/,
    },
  ],
}

Types for loader options

interface IOptions {
  exclude?: string[];
  plugins?: ParserPlugin[];
  enable?: boolean;
  debug?: boolean;
  sourceType?: ParserOptions['sourceType'];
}

Make sure the envvarprep-loader gets the opportunity to first transform the code before any other loader. So place it at the last index of use array. The last loader is executed first Read the info at webpack

Process to inject env to the built files

Only replacing process env with the placeholder text will not work. We need a mechanism to inject the param values also.

We provide a default injection scripts , which will work in most of the cases. For very specific and customized build processes, one can checkout the injectEnv.tsx in the codebase and implement their owns.

import { injectEnv } from 'envvarprep-loader';

Usage

const env = {
  REACT_APP_PARAM_TWO: 'I am param two',
  REACT_APP_PARAM_THREE: 'I am param three',
};
const payload = {
  globOptions: {
    cwd: `${__dirname}/injectEnvInputFolder`,
  },
  pattern: '*.js?(.map)',
  envVar: env,
  debug: true,
  destination: `${__dirname}/injectEnvInputFolder/output`,
  updateInline: false,
};
injectEnv(payload);

envvarprep-loader is written using typescript, so type suggestion can be used to make use of the loader and helper function

injectEnv Options

interface IEnvObj {
  [key: string]: string;
}

interface IInjectEnv {
  globOptions: glob.IOptions;
  envVar?: IEnvObj;
  debug?: boolean;
  destination?: string;
  pattern?: string;
  updateInline?: boolean;
}

Default values

const defaultOptions = {
  globOptions: {
    cwd: __dirname,
  },
  pattern: '*',
  envVar: {},
  debug: process.env.NODE_ENV !== 'production',
  destination: __dirname,
  updateInline: true,
};

globOptions: All the options which are accepted by Glob

pattern:string [optional] glob pattern string see the pattern in section

envVar(object): Env objects that is fetched from any other param store.

debug:boolean[optional]: (default is dependent in process.env.NODE_ENV !== 'production').

destination:string [optional]: A destination where you want to put the updated files. By default if updateInline is not passed , the original files will be updated. This is what will be needed most of the times

updateInline: if true (default), the original files will be updated.

Problem statement:

So in typical organisation, we generally have many environment for various
stateholders e.g dev, staging, uat, preproduction and production. We keep secret keys in some kind of param store which varies across environment. When we need to deploy same code on different environment we need to rebuild everything, even if the code remains same, only the secrets might have changed. We need not repeat whole npm install, production build and other processes which are common. we can reuse the built code and inject secrets in later steps(which will only care for changed param values not other things). This usual build process may be okish for some projects, where build process is itself very minute. But in most of the projects the build takes 5-10 minutes. The process I mentioned above can bring the time within 1 minutes.
Think about time and money that can be saved in a organization having multiple projects.



How this will done ?

I will be making use babel ASTs , babel parsers and webpack loaders to do so. These tools can be used to give us an extra step where we can inject secrets.

How it works

Image explains all what is happening with the loader and script.

Know Issues:

  • Syntax like below will not work as expected. So make sure the code is being converted to process.env.NAME or process.env["NAME"] from you babel presets.
const { env } = process;
const { REACT_APP_PARAM_TWO } = env;
console.log('REACT_APP_PARAM_TWO HI ', REACT_APP_PARAM_TWO);

Link to the sample repositories making use of this loader:

https://github.com/simbathesailor/separate-env-injection-playground

Improvements:

  • Only for non .js file, we do env injection with string replacement. For .js files we make use of babel ast transformation (which cover almost all the usecases). Need to look for solution for other types of file .js.map files. Any prs and discussions are welcome !!.
  • Make use of some babel plugin to remove the dead code in the second phase. As env injection might evaluate certain code blocks to be unnecessary. Doing that should bring in the same bundle size which happens otherwise now.

Built with TSDX

About

A webpack loader which prepares the builds to allow env injection later on without building everything again.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published