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"]
              }
            }
          }
        }
      }
    }
  }
}
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;
}

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.