Settings

All packages can be customized per authenticated username using the settings.users block. The base settings are then searched until something is found.

{
  "document": {
    "chrome": {
      "handler": "@pi-r/chrome",
      "eval": {
        "function": true, // Enable inline functions
        "absolute": false, // Enable absolute paths to local files
        "template": false, // Enable external template functions
        "userconfig": false // Enable functions inside local files from user queries
      },
      "settings": {
        "directory": {
          "package": "../packages" // Override built-in plugins (base directory/ + users/username/? + posthtml.js)
        },
        "users": {
          "username": {
            "html": {
              "posthtml": {/* Same */}
            },
            "css": {
              "postcss": {/* Same */}
            }
          }
        }
      }
    }
  }
}

Warning

eval is a global setting for all users. Any unsafe execution is disabled by default.

Using built-in transformer

External plugins per package have to be pre-installed from NPM and may not be available for auto-install.

{
  "document": {
    "chrome": {
      "settings": {
        "transform": {
          "html": {
            "posthtml": { // npm i @pi-r/posthtml
              "transform": {
                "plugins": [
                  ["posthtml-doctype", { "doctype": "HTML 5" }],
                  ["posthtml-include", { "root": "./", "encoding": "utf-8" }],
                  "posthtml-attrs-sorter",
                  ["htmlnano", { "collapseWhitespace": "conservative" }]
                ]
              },
              "transform-output": {
                "directives": [
                  { "name": "?php", "start": "<", "end": ">" }
                ]
              }
            },
            "prettier": { // npm i @pi-r/prettier
              "beautify": {
                "parser": "html",
                "printWidth": 120,
                "tabWidth": 4
              }
            }
          },
          "css": {
            "postcss": { // npm i @pi-r/postcss
              "transform": {
                "plugins": [
                  "autoprefixer",
                  ["cssnano", { "preset": ["cssnano-preset-default", { "discardComments": false }] }]
                ]
              }
            }
          }
        }
      }
    }
  }
}

Using inline function [1]

The suffix “-output” is used to create the variable options.outputConfig.

{
  "document": {
    "chrome": {
      "settings": {
        "transform": {
          "js": {
            "terser": { // npm i @pi-r/terser
              "minify-example": "async (terser, value, options, require) => await terser.minify(value, options.outputConfig).code;", // Asynchronous
              "minify-example-output": {
                "keep_classnames": true // "minify-example-output"
              }
            }
          },
          "css": {
            "sass": { // npm i @pi-r/sass
              "sass-example": "(sass, value, options, resolve, require) => resolve(sass.renderSync({ ...options.outputConfig, data: value }).css);", // Synchronous
              "sass-example-output": {
                "outputStyle": "compressed",
                "sourceMap": true,
                "sourceMapContents": true
              }
            }
          }
        }
      }
    }
  }
}

Caution

Arrow functions are supported for convenience. Explicit function use is recommended.

Using local file

{
  "document": {
    "chrome": {
      "settings": {
        "transform": {
          "js": {
            "@babel/core": { // npm i @pi-r/babel
              "es5-example": "./es5.js", // JS extension uses Function constructor
              "es5-example-output": {
                "presets": ["@babel/preset-env"]
              },
              "es5-debug": "./es5-debug.cjs", // CJS extension loaded using "require"
              "es5-debug-output": {
                "presets": ["@babel/preset-env"]
              },
              "es6-example": "./es6.mjs", // MJS extension loaded using dynamic "import"
            }
          }
        }
      }
    }
  }
}
es5.js (function only)
function (context, value, options, resolve, require) {
  const path = require("path");
  context.transform(value, options.outputConfig, function (err, result) {
    resolve(!err && result ? result.code : "");
  });
}
es5-debug.cjs
const path = require("path");
let ID = 0;

module.exports = async function (context, value, options) {
  return await context.transform(`/* ${ID++} */` + value, options.outputConfig).code;
}
es6.mjs [2]
import path from "node:path";
let ID = 0;

export default async function (context, value, options) {
  return await context.transform(`/* ${ID++} */` + value, options.outputConfig).code;
}``.mjs``.

Caution

The .js extension uses the “type” value in your package.json to determine which module loader to use. It is better to be explicit using either .cjs or

Using custom package

You can create or use a package from NPM which will behave like a built-in transformer. The only difference is the context parameter being set to the Document module.

{
  "document": {
    "chrome": {
      "settings": {
        "transform": {
          "js": {
            /* Override built-in transformer */
            "@babel/core": {
              /* npm i babel-custom */
              "npm-example": "npm:babel-custom", // function(Document, value, options) { const context = require("babel-custom"); }
              "npm-example-output": {
                "presets": ["@babel/preset-env"]
              },
              /* OR */
              "npm-example-output": "npm:babel-custom-output" // Not recommended (npm i babel-custom-output)
            }
          },
          "css": {
            /* npm i sass-custom */
            "sass-custom": {
              "transform": { // options.baseConfig
                "sourceMap": true
              }
            }
          }
        }
      }
    }
  }
}

Attention

The setting name has to match the NPM package name.

Using page template

The same concept can be used inline anywhere using a script tag with the type attribute set to “text/template”. The script template will be completely removed from the final output.

<script type="text/template" data-chrome-template="js::@babel/core::es5-example">
  async function (context, value, options) {
    const options = { ...options.toBaseConfig(), presets: ["@babel/preset-env"], sourceMaps: true };
    const result = await context.transform(value, options);
    if (result) {
      if (result.map) {
        options.sourceMap.nextMap("babel", result.code, result.map);
      }
      return result.code;
    }
  }
</script>
Alternate
{
  "selector": "",
  "type": "js",
  "template": {
    "module": "@babel/core",
    "identifier": "es5-example",
    "value": "async function (context, value, options) {/* Same */}" // Arrow functions not supported
  }
}

Attention

Using data-chrome-template requires the setting eval.template = true.