postcss fails to read NODE_ENV correctly

🐛 bug report

When running parcel watch / serve and even when I add NODE_ENV=development variable before running command it still reads process.env.NODE_ENV is production.

I have console log in my .postcss and it prints production no matter what I do.

🎛 Configuration (.babelrc, package.json, cli command)

    "build": "parcel build '.tmp/parcel/**/index.html' --cache-dir .tmp/.cache",
    "build:dev": "NODE_ENV=development parcel build '.tmp/parcel/**/*.html' --cache-dir .tmp/.cache",
    "watch": "parcel watch '.tmp/parcel/**/*.html' --cache-dir .tmp/.cache",
    "watch:dev": "NODE_ENV=development parcel watch '.tmp/parcel/**/*.html' --cache-dir .tmp/.cache",
    "serve": "parcel serve --port 3000 '.tmp/parcel/**/*.html' --cache-dir .tmp/.cache",
    "serve:dev": "NODE_ENV=development parcel serve --port 3000 '.tmp/parcel/**/*.html' --cache-dir .tmp/.cache",
{
    "presets": [
        "@babel/preset-env", {
            "include": ["@babel/plugin-transform-strict-mode" ]
      }]
}
console.log('.postcssrc', process.env.NODE_ENV)

const purgecss = require('@fullhuman/postcss-purgecss')({
  content: ['.tmp/parcel/**/*.html'],
  // Include any special characters you're using in this regular expression
  defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:\.\/]+/g) || [],
})

module.exports = {
  plugins: [
    require('postcss-easy-import'),
    require('tailwindcss')('./pages/assets/css/tailwind.config.js'),
    require('autoprefixer'),
    ...(process.env.NODE_ENV === 'production' ? [purgecss] : []),
  ],
}

check npm scripts
https://github.com/danielstaleiny/minimal-example-parcel2

🤔 Expected Behavior

parcel should have NODE_ENV ‘development’ when running watch / serve mode and when setting up in cli NODE_ENV environment.

😯 Current Behavior

parcel build -> nothing is printed from .postcss??
NODE_ENV=development -> nothing is printed .postcss??
parcel watch -> .postcss production
NODE_ENV=development parcel watch -> .postcss production
parcel serve -> .postcss production
NODE_ENV=development parcel serve -> .postcss production

💁 Possible Solution

Propagate NODE_ENV variable correctly to postcss

🔦 Context

runs purge when it is not needed.

💻 Code Sample

https://github.com/danielstaleiny/minimal-example-parcel2

🌍 Your Environment

Software Version(s)
Parcel “parcel”: “^2.0.0-nightly.240”,
Node v10.17.0
npm/Yarn 6.11.3
Operating System NixOS unstable (20.03)

4 thoughts on “postcss fails to read NODE_ENV correctly

  1. The JS config is “executed” here:

    config: clone(require(configFile)),

    Problems I’ve discovered just now:

    1. (The file is required so when it is changed in watch mode, require() returns the same result. The cache needs to be cleared with something like delete require.cache[configFile])
    2. (Before calling this require(), the value in options.env.NODE_ENV should be loaded into process.env (and reverted?))
    3. This line causes the config to be reevaluated even if it’s contents didn’t change (e.g. env vars):
      config.shouldInvalidateOnStartup();

      The config is indeed reevaluated (and it is different), but the asset isn’t transformed again (so somehow Parcel has decided that the config didn’t change at all?):

      async transform({asset, config, options, resolve}) {
    4. requires in the JS config to missing modules should throw an error, but these are swallowed here:
      if (err.code === ‘MODULE_NOT_FOUND’ || err.code === ‘ENOENT’) {

    cc @DeMoorJasper @padmaia

  2. Any workarounds for this issue? I don’t fancy turning purge on/off and deleting cache manually whenever I need to publish. 😭

  3. I had issues with tailwind as well for the longest time. I decided to do a diff between process.env in build & dev mode. Turns out there’s a variable called npm_lifecycle_event that seems to contain the name of the npm script that’s running. You can essentially use this as your hook to know whether you are in dev mode or not.

    For instance say you have the following scripts in your package.json :

    {
        ...,
        "scripts": {
            "dev": "parcel src/index.html",
            "build": "parce build src/index.html"
        },
        ...
    }

    and the following in your tailwind.config.js

    // This will be equal to the current running script "build" | "dev"
    const isProd = process.env["npm_lifecycle_event"] === "build";
    
    module.exports = {
      purge: {
        enabled: isProd,
        mode: "layers",
        layers: ["base", "components", "utilities"],
        content: ["src/**/*.html", "src/**/*.tsx"],
      },
    };

    This will build with purge mode when you execute the build command and leave as is with dev script.

    More information can be found here: https://medium.com/@brianhan/use-this-npm-variable-as-a-flag-for-your-build-scripts-31069f5e2e57

    Hope that helps!