All Projects → afterwind-io → preprocessor-loader

afterwind-io / preprocessor-loader

Licence: MIT license
Bring the awesome "Conditional Compilation" to the Webpack, and more.

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects

Projects that are alternatives of or similar to preprocessor-loader

Fortran-Tools
Fortran compilers, preprocessors, static analyzers, transpilers, IDEs, build systems, etc.
Stars: ✭ 31 (-3.12%)
Mutual labels:  preprocessor
react-spa-template
This is a sample template for single page applications built using React + Router to work with webpack dev server
Stars: ✭ 19 (-40.62%)
Mutual labels:  webpack-loader
markup-inline-loader
a webpack loader to embed svg/MathML to html
Stars: ✭ 24 (-25%)
Mutual labels:  webpack-loader
angular-hmr-loader
🔥 Angular HMR Webpack Loader by @AngularClass
Stars: ✭ 32 (+0%)
Mutual labels:  webpack-loader
gpp
General PreProcessor
Stars: ✭ 25 (-21.87%)
Mutual labels:  preprocessor
gulp-styl
Preprocess CSS with Styl
Stars: ✭ 21 (-34.37%)
Mutual labels:  preprocessor
frontend-starter-kit-with-gulp
Frontend Starter Kit with Gulp for either Themeforest Projects or customizable projects.
Stars: ✭ 34 (+6.25%)
Mutual labels:  preprocessor
webpack-modernizr-loader
Get your modernizr build bundled with webpack, use modernizr with webpack easily
Stars: ✭ 35 (+9.38%)
Mutual labels:  webpack-loader
webpack-webmanifest-loader
Minimalistic webpack loader to generate webmanifest file (and process icons URLs).
Stars: ✭ 16 (-50%)
Mutual labels:  webpack-loader
lit
a little preprocessor for literate programming
Stars: ✭ 108 (+237.5%)
Mutual labels:  preprocessor
lodash-loader
Cherry-picks Lodash functions and require them explicitly to reduce the webpack bundle size.
Stars: ✭ 13 (-59.37%)
Mutual labels:  webpack-loader
gulp-rework
Preprocess CSS with Rework
Stars: ✭ 30 (-6.25%)
Mutual labels:  preprocessor
angular-translate-loader
"angular-translate" loader for webpack
Stars: ✭ 15 (-53.12%)
Mutual labels:  webpack-loader
flextool
C++ compile-time programming (serialization, reflection, code modification, enum to string, better enum, enum to json, extend or parse language, etc.)
Stars: ✭ 32 (+0%)
Mutual labels:  preprocessor
color-loader
🎨 A webpack loader that extracts the color palette of an image
Stars: ✭ 14 (-56.25%)
Mutual labels:  webpack-loader
dcc
Direct/Interactive C Compiler
Stars: ✭ 18 (-43.75%)
Mutual labels:  preprocessor
virtual-dependency-loader
webpack loader that takes a single file, and declare pieces of that file as "dependencies" as if it existed.
Stars: ✭ 33 (+3.13%)
Mutual labels:  webpack-loader
vrm pp
Small C++ preprocessor library
Stars: ✭ 12 (-62.5%)
Mutual labels:  preprocessor
typescript-babel-jest
DEPRECATED: ⚡ Typescript-babel jest preprocessor
Stars: ✭ 33 (+3.13%)
Mutual labels:  preprocessor
svelte-loader-hot
Webpack loader for svelte components with HMR support
Stars: ✭ 22 (-31.25%)
Mutual labels:  webpack-loader

webpack-preprocessor-loader

Version Node Downloads License Build Status

Bring the awesome "Conditional Compilation" to the Webpack, and more.

Why

webpack-preprocessor-loader leverages the concept of Conditional Compilation to output specific code based on conditional directives. By which you can:

  • Hide specific contents from the final result;
  • Import different packages by specified environment (eg: development/production);
  • Remove debugs in production;
  • Split codes in production, while bundle them in development;
  • Many other scenarios...

For a quick review, given:

ENV === "product", debug === false, secret === false

In code:

// #!if ENV === 'develop'
import someModule from "module-name";
// #!else
const anotherModule = import("another-module-name");
// #!endif

// #!debug
console.log(someModule);

/*
 * My precious code!
 * #!secret
 */
const the_answer_to_everything = "42";

Yields:

const anotherModule = import("another-module-name");

Pros:

  • It is "Conditional Compilation";
  • Say goodbye to those "process.env.NODE_ENV"s messing around the code;
  • Deals directly with raw text, so it just works on any text-based file;
  • Create custom directives if needed;

Cons:

  • Maybe a little verbose in some cases;

    If so, consider using webpack.DefinePlugin backwards.

Compatibility

  • webpack: >=4.0.0
  • node: 6.11.5 minimum (aligned with webpack 4)

Installation

yarn add webpack-preprocessor-loader -D

or

npm install webpack-preprocessor-loader -D

Configuration

Since it deals directly with the raw text, webpack-preprocessor-loader should be the last loader in use definition. A full example config is as follows:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          "babel-loader",
          // ... other loaders
          {
            loader: "webpack-preprocessor-loader",
            options: {
              debug: process.env.NODE_ENV !== "product",
              directives: {
                secret: false,
              },
              params: {
                ENV: process.env.NODE_ENV,
              },
              verbose: false,
            },
          },
        ],
      },
    ],
  },
};

More details see Options.

Usage

Note that any text-based file can be compiled, not only codes, for example:

  • HTML/Pug/...
  • Sass/Less/...
  • Json5/Xml/Yaml/...

Basics

Conditional Compilation relies on special comments, aka directive, which start with #!, followed by the directive name, e.g.,

// #!directive

Directives can be used to tag a certain line, or wrap a whole block of code.

// #!debug
const foo = 1;

// #!if env === "development"
const bar = 2;
const baz = 3;
// #!endif

Unlike normal comments, the directive must not appear in-line. For example:

// Won't work
const bar = 2; // #!debug // <-- Directive will be ignored.
/* #!debug */ const baz = 3; // <-- The code will always be omitted with the directive.

Multiple variants of comment are supported. Details see Comment Syntax.

Single-line control

One common case is that we want to omit one specified line of code under certain condition(s).

For example, we want to log some messages to the console, but only during development. To drop the code based on environment, we can define a custom directive.

First, declare a property in options.directives, e.g. dev.

In config:

{
  loader: 'webpack-preprocessor-loader',
  options: {
    directives: {
      dev: process.env.NODE_ENV === "development",
    },
  },
},

In code:

// #!dev
console.log("DEBUG ONLY");

During the compilation, if the value of dev is false, the exact line of code under the directive will be omitted, and vice versa.

For the development/production scenario, the loader provides a handy built-in directive called #!debug. Details see Options - debug and Built-in Directives.

Multi-line control

The other common case is that we want to omit multiple lines of code at once depends on certain condition(s). Since custom directives can only tag one line of code, we need another group of built-in directives: #!if/#!endif.

Like real-world ifs, it needs a condition. We can also provide variables to form an expression.

Declare a property in options.params, e.g. env.

In config:

{
  loader: 'webpack-preprocessor-loader',
  options: {
    params: {
      env: process.env.NODE_ENV,
    },
  },
},

In code:

// #!if env === "development"
console.log("DEBUG ONLY");
doSomethingForTheDev();
// #!endif

Once compiled, the codes between #!if and #!endif will be omitted, if the condition expression provided evaluates to falsy value in javascript.

More details see Built-in Directives.

Branching

Sometimes, we may need a little bit more complex control flow. For example, the project runs different code between multiple stages. Like conditional branching in standard language, the loader provides #!else/#!elseif directives to stimulate the behavior of if statement.

Suppose there is a parameter called "env" defined in options.params, statement branching can easily be expressed like:

// #!if env === "development"
doSomethingA();
doSomethingA2();
// #!elseif env === "canary"
doSomethingB();
doSomethingB2();
// #!else
doSomethingC();
doSomethingC2();
// #!endif

doSomethingCommon();

In addition, nested #!if is also supported. More details see Built-in Directives.

Comment Syntax

The loader supports the following comment variants:

  • Line comment

    // #!if foo === 1
  • Block comment:

    /* #!if foo === 1 */
    
    /*
     * #!if stage === 'product'
     */
  • HTML comment:

    <!-- #!if foo === 1 -->
  • JSX comment:

    <div>{/* #!if foo === 1 */}</div>

And for better maintenance, embedded comments in directive are also supported. For example:

/*
 * Look mom I have a comment!
 * #!if stage === 'product'
 */

// I have a comment too. #!if stage === 'product'

Options

debug

type: boolean

default: false

Provides constant value for built-in #!debug directive. See Directives - #!debug.

directives

type: {[key: string]: boolean}

default: {}

Define custom directives. For example, to create a directive called "secret":

In config:

{
  loader: 'webpack-preprocessor-loader',
  options: {
    directives: {
      secret: false,
    },
  },
},

In code:

// #!secret
console.log("wow"); // This line will be omitted

Note that the custom directive only affects its next line, which means:

// #!secret
console.log("Removed"); // This line will be omitted
console.log("Preserved"); // This line will not be affected by "#!secret", hence it will be preserved anyway

If an undefined directive is referenced, say "foo", the next line marked by #!foo will always be omitted, because the value of foo is undefined, identical as false.

params

type: {[key: string]: any}

default: {}

Provide constant values for built-in #!if / #!elseif / #!else / #!endif directives. See Directives - #!if / #!else / #!elseif / #!endif

verbose

type: boolean

default: false

Preserve all directive comments and omitted lines as comments. Basically for debugging purpose.

Given:

// options.params.ENV === 'product'

// #!if ENV === 'develop'
console.log("many doge");
// #!else
console.log("much wow");
// #!endif

If set to true, yields:

// #!if ENV === 'develop'
// console.log('many doge');
// #!else
console.log("much wow");
// #!endif

Built-in Directives

#!if / #!else / #!elseif / #!endif

Basic Usage

As name suggests, these directives work similarly like real if logic.

In config:

{
  loader: 'webpack-preprocessor-loader',
  options: {
    params: {
      foo: 1,
      bar: 1,
    },
  },
},

Demo in Javascript:

// #!if foo === 1
const foo = 1;

// Even nested...
// #!if bar === 1
const bar = 1;
// Or even nested custom directive!
// Suppose "options.directives.test === true"
// #!test
const baz = 0;
// #!else
const bar = 2;
// #!test
const baz = 1; // <-- omitted, because bar !== 1, even though test === true
// #!endif

// #!else
const foo = 2;

// #!endif

Yields

const foo = 1;
const bar = 1;
const baz = 0;

Any valid #!if / #!else / #!elseif / #!endif combination is accepted, only remember always close branching statements by #!endif.

Complex Condition

The condition can also be some more complex expressions. For example:

// #!if foo === 1 && bar === 2

// #!if foo + bar === 3

// Seriously?
// #!if (function(a){ return a === 1; })(foo)

Behind the scenes, the expression is wrapped in a return clause, and dynamically evaluated during compilation, thus its context is irrelevant to the code. So all variables in the expression should be pre-defined in the params and treated as constants. Finally ensure the expression returns a boolean value.

#!debug

A semantic and handy directive to mark specific line only to be preserved when needed. For example:

// options.debug === false

// #!debug
console.log("test"); // This line will be omitted

Note that the #!debug directive only affects its next line, which means:

// options.debug === false

// #!debug
console.log("Removed"); // This line will be omitted
console.log("Preserved"); // This line will not be affected by "#!debug", hence it will be preserved anyway

Caveats

Javascript

The following code yields errors during linting:

// #!if env === 'develop'
const foo = 1;
// #!else
const foo = -1;
// #!endif

// "[ts] Cannot redeclare block-scoped variable 'foo'."
// "[eslint] Parsing error: Identifier 'foo' has already been declared"

Typescript

To suppress the error, a tricky way is simply adding // @ts-ignore before all declarations:

// @ts-ignore #!if env === 'develop'
const foo = 1;
// @ts-ignore #!else
const foo = -1;
// #!endif

// Errors gone.

ESlint

It is hard to get around this problem while linting through editor plugin, because ESLint parses the file into AST first, which caused a parsing error. So the only solution is to temporarily comment one or more declarations out during code editing.

Otherwise, if eslint-loader is used, simply put it before webpack-preprocessor-loader:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          "babel-loader",
          "eslint-loader",
          {
            loader: "webpack-preprocessor-loader",
            options: {
              // ...
            },
          },
        ],
      },
    ],
  },
};

License

MIT License

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].