skip to Main Content

Lets say you want to transpile Javascript with injected PHP in it, to be preprocessed by the PHP module of your server. This way, PHP can process the JS with context from the backend, before being delivered as a response.

function component() {
  const element = document.createElement('div');

  <?php if(isset($_GET["id"])): ?>
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  <?php else: ?>
  element.innerHTML = _.join(['Hello', 'PHP injected', ' webpack'], ' ');
  <?php endif; ?>
  element.classList.add('hello');
  return element;
}

document.body.appendChild(component());

When trying to transpile the JS with the babel-loader in Webpack, the PHP tokens are creating errors. How would you transpile such a JS File?

I created a custom babel-php-injected-loader, which uses babel.

const babel = require('@babel/core');

module.exports = function (source) {
    const callback = this.async();  // Mark the loader as asynchronous

    // Find all PHP blocks in the source
    const phpBlocks = [];
    const phpRegex = /<?php[sS]*??>/g;
    let i = 0;

    // Replace each PHP block with a unique placeholder
    const processedSource = source.replace(phpRegex, (m) => {
        const placeholder = `__PHP_PLACEHOLDER_${i++}__`;
        phpBlocks.push({ placeholder, code: m });
        return placeholder;  // Directly replace PHP blocks with placeholders
    });

    // Process the modified JavaScript (without PHP blocks) using Babel
    babel.transform(processedSource, {
        presets: ['@babel/preset-env'], // Same options as you'd pass to babel-loader
    }, (err, result) => {
        if (err) return callback(err);  // If Babel fails, return an error

        let finalSource = result.code;

        // Re-inject the raw PHP code into the final source, replacing the placeholders
        phpBlocks.forEach(({ placeholder, code }) => {
            finalSource = finalSource.replace(placeholder, code);  // Insert raw PHP directly
        });

        // Return the final source with raw PHP reinserted
        callback(null, finalSource);
    });
};

It looks like webpack’s parser from the bundler does not like PHP injections when transpiling.

How would you transpile PHP injected JS with Webpack?

Kind Regards

2

Answers


  1. You need to run that on Server and don’t forget that it should be .php extension and it is better to include the script tag or place these in echo "here"

    Login or Signup to reply.
  2. Since a PHP script that outputs JS is not valid JS code, you can’t directly parse it with a JS parser.
    You may find ways to work around it, but even if you manage to minify the file correctly, the real problem is the source map.

    A source map, as the name suggests, maps locations in the generated file to the corresponding locations in the original file.
    Let’s consider your sample code:

    function component() {
      const element = document.createElement('div');
    
      <?php if(isset($_GET["id"])): ?>
      element.innerHTML = _.join(['Hello', 'webpack'], ' ');
      <?php else: ?>
      element.innerHTML = _.join(['Hello', 'PHP injected', ' webpack'], ' ');
      <?php endif; ?>
      element.classList.add('hello');
      return element;
    }
    
    document.body.appendChild(component());
    

    In case the PHP condition evaluates to true, the output is:

    function component() {
      const element = document.createElement('div');
    
      element.innerHTML = _.join(['Hello', 'webpack'], ' ');
      element.classList.add('hello');
      return element;
    }
    
    document.body.appendChild(component());
    

    If the condition evaluates to false:

    function component() {
      const element = document.createElement('div');
    
      element.innerHTML = _.join(['Hello', 'PHP injected', ' webpack'], ' ');
      element.classList.add('hello');
      return element;
    }
    
    document.body.appendChild(component());
    

    In the first case, line 4 of the JS file maps to line 5 of the PHP file.

    In the second case, line 4 of the JS file maps to line 7 of the PHP file.

    So the mapping depends on the result of the PHP execution; it means it’s impossible to create a source map.

    If you really want to minify the JS code, the correct approach is to separate the static and dynamic parts.
    Your code can be rewritten to:

    function component(arr) {
      const element = document.createElement('div');
    
      element.innerHTML = _.join(arr, ' ');
      element.classList.add('hello');
      return element;
    }
    

    and:

    <?php if(isset($_GET["id"])): ?>
    document.body.appendChild(component(['Hello', 'webpack']));
    <?php else: ?>
    document.body.appendChild(component(['Hello', 'PHP injected', ' webpack']));
    <?php endif; ?>
    

    The static part can be put in a static JS file and processed by Webpack like any other.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search