Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@metalsmith/in-place

Package Overview
Dependencies
Maintainers
3
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@metalsmith/in-place - npm Package Compare versions

Comparing version
4.6.0
to
5.0.0
+293
lib/index.cjs
'use strict';
var isUtf8 = require('is-utf8');
var path = require('path');
var jstransformer = require('jstransformer');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return n;
}
var isUtf8__default = /*#__PURE__*/_interopDefaultLegacy(isUtf8);
var jstransformer__default = /*#__PURE__*/_interopDefaultLegacy(jstransformer);
/**
* Parse a filepath into dirname, base & extensions
* @param {string} filename
*/
function parseFilepath(filename) {
const isNested = filename.includes(path.sep);
const dir = isNested ? path.dirname(filename) : '';
const [base, ...extensions] = path.basename(filename).split('.');
return {
dirname: dir,
base,
extensions
};
}
/**
* @param {string} filename
* @param {import('./index').Options} opts
* @returns {string}
*/
function handleExtname(filename, opts) {
const {
dirname,
base,
extensions
} = parseFilepath(filename);
const extname = opts.extname && opts.extname.slice(1);
// decouples file extension chaining order from transformer usage order
for (let i = extensions.length; i--;) {
if (opts.transform.inputFormats.includes(extensions[i])) {
extensions.splice(i, 1);
break;
}
}
const isLast = !extensions.length;
if (isLast && extname) extensions.push(extname);
return [path.join(dirname, base), ...extensions].join('.');
}
/**
* Get a transformer by name ("jstransformer-ejs"), shortened name ("ejs") or filesystem path
* @param {string|JsTransformer} namePathOrTransformer
* @param {import('metalsmith').Debugger} debug
* @returns {Promise<JsTransformer>}
*/
function getTransformer(namePathOrTransformer, debug) {
let transform = null;
const t = namePathOrTransformer;
const tName = t;
const tPath = t;
// let the jstransformer constructor throw errors
if (typeof t !== 'string') {
transform = Promise.resolve(t);
} else {
if (path.isAbsolute(tPath) || tPath.startsWith('.') || tName.startsWith('jstransformer-')) {
debug('Importing transformer: %s', tPath);
transform = (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(tPath).then(t => t.default);
} else {
debug('Importing transformer: jstransformer-%s', tName);
// suppose a shorthand where the jstransformer- prefix is omitted, more likely
transform = (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(`jstransformer-${tName}`).then(t => t.default).catch(() => {
// else fall back to trying to import the name
debug.warn('"jstransformer-%s" not found, trying "%s" instead', tName, tName);
return (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })(tName).then(t => t.default);
});
}
}
return transform.then(t => {
return jstransformer__default["default"](t);
});
}
/**
* @callback Render
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @returns {string}
*/
/**
* @callback RenderAsync
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @param {Function} callback
* @returns {Promise<string>}
*/
/**
* @callback Compile
* @param {string} source
* @param {Object} options
* @returns {string}
*/
/**
* @callback CompileAsync
* @param {string} source
* @param {Object} options
* @param {Function} callback
* @returns {Promise<string>}
*/
/**
* @typedef {Object} JsTransformer
* @property {string} name
* @property {string[]} inputFormats
* @property {string} outputFormat
* @property {Render} [render]
* @property {RenderAsync} [renderAsync]
* @property {Compile} [compile]
* @property {CompileAsync} [compileAsync]
*/
/* c8 ignore start */
let debug = () => {
throw new Error('uninstantiated debug');
};
/* c8 ignore end */
async function render({
filename,
files,
metalsmith,
options,
transform
}) {
const file = files[filename];
const engineOptions = Object.assign({}, options.engineOptions);
if (options.engineOptions.filename) {
Object.assign(engineOptions, {
// set the filename in options for jstransformers requiring it (like Pug)
filename: metalsmith.path(metalsmith.source(), filename)
});
}
const metadata = metalsmith.metadata();
debug(`rendering ${filename}`);
const locals = Object.assign({}, metadata, file);
const contents = file.contents.toString();
return transform.renderAsync(contents, engineOptions, locals).then(rendered => {
const newName = handleExtname(filename, {
...options,
transform
});
debug('Done rendering %s', filename);
debug('Renaming "%s" to "%s"', filename, newName);
if (newName !== filename) {
delete files[filename];
files[newName] = file;
}
files[newName].contents = Buffer.from(rendered.body);
}).catch(err => {
err.message = `${filename}: ${err.message}`;
throw err;
});
}
/**
* Validate, checks whether a file should be processed
*/
function validate({
filename,
files,
transform
}) {
const {
extensions
} = parseFilepath(filename);
debug(`validating ${filename} %O %O`, extensions, transform.inputFormats);
// IF the transform has inputFormats defined, invalidate the file if it has no matching extname
if (transform.inputFormats && !extensions.some(fmt => transform.inputFormats.includes(fmt))) {
debug.warn('Validation failed for file "%s", transformer %s supports extensions %s.', filename, transform.name, transform.inputFormats.map(i => `.${i}`).join(', '));
}
// Files that are not utf8 are ignored
if (!isUtf8__default["default"](files[filename].contents)) {
debug.warn(`Validation failed, %s is not utf-8`, filename);
return false;
}
return true;
}
/**
* @typedef {Object} Options
* @property {string|JsTransformer} transform Jstransformer to run: name of a node module or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)
* @property {string} [pattern='**\/*.<transform.inputFormats>'] (*optional*) One or more paths or glob patterns to limit the scope of the transform. Defaults to `'**\/*.<transform.inputFormats>*'`
* @property {Object} [engineOptions={}] (*optional*) Pass options to the jstransformer templating engine that's rendering your files. The default is `{}`
* @property {string} [extname] (*optional*) Pass `''` to remove the extension or `'.<extname>'` to keep or rename it. Defaults to `transform.outputFormat`
**/
/**
* Set default options based on jstransformer `transform`
* @param {JsTransformer} transform
* @returns {Options}
*/
function normalizeOptions(transform) {
const extMatch = transform.inputFormats.length === 1 ? transform.inputFormats[0] : `{${transform.inputFormats.join(',')}}`;
return {
pattern: `**/*.${extMatch}*`,
extname: `.${transform.outputFormat}`,
engineOptions: {}
};
}
/**
* A metalsmith plugin for in-place templating
* @param {Options} options
* @returns {import('metalsmith').Plugin}
*/
function inPlace(options = {}) {
let transform;
return async function inPlace(files, metalsmith, done) {
debug = metalsmith.debug('@metalsmith/in-place');
// Check whether the pattern option is valid
if (options.pattern && !(typeof options.pattern === 'string' || Array.isArray(options.pattern))) {
return done(new Error('invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/@metalsmith/in-place#pattern'));
}
// skip resolving the transform option on repeat runs
if (!transform) {
try {
transform = await getTransformer(options.transform, debug);
} catch (err) {
// pass through jstransformer & Node import resolution errors
return done(err);
}
}
options = Object.assign(normalizeOptions(transform), options);
debug('Running with options %O', options);
const matchedFiles = metalsmith.match(options.pattern);
// Filter files by validity, pass basename to avoid dots in folder path
const validFiles = matchedFiles.filter(filename => validate({
filename,
files,
transform
}));
// Let the user know when there are no files to process
if (validFiles.length === 0) {
debug.warn('No valid files to process.');
return done();
} else {
debug('Rendering %s files', validFiles.length);
}
// Map all files that should be processed to an array of promises and call done when finished
return Promise.all(validFiles.map(filename => render({
filename,
files,
metalsmith,
options,
transform
}))).then(() => done()).catch(error => done(error));
};
}
module.exports = inPlace;
//# sourceMappingURL=index.cjs.map
{"version":3,"file":"index.cjs","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["import { dirname, join, basename, sep, isAbsolute } from 'path'\nimport jstransformer from 'jstransformer'\n\n/**\n * Parse a filepath into dirname, base & extensions\n * @param {string} filename\n */\nexport function parseFilepath(filename) {\n const isNested = filename.includes(sep)\n const dir = isNested ? dirname(filename) : ''\n const [base, ...extensions] = basename(filename).split('.')\n return { dirname: dir, base, extensions }\n}\n\n/**\n * @param {string} filename\n * @param {import('./index').Options} opts\n * @returns {string}\n */\nexport function handleExtname(filename, opts) {\n const { dirname, base, extensions } = parseFilepath(filename)\n const extname = opts.extname && opts.extname.slice(1)\n // decouples file extension chaining order from transformer usage order\n for (let i = extensions.length; i--; ) {\n if (opts.transform.inputFormats.includes(extensions[i])) {\n extensions.splice(i, 1)\n break\n }\n }\n const isLast = !extensions.length\n if (isLast && extname) extensions.push(extname)\n return [join(dirname, base), ...extensions].join('.')\n}\n\n/**\n * Get a transformer by name (\"jstransformer-ejs\"), shortened name (\"ejs\") or filesystem path\n * @param {string|JsTransformer} namePathOrTransformer\n * @param {import('metalsmith').Debugger} debug\n * @returns {Promise<JsTransformer>}\n */\nexport function getTransformer(namePathOrTransformer, debug) {\n let transform = null\n const t = namePathOrTransformer\n const tName = t\n const tPath = t\n\n // let the jstransformer constructor throw errors\n if (typeof t !== 'string') {\n transform = Promise.resolve(t)\n } else {\n if (isAbsolute(tPath) || tPath.startsWith('.') || tName.startsWith('jstransformer-')) {\n debug('Importing transformer: %s', tPath)\n transform = import(tPath).then((t) => t.default)\n } else {\n debug('Importing transformer: jstransformer-%s', tName)\n // suppose a shorthand where the jstransformer- prefix is omitted, more likely\n transform = import(`jstransformer-${tName}`)\n .then((t) => t.default)\n .catch(() => {\n // else fall back to trying to import the name\n debug.warn('\"jstransformer-%s\" not found, trying \"%s\" instead', tName, tName)\n return import(tName).then((t) => t.default)\n })\n }\n }\n return transform.then((t) => {\n return jstransformer(t)\n })\n}\n","import isUtf8 from 'is-utf8'\nimport { parseFilepath, handleExtname, getTransformer } from './utils.js'\n\n/**\n * @callback Render\n * @param {string} source\n * @param {Object} options\n * @param {Object} locals\n * @returns {string}\n */\n\n/**\n * @callback RenderAsync\n * @param {string} source\n * @param {Object} options\n * @param {Object} locals\n * @param {Function} callback\n * @returns {Promise<string>}\n */\n\n/**\n * @callback Compile\n * @param {string} source\n * @param {Object} options\n * @returns {string}\n */\n\n/**\n * @callback CompileAsync\n * @param {string} source\n * @param {Object} options\n * @param {Function} callback\n * @returns {Promise<string>}\n */\n\n/**\n * @typedef {Object} JsTransformer\n * @property {string} name\n * @property {string[]} inputFormats\n * @property {string} outputFormat\n * @property {Render} [render]\n * @property {RenderAsync} [renderAsync]\n * @property {Compile} [compile]\n * @property {CompileAsync} [compileAsync]\n */\n\n/* c8 ignore start */\nlet debug = () => {\n throw new Error('uninstantiated debug')\n}\n/* c8 ignore end */\n\nasync function render({ filename, files, metalsmith, options, transform }) {\n const file = files[filename]\n const engineOptions = Object.assign({}, options.engineOptions)\n if (options.engineOptions.filename) {\n Object.assign(engineOptions, {\n // set the filename in options for jstransformers requiring it (like Pug)\n filename: metalsmith.path(metalsmith.source(), filename)\n })\n }\n const metadata = metalsmith.metadata()\n debug(`rendering ${filename}`)\n\n const locals = Object.assign({}, metadata, file)\n const contents = file.contents.toString()\n\n return transform\n .renderAsync(contents, engineOptions, locals)\n .then((rendered) => {\n const newName = handleExtname(filename, { ...options, transform })\n debug('Done rendering %s', filename)\n debug('Renaming \"%s\" to \"%s\"', filename, newName)\n\n if (newName !== filename) {\n delete files[filename]\n files[newName] = file\n }\n files[newName].contents = Buffer.from(rendered.body)\n })\n .catch((err) => {\n err.message = `${filename}: ${err.message}`\n throw err\n })\n}\n\n/**\n * Validate, checks whether a file should be processed\n */\n\nfunction validate({ filename, files, transform }) {\n const { extensions } = parseFilepath(filename)\n debug(`validating ${filename} %O %O`, extensions, transform.inputFormats)\n\n // IF the transform has inputFormats defined, invalidate the file if it has no matching extname\n if (transform.inputFormats && !extensions.some((fmt) => transform.inputFormats.includes(fmt))) {\n debug.warn(\n 'Validation failed for file \"%s\", transformer %s supports extensions %s.',\n filename,\n transform.name,\n transform.inputFormats.map((i) => `.${i}`).join(', ')\n )\n }\n\n // Files that are not utf8 are ignored\n if (!isUtf8(files[filename].contents)) {\n debug.warn(`Validation failed, %s is not utf-8`, filename)\n return false\n }\n return true\n}\n\n/**\n * @typedef {Object} Options\n * @property {string|JsTransformer} transform Jstransformer to run: name of a node module or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)\n * @property {string} [pattern='**\\/*.<transform.inputFormats>'] (*optional*) One or more paths or glob patterns to limit the scope of the transform. Defaults to `'**\\/*.<transform.inputFormats>*'`\n * @property {Object} [engineOptions={}] (*optional*) Pass options to the jstransformer templating engine that's rendering your files. The default is `{}`\n * @property {string} [extname] (*optional*) Pass `''` to remove the extension or `'.<extname>'` to keep or rename it. Defaults to `transform.outputFormat`\n **/\n\n/**\n * Set default options based on jstransformer `transform`\n * @param {JsTransformer} transform\n * @returns {Options}\n */\nfunction normalizeOptions(transform) {\n const extMatch =\n transform.inputFormats.length === 1 ? transform.inputFormats[0] : `{${transform.inputFormats.join(',')}}`\n return {\n pattern: `**/*.${extMatch}*`,\n extname: `.${transform.outputFormat}`,\n engineOptions: {}\n }\n}\n\n/**\n * A metalsmith plugin for in-place templating\n * @param {Options} options\n * @returns {import('metalsmith').Plugin}\n */\nfunction inPlace(options = {}) {\n let transform\n\n return async function inPlace(files, metalsmith, done) {\n debug = metalsmith.debug('@metalsmith/in-place')\n\n // Check whether the pattern option is valid\n if (options.pattern && !(typeof options.pattern === 'string' || Array.isArray(options.pattern))) {\n return done(\n new Error(\n 'invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/@metalsmith/in-place#pattern'\n )\n )\n }\n\n // skip resolving the transform option on repeat runs\n if (!transform) {\n try {\n transform = await getTransformer(options.transform, debug)\n } catch (err) {\n // pass through jstransformer & Node import resolution errors\n return done(err)\n }\n }\n\n options = Object.assign(normalizeOptions(transform), options)\n\n debug('Running with options %O', options)\n\n const matchedFiles = metalsmith.match(options.pattern)\n\n // Filter files by validity, pass basename to avoid dots in folder path\n const validFiles = matchedFiles.filter((filename) => validate({ filename, files, transform }))\n\n // Let the user know when there are no files to process\n if (validFiles.length === 0) {\n debug.warn('No valid files to process.')\n return done()\n } else {\n debug('Rendering %s files', validFiles.length)\n }\n\n // Map all files that should be processed to an array of promises and call done when finished\n return Promise.all(validFiles.map((filename) => render({ filename, files, metalsmith, options, transform })))\n .then(() => done())\n .catch((error) => done(error))\n }\n}\n\nexport default inPlace\n"],"names":["parseFilepath","filename","isNested","includes","sep","dir","dirname","base","extensions","basename","split","handleExtname","opts","extname","slice","i","length","transform","inputFormats","splice","isLast","push","join","getTransformer","namePathOrTransformer","debug","t","tName","tPath","Promise","resolve","isAbsolute","startsWith","then","default","catch","warn","jstransformer","Error","render","files","metalsmith","options","file","engineOptions","Object","assign","path","source","metadata","locals","contents","toString","renderAsync","rendered","newName","Buffer","from","body","err","message","validate","some","fmt","name","map","isUtf8","normalizeOptions","extMatch","pattern","outputFormat","inPlace","done","Array","isArray","matchedFiles","match","validFiles","filter","all","error"],"mappings":"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"}
import { Plugin } from 'metalsmith';
export default initializeInPlace;
export type Render = (source: string, options: any, locals: any) => string;
export type RenderAsync = (source: string, options: any, locals: any, callback: Function) => Promise<string>;
export type Compile = (source: string, options: any) => string;
export type CompileAsync = (source: string, options: any, callback: Function) => Promise<string>;
export type JsTransformer = {
name: string;
inputFormats: string[];
outputFormat: string;
render?: Render;
renderAsync?: RenderAsync;
compile?: Compile;
compileAsync?: CompileAsync;
[key]?: string;
};
export type Options = {
/**
* Jstransformer to run: name of a node module or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)
*/
transform: string | JsTransformer;
/**
* One or more paths or glob patterns to limit the scope of the transform.
* @default '**\/*.<transform.inputFormats>'
*/
pattern?: string|string[];
/**
* Pass options to the jstransformer templating engine that's rendering your files.
* @default {}
*/
engineOptions?: any;
/**
* Pass `''` to remove the extension or `'.<extname>'` to keep or rename it.
* @default transform.outputFormat
*/
extname?: string;
};
/**
* A metalsmith plugin for in-place templating
* @example
* import nunjucks from 'jstransformer-nunjucks'
*
* metalsmith
* .use(inPlace({ transform: 'jstransformer-nunjucks' })) // use jstransformer-nunjucks
* .use(inPlace({ transform: 'nunjucks' })) // shorthand for above
* .use(inPlace({ transform: nunjucks })) // equivalent to above
* .use(inPlace({ transform: './local/transform.js' })) // custom local transformer
* .use(inPlace({ transform: { // custom inline transformer
* name: 'prepend-hello',
* inputFormats: ['prepend-hello'],
* outputFormat: 'html',
* render(str, options, locals) => {
* return 'hello ' + str
* }
* }}))
*/
declare function initializeInPlace(options?: Options): Plugin;
{"version":3,"file":"index.js","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["import { dirname, join, basename, sep, isAbsolute } from 'path'\nimport jstransformer from 'jstransformer'\n\n/**\n * Parse a filepath into dirname, base & extensions\n * @param {string} filename\n */\nexport function parseFilepath(filename) {\n const isNested = filename.includes(sep)\n const dir = isNested ? dirname(filename) : ''\n const [base, ...extensions] = basename(filename).split('.')\n return { dirname: dir, base, extensions }\n}\n\n/**\n * @param {string} filename\n * @param {import('./index').Options} opts\n * @returns {string}\n */\nexport function handleExtname(filename, opts) {\n const { dirname, base, extensions } = parseFilepath(filename)\n const extname = opts.extname && opts.extname.slice(1)\n // decouples file extension chaining order from transformer usage order\n for (let i = extensions.length; i--; ) {\n if (opts.transform.inputFormats.includes(extensions[i])) {\n extensions.splice(i, 1)\n break\n }\n }\n const isLast = !extensions.length\n if (isLast && extname) extensions.push(extname)\n return [join(dirname, base), ...extensions].join('.')\n}\n\n/**\n * Get a transformer by name (\"jstransformer-ejs\"), shortened name (\"ejs\") or filesystem path\n * @param {string|JsTransformer} namePathOrTransformer\n * @param {import('metalsmith').Debugger} debug\n * @returns {Promise<JsTransformer>}\n */\nexport function getTransformer(namePathOrTransformer, debug) {\n let transform = null\n const t = namePathOrTransformer\n const tName = t\n const tPath = t\n\n // let the jstransformer constructor throw errors\n if (typeof t !== 'string') {\n transform = Promise.resolve(t)\n } else {\n if (isAbsolute(tPath) || tPath.startsWith('.') || tName.startsWith('jstransformer-')) {\n debug('Importing transformer: %s', tPath)\n transform = import(tPath).then((t) => t.default)\n } else {\n debug('Importing transformer: jstransformer-%s', tName)\n // suppose a shorthand where the jstransformer- prefix is omitted, more likely\n transform = import(`jstransformer-${tName}`)\n .then((t) => t.default)\n .catch(() => {\n // else fall back to trying to import the name\n debug.warn('\"jstransformer-%s\" not found, trying \"%s\" instead', tName, tName)\n return import(tName).then((t) => t.default)\n })\n }\n }\n return transform.then((t) => {\n return jstransformer(t)\n })\n}\n","import isUtf8 from 'is-utf8'\nimport { parseFilepath, handleExtname, getTransformer } from './utils.js'\n\n/**\n * @callback Render\n * @param {string} source\n * @param {Object} options\n * @param {Object} locals\n * @returns {string}\n */\n\n/**\n * @callback RenderAsync\n * @param {string} source\n * @param {Object} options\n * @param {Object} locals\n * @param {Function} callback\n * @returns {Promise<string>}\n */\n\n/**\n * @callback Compile\n * @param {string} source\n * @param {Object} options\n * @returns {string}\n */\n\n/**\n * @callback CompileAsync\n * @param {string} source\n * @param {Object} options\n * @param {Function} callback\n * @returns {Promise<string>}\n */\n\n/**\n * @typedef {Object} JsTransformer\n * @property {string} name\n * @property {string[]} inputFormats\n * @property {string} outputFormat\n * @property {Render} [render]\n * @property {RenderAsync} [renderAsync]\n * @property {Compile} [compile]\n * @property {CompileAsync} [compileAsync]\n */\n\n/* c8 ignore start */\nlet debug = () => {\n throw new Error('uninstantiated debug')\n}\n/* c8 ignore end */\n\nasync function render({ filename, files, metalsmith, options, transform }) {\n const file = files[filename]\n const engineOptions = Object.assign({}, options.engineOptions)\n if (options.engineOptions.filename) {\n Object.assign(engineOptions, {\n // set the filename in options for jstransformers requiring it (like Pug)\n filename: metalsmith.path(metalsmith.source(), filename)\n })\n }\n const metadata = metalsmith.metadata()\n debug(`rendering ${filename}`)\n\n const locals = Object.assign({}, metadata, file)\n const contents = file.contents.toString()\n\n return transform\n .renderAsync(contents, engineOptions, locals)\n .then((rendered) => {\n const newName = handleExtname(filename, { ...options, transform })\n debug('Done rendering %s', filename)\n debug('Renaming \"%s\" to \"%s\"', filename, newName)\n\n if (newName !== filename) {\n delete files[filename]\n files[newName] = file\n }\n files[newName].contents = Buffer.from(rendered.body)\n })\n .catch((err) => {\n err.message = `${filename}: ${err.message}`\n throw err\n })\n}\n\n/**\n * Validate, checks whether a file should be processed\n */\n\nfunction validate({ filename, files, transform }) {\n const { extensions } = parseFilepath(filename)\n debug(`validating ${filename} %O %O`, extensions, transform.inputFormats)\n\n // IF the transform has inputFormats defined, invalidate the file if it has no matching extname\n if (transform.inputFormats && !extensions.some((fmt) => transform.inputFormats.includes(fmt))) {\n debug.warn(\n 'Validation failed for file \"%s\", transformer %s supports extensions %s.',\n filename,\n transform.name,\n transform.inputFormats.map((i) => `.${i}`).join(', ')\n )\n }\n\n // Files that are not utf8 are ignored\n if (!isUtf8(files[filename].contents)) {\n debug.warn(`Validation failed, %s is not utf-8`, filename)\n return false\n }\n return true\n}\n\n/**\n * @typedef {Object} Options\n * @property {string|JsTransformer} transform Jstransformer to run: name of a node module or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)\n * @property {string} [pattern='**\\/*.<transform.inputFormats>'] (*optional*) One or more paths or glob patterns to limit the scope of the transform. Defaults to `'**\\/*.<transform.inputFormats>*'`\n * @property {Object} [engineOptions={}] (*optional*) Pass options to the jstransformer templating engine that's rendering your files. The default is `{}`\n * @property {string} [extname] (*optional*) Pass `''` to remove the extension or `'.<extname>'` to keep or rename it. Defaults to `transform.outputFormat`\n **/\n\n/**\n * Set default options based on jstransformer `transform`\n * @param {JsTransformer} transform\n * @returns {Options}\n */\nfunction normalizeOptions(transform) {\n const extMatch =\n transform.inputFormats.length === 1 ? transform.inputFormats[0] : `{${transform.inputFormats.join(',')}}`\n return {\n pattern: `**/*.${extMatch}*`,\n extname: `.${transform.outputFormat}`,\n engineOptions: {}\n }\n}\n\n/**\n * A metalsmith plugin for in-place templating\n * @param {Options} options\n * @returns {import('metalsmith').Plugin}\n */\nfunction inPlace(options = {}) {\n let transform\n\n return async function inPlace(files, metalsmith, done) {\n debug = metalsmith.debug('@metalsmith/in-place')\n\n // Check whether the pattern option is valid\n if (options.pattern && !(typeof options.pattern === 'string' || Array.isArray(options.pattern))) {\n return done(\n new Error(\n 'invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/@metalsmith/in-place#pattern'\n )\n )\n }\n\n // skip resolving the transform option on repeat runs\n if (!transform) {\n try {\n transform = await getTransformer(options.transform, debug)\n } catch (err) {\n // pass through jstransformer & Node import resolution errors\n return done(err)\n }\n }\n\n options = Object.assign(normalizeOptions(transform), options)\n\n debug('Running with options %O', options)\n\n const matchedFiles = metalsmith.match(options.pattern)\n\n // Filter files by validity, pass basename to avoid dots in folder path\n const validFiles = matchedFiles.filter((filename) => validate({ filename, files, transform }))\n\n // Let the user know when there are no files to process\n if (validFiles.length === 0) {\n debug.warn('No valid files to process.')\n return done()\n } else {\n debug('Rendering %s files', validFiles.length)\n }\n\n // Map all files that should be processed to an array of promises and call done when finished\n return Promise.all(validFiles.map((filename) => render({ filename, files, metalsmith, options, transform })))\n .then(() => done())\n .catch((error) => done(error))\n }\n}\n\nexport default inPlace\n"],"names":["parseFilepath","filename","isNested","includes","sep","dir","dirname","base","extensions","basename","split","handleExtname","opts","extname","slice","i","length","transform","inputFormats","splice","isLast","push","join","getTransformer","namePathOrTransformer","debug","t","tName","tPath","Promise","resolve","isAbsolute","startsWith","then","default","catch","warn","jstransformer","Error","render","files","metalsmith","options","file","engineOptions","Object","assign","path","source","metadata","locals","contents","toString","renderAsync","rendered","newName","Buffer","from","body","err","message","validate","some","fmt","name","map","isUtf8","normalizeOptions","extMatch","pattern","outputFormat","inPlace","done","Array","isArray","matchedFiles","match","validFiles","filter","all","error"],"mappings":"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"}
+211
-126

@@ -1,81 +0,162 @@

const path = require('path')
const isUtf8 = require('is-utf8')
const getTransformer = require('./get-transformer')
let debug = () => {
throw new Error('uninstantiated debug')
}
import isUtf8 from 'is-utf8';
import { isAbsolute, sep, dirname, basename, join } from 'path';
import jstransformer from 'jstransformer';
/**
* Parse a filepath into dirname, base & extensions
* @param {string} filename
*/
function parseFilepath(filename) {
const isNested = filename.includes(path.sep)
const dirname = isNested ? path.dirname(filename) : ''
const [base, ...extensions] = path.basename(filename).split('.')
return { dirname, base, extensions }
const isNested = filename.includes(sep);
const dir = isNested ? dirname(filename) : '';
const [base, ...extensions] = basename(filename).split('.');
return {
dirname: dir,
base,
extensions
};
}
/**
* Engine, renders file contents with all available transformers
* @param {string} filename
* @param {import('./index').Options} opts
* @returns {string}
*/
function render({ filename, files, metalsmith, settings }) {
const { dirname, base, extensions } = parseFilepath(filename)
const file = files[filename]
const engineOptions = Object.assign({}, settings.engineOptions)
const metadata = metalsmith.metadata()
const isLastExtension = extensions.length === 1
debug(`rendering ${filename}`)
const ext = extensions.pop()
const transform = getTransformer(ext)
const locals = Object.assign({}, metadata, file)
// Stop if the current extension can't be transformed
if (!transform) {
debug(`no transformer available for ${ext} extension for ${filename}`)
return Promise.resolve()
function handleExtname(filename, opts) {
const {
dirname,
base,
extensions
} = parseFilepath(filename);
const extname = opts.extname && opts.extname.slice(1);
// decouples file extension chaining order from transformer usage order
for (let i = extensions.length; i--;) {
if (opts.transform.inputFormats.includes(extensions[i])) {
extensions.splice(i, 1);
break;
}
}
const isLast = !extensions.length;
if (isLast && extname) extensions.push(extname);
return [join(dirname, base), ...extensions].join('.');
}
// Stringify file contents
const contents = file.contents.toString()
/**
* Get a transformer by name ("jstransformer-ejs"), shortened name ("ejs") or filesystem path
* @param {string|JsTransformer} namePathOrTransformer
* @param {import('metalsmith').Debugger} debug
* @returns {Promise<JsTransformer>}
*/
function getTransformer(namePathOrTransformer, debug) {
let transform = null;
const t = namePathOrTransformer;
const tName = t;
const tPath = t;
// If this is the last extension, replace it with a new one
if (isLastExtension) {
debug(`last extension reached, replacing last extension with ${transform.outputFormat}`)
extensions.push(transform.outputFormat)
// let the jstransformer constructor throw errors
if (typeof t !== 'string') {
transform = Promise.resolve(t);
} else {
if (isAbsolute(tPath) || tPath.startsWith('.') || tName.startsWith('jstransformer-')) {
debug('Importing transformer: %s', tPath);
transform = import(tPath).then(t => t.default);
} else {
debug('Importing transformer: jstransformer-%s', tName);
// suppose a shorthand where the jstransformer- prefix is omitted, more likely
transform = import(`jstransformer-${tName}`).then(t => t.default).catch(() => {
// else fall back to trying to import the name
debug.warn('"jstransformer-%s" not found, trying "%s" instead', tName, tName);
return import(tName).then(t => t.default);
});
}
}
return transform.then(t => {
return jstransformer(t);
});
}
// Check if the filename should be set in the engine options
if (settings.setFilename) {
debug(`setting filename in the engine options`)
engineOptions.filename = path.join(metalsmith.source(), filename)
}
/**
* @callback Render
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @returns {string}
*/
// Transform the contents
debug(`rendering ${ext} extension for ${filename}`)
/**
* @callback RenderAsync
* @param {string} source
* @param {Object} options
* @param {Object} locals
* @param {Function} callback
* @returns {Promise<string>}
*/
return transform
.renderAsync(contents, engineOptions, locals)
.then((rendered) => {
// Delete old file
delete files[filename] // eslint-disable-line no-param-reassign
/**
* @callback Compile
* @param {string} source
* @param {Object} options
* @returns {string}
*/
// Update files with the newly rendered file
const newName = [path.join(dirname, base), ...extensions].join('.')
files[newName] = file // eslint-disable-line no-param-reassign
files[newName].contents = Buffer.from(rendered.body) // eslint-disable-line no-param-reassign
/**
* @callback CompileAsync
* @param {string} source
* @param {Object} options
* @param {Function} callback
* @returns {Promise<string>}
*/
debug(`done rendering ${filename}, renamed to ${newName}`)
/**
* @typedef {Object} JsTransformer
* @property {string} name
* @property {string[]} inputFormats
* @property {string} outputFormat
* @property {Render} [render]
* @property {RenderAsync} [renderAsync]
* @property {Compile} [compile]
* @property {CompileAsync} [compileAsync]
*/
// Stop rendering if this was the last extension
if (isLastExtension) {
return Promise.resolve()
}
/* c8 ignore start */
let debug = () => {
throw new Error('uninstantiated debug');
};
/* c8 ignore end */
// Otherwise, keep rendering until there are no applicable transformers left
return render({ filename: newName, files, metalsmith, settings })
})
.catch((err) => {
err.message = `${filename}: ${err.message}` // eslint-disable-line no-param-reassign
throw err
})
async function render({
filename,
files,
metalsmith,
options,
transform
}) {
const file = files[filename];
const engineOptions = Object.assign({}, options.engineOptions);
if (options.engineOptions.filename) {
Object.assign(engineOptions, {
// set the filename in options for jstransformers requiring it (like Pug)
filename: metalsmith.path(metalsmith.source(), filename)
});
}
const metadata = metalsmith.metadata();
debug(`rendering ${filename}`);
const locals = Object.assign({}, metadata, file);
const contents = file.contents.toString();
return transform.renderAsync(contents, engineOptions, locals).then(rendered => {
const newName = handleExtname(filename, {
...options,
transform
});
debug('Done rendering %s', filename);
debug('Renaming "%s" to "%s"', filename, newName);
if (newName !== filename) {
delete files[filename];
files[newName] = file;
}
files[newName].contents = Buffer.from(rendered.body);
}).catch(err => {
err.message = `${filename}: ${err.message}`;
throw err;
});
}

@@ -87,10 +168,15 @@

function validate({ filename, files }) {
debug(`validating ${filename}`)
const { extensions } = parseFilepath(filename)
function validate({
filename,
files,
transform
}) {
const {
extensions
} = parseFilepath(filename);
debug(`validating ${filename} %O %O`, extensions, transform.inputFormats);
// Files without an extension cannot be processed
if (!extensions.length) {
debug(`validation failed, ${filename} does not have an extension`)
return false
// IF the transform has inputFormats defined, invalidate the file if it has no matching extname
if (transform.inputFormats && !extensions.some(fmt => transform.inputFormats.includes(fmt))) {
debug.warn('Validation failed for file "%s", transformer %s supports extensions %s.', filename, transform.name, transform.inputFormats.map(i => `.${i}`).join(', '));
}

@@ -100,15 +186,6 @@

if (!isUtf8(files[filename].contents)) {
debug(`validation failed, ${filename} is not utf-8`)
return false
debug.warn(`Validation failed, %s is not utf-8`, filename);
return false;
}
// Files without an applicable jstransformer are ignored
const extension = extensions[extensions.length - 1]
const transformer = getTransformer(extension)
if (!transformer) {
debug(`validation failed, no jstransformer found for last extension of ${filename}`)
}
return transformer
return true;
}

@@ -118,14 +195,20 @@

* @typedef {Object} Options
* @property {string} [pattern='**'] (*optional*) Limit the files to process by 1 or more glob patterns. Defaults to `'**'` (all)
* @property {string|JsTransformer} transform Jstransformer to run: name of a node module or local JS module path (starting with `.`) whose default export is a jstransformer. As a shorthand for existing transformers you can remove the `jstransformer-` prefix: `marked` will be understood as `jstransformer-marked`. Or an actual jstransformer; an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)
* @property {string} [pattern='**\/*.<transform.inputFormats>'] (*optional*) One or more paths or glob patterns to limit the scope of the transform. Defaults to `'**\/*.<transform.inputFormats>*'`
* @property {Object} [engineOptions={}] (*optional*) Pass options to the jstransformer templating engine that's rendering your files. The default is `{}`
* @property {boolean} [suppressNoFilesError=false] (*optional*) Decide whether to ignore an error indicating that the plugin didn't find any matching files to process. The default is `false`
* @property {boolean} [setFilename=false] (*optional*) Some templating engines, like [pug](https://github.com/pugjs/pug), need a `filename` property to be present in the options to be able to process relative includes, extends, etc. Setting this option to `true` will add the current filename to the options passed to each jstransformer. The default is `false`
* @property {string} [extname] (*optional*) Pass `''` to remove the extension or `'.<extname>'` to keep or rename it. Defaults to `transform.outputFormat`
**/
/** @type {Options} */
const defaultOptions = {
pattern: '**',
engineOptions: {},
suppressNoFilesError: false,
setFilename: false
/**
* Set default options based on jstransformer `transform`
* @param {JsTransformer} transform
* @returns {Options}
*/
function normalizeOptions(transform) {
const extMatch = transform.inputFormats.length === 1 ? transform.inputFormats[0] : `{${transform.inputFormats.join(',')}}`;
return {
pattern: `**/*.${extMatch}*`,
extname: `.${transform.outputFormat}`,
engineOptions: {}
};
}

@@ -138,50 +221,52 @@

*/
function initializeInPlace(options = defaultOptions) {
const settings = Object.assign({}, defaultOptions, options)
function inPlace(options = {}) {
let transform;
return async function inPlace(files, metalsmith, done) {
debug = metalsmith.debug('@metalsmith/in-place');
return function inPlace(files, metalsmith, done) {
debug = metalsmith.debug('@metalsmith/in-place')
debug('Running with options %O', settings)
// Check whether the pattern option is valid
if (!(typeof settings.pattern === 'string' || Array.isArray(settings.pattern))) {
return done(
new Error(
'invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/@metalsmith/in-place#pattern'
)
)
if (options.pattern && !(typeof options.pattern === 'string' || Array.isArray(options.pattern))) {
return done(new Error('invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/@metalsmith/in-place#pattern'));
}
// throw update error in case users didn't see the peerDependency warning
/* istanbul ignore next */
if (!metalsmith.match) {
throw new Error(
'This version of @metalsmith/in-place requires metalsmith^2.4.1\'s newly added match method\nPlease update metalsmith"'
)
// skip resolving the transform option on repeat runs
if (!transform) {
try {
transform = await getTransformer(options.transform, debug);
} catch (err) {
// pass through jstransformer & Node import resolution errors
return done(err);
}
}
options = Object.assign(normalizeOptions(transform), options);
debug('Running with options %O', options);
const matchedFiles = metalsmith.match(options.pattern);
const matchedFiles = metalsmith.match(settings.pattern)
// Filter files by validity, pass basename to avoid dots in folder path
const validFiles = matchedFiles.filter((filename) => validate({ filename, files }))
const validFiles = matchedFiles.filter(filename => validate({
filename,
files,
transform
}));
// Let the user know when there are no files to process, usually caused by missing jstransformer
// Let the user know when there are no files to process
if (validFiles.length === 0) {
const message = 'no files to process. See https://www.npmjs.com/package/@metalsmith/in-place#no-files-to-process'
if (settings.suppressNoFilesError) {
debug(message)
return done()
}
return done(new Error(message))
debug.warn('No valid files to process.');
return done();
} else {
debug('Rendering %s files', validFiles.length);
}
// Map all files that should be processed to an array of promises and call done when finished
return Promise.all(validFiles.map((filename) => render({ filename, files, metalsmith, settings })))
.then(() => done())
.catch(/* istanbul ignore next */ (error) => done(error))
}
return Promise.all(validFiles.map(filename => render({
filename,
files,
metalsmith,
options,
transform
}))).then(() => done()).catch(error => done(error));
};
}
module.exports = initializeInPlace
export { inPlace as default };
//# sourceMappingURL=index.js.map
{
"name": "@metalsmith/in-place",
"author": "ismay",

@@ -11,4 +12,10 @@ "description": "A metalsmith plugin for in-place templating",

],
"main": "lib/index.js",
"name": "@metalsmith/in-place",
"source": "src/index.js",
"main": "lib/index.cjs",
"module": "lib/index.js",
"type": "module",
"exports": {
"import": "./lib/index.js",
"require": "./lib/index.cjs"
},
"repository": {

@@ -22,10 +29,12 @@ "type": "git",

},
"version": "4.6.0",
"version": "5.0.0",
"scripts": {
"changelog": "auto-changelog -u --starting-date 2021-12-01 --sort-commits date-desc --commit-limit false --ignore-commit-pattern '(dev|Release|Merge)'",
"coverage": "nyc report --reporter=text-lcov > ./coverage.info",
"changelog": "auto-changelog -u --starting-date 2021-12-01 --commit-limit false --ignore-commit-pattern '^((dev|chore|ci):|Release)'",
"coverage": "npm test && c8 report --reporter=text-lcov > ./coverage.info",
"format": "prettier --write \"**/*.{yml,md,js,json}\"",
"format:check": "prettier --list-different \"**/*.{yml,md,js,json}\"",
"test": "nyc mocha",
"test": "c8 mocha",
"release": "release-it",
"build": "microbundle --target node -f cjs,esm --strict --generateTypes=false",
"prepack": "npm run build",
"lint": "eslint --fix .",

@@ -37,18 +46,19 @@ "lint:check": "eslint --fix-dry-run ."

"auto-changelog": "^2.4.0",
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0",
"c8": "^7.14.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.5.1",
"husky": "^7.0.4",
"eslint-plugin-n": "^16.0.1",
"husky": "^8.0.3",
"jstransformer-handlebars": "^1.2.0",
"jstransformer-marked": "^1.2.0",
"jstransformer-pug": "^0.4.0",
"metalsmith": "^2.5.1",
"mocha": "^9.2.2",
"metalsmith": "^2.6.1",
"microbundle": "^0.15.1",
"mocha": "^10.2.0",
"nyc": "^15.1.0",
"prettier": "^2.8.0",
"release-it": "^15.5.1"
"prettier": "^3.0.0",
"release-it": "^16.1.2"
},
"dependencies": {
"inputformat-to-jstransformer": "^1.2.1",
"is-utf8": "^0.2.1",

@@ -71,4 +81,4 @@ "jstransformer": "^1.0.0"

"engines": {
"node": ">=12"
"node": ">=14.14.0"
}
}
+130
-78
# @metalsmith/in-place
A metalsmith plugin for transforming your source files
A metalsmith plugin for transforming source files' contents. Complements [@metalsmith/layouts](https://github.com/metalsmith/layouts)

@@ -11,5 +11,7 @@ [![metalsmith: core plugin][metalsmith-badge]][metalsmith-url]

This plugin allows you to render templating syntax in your source files. It uses file extensions to infer which templating engine to use. So files ending in `.njk` will be processed as nunjucks, `.md` as markdown, etc. You can even chain transformations by appending multiple extensions, which will be processed right-to-left.
## Features
If you want to wrap your source files in a common template, you can use [@metalsmith/layouts](https://github.com/metalsmith/metalsmith/layouts). For usage examples check out our [wiki](https://github.com/metalsmith/metalsmith/in-place/wiki). Feel free to contribute an example if anything is missing, or update the existing ones. For templating engine specific questions try the aforementioned channels, as well as the documentation for [jstransformers](https://github.com/jstransformers) and your templating engine of choice.
- renders source files' `contents` field with any existing or a custom [Jstransformer templating engine](https://github.com/jstransformers/jstransformer)
- alters file extensions from `transform.inputFormats` to `transform.outputFormat`
- can be used multiple times with different configs per metalsmith pipeline

@@ -21,3 +23,3 @@ ## Installation

```bash
npm install @metalsmith/in-place
npm install @metalsmith/in-place jstransformer-handlebars
```

@@ -28,83 +30,92 @@

```bash
yarn add @metalsmith/in-place
yarn add @metalsmith/in-place jstransformer-handlebars
```
This plugin works with [jstransformers](https://github.com/jstransformers/jstransformer) but they should be installed separately. `jstransformer-handlebars` is just an example, you could use any transformer. To render markdown you could install [jstransformer-marked](https://github.com/jstransformers/jstransformer-marked). To render handlebars you would install [jstransformer-handlebars](https://github.com/jstransformers/jstransformer-handlebars). Other popular templating options include: [Nunjucks](https://github.com/jstransformers/jstransformer-nunjucks), [Twig](https://github.com/jstransformers/jstransformer-twig), [Pug](https://github.com/jstransformers/jstransformer-pug), or [EJS](https://github.com/jstransformers/jstransformer-ejs). See also [this map](https://github.com/jstransformers/inputformat-to-jstransformer/blob/master/dictionary.json) to see which extensions map to which jstransformer.
## Usage
This plugin uses [jstransformers](https://github.com/jstransformers/jstransformer) to transform files. Since there are a lot of jstransformers we don't install them automatically, so you'll also need to install the appropriate jstransformers.
Pass `@metalsmith/in-place` to `metalsmith.use` :
For example, to render markdown you would install [jstransformer-markdown](https://github.com/jstransformers/jstransformer-markdown). To render handlebars you would install [jstransformer-handlebars](https://github.com/jstransformers/jstransformer-handlebars). Other popular templating options are: Nunjucks, Twig, Pug, EJS. See the [jstransformer organisation](https://github.com/jstransformers) for all available jstransformers and [this dictionary](https://github.com/jstransformers/inputformat-to-jstransformer/blob/master/dictionary.json) to see which extensions map to which jstransformer.
```js
import inPlace from '@metalsmith/in-place'
## Options
// shorthand
metalsmith.use(inPlace({ transform: 'nunjucks' }))
You can pass options to `@metalsmith/in-place` with the [Javascript API](https://github.com/segmentio/metalsmith#api) or [CLI](https://github.com/segmentio/metalsmith#cli). The options are:
// same as shorthand
metalsmith.use(
inPlace({
transform: jsTransformerNunjucks, // resolved
extname: '.html',
pattern: '**/*.{njk,nunjucks}*',
engineOptions: {}
})
)
```
- [pattern](#pattern): optional. Only files that match this pattern will be processed. Accepts a string or an array of strings. The default is `**`.
- [engineOptions](#engineoptions): optional. Use this to pass options to the jstransformer that's rendering your files. The default is `{}`.
- [suppressNoFilesError](#suppressnofileserror): optional. The no-files-to-process error will be suppressed. The default is `false`.
- [setFilename](#setfilename): optional. Some templating engines, like [pug](https://github.com/pugjs/pug), need a `filename` property to be present in the options to be able to process relative includes, extends, etc. Setting this option to `true` will add the current filename to the options passed to each jstransformer. The default is `false`.
In the transformed file, you have access to `{ ...metalsmith.metadata(), ...fileMetadata }`, so that the following build
### `pattern`
```js
metalsmith.metadata({ title: 'Default title', nodeVersion: process.version }).use(inPlace({ transform: 'handlebars' }))
```
Only files that match this pattern will be processed. So this `metalsmith.json`:
for a file:
```json
{
"source": "src",
"destination": "build",
"plugins": {
"@metalsmith/in-place": {
"pattern": "blog/**/*"
}
}
}
```yml
---
title: Article title
---
<h1>{{ title }}</h1>Node v{{ nodeVersion }}
```
Would only process files within the `./src/blog` folder, because the pattern is
relative to your source folder. See [Metalsmith#match](https://metalsmith.io/api/#Metalsmith+match)
for further details.
would render `<h1>Article title</h1>Node v16.20`
### `engineOptions`
Multiple transforms can be used to target different sets of files, or to reprocess the same files multiple times in the order they are `metalsmith.use`'d:
Use this to pass options to the jstransformer that's rendering your templates. So this
`metalsmith.json`:
```js
// this build will apply the marked transform to index.md, the handlebars transform to index.hbs,
// and handlebars first, marked second to both index.hbs.md, index.md.hbs, and html-minifier to all (only in production)
metalsmith
.env('NODE_ENV', process.env.NODE_ENV)
.use(inPlace({ transform: 'handlebars', extname: null }))
.use(inPlace({ transform: 'marked' }))
```json
{
"source": "src",
"destination": "build",
"plugins": {
"@metalsmith/in-place": {
"engineOptions": {
"cache": false
}
}
}
if (metalsmith.env('NODE_ENV') !== 'development') {
metalsmith.use(inPlace({ transform: 'html-minifier' }))
}
```
Would pass `{ "cache": false }` to each used jstransformer.
### Options
### `suppressNoFilesError`
In most cases, you will only need to specify the `transform` and `engineOptions` option.
`@metalsmith/in-place` exits with [an error](#no-files-to-process) if it can’t find any files to process. If you’re doing any kind of incremental builds via something like `metalsmith-watch`, this is problematic as you’re likely only rebuilding files that have changed. This flag allows you to suppress that error. So this `metalsmith.json`:
- [transform](#transform) (`string|JsTransformer`): **required**. Which transformer to use. The full name of the transformer, e.g. `jstransformer-handlebars`, its shorthand `handlebars`, a relative JS module path starting with `.`, e.g. `./my-transformer.js`, whose default export is a jstransformer or an actual jstransformer: an object with `name`, `inputFormats`,`outputFormat`, and at least one of the render methods `render`, `renderAsync`, `compile` or `compileAsync` described in the [jstransformer API docs](https://github.com/jstransformers/jstransformer#api)
- [extname](#extension-handling) (`string|false|null`): optional. How to transform a file's extensions: `''|false|null` to remove the last `transform.inputFormat` matching extension, `.<ext>` to force an extension rename.
- [engineOptions](#engineoptions) (`Object<string, any>`): optional. Pass options to the jstransformer that's rendering the files. The default is `{}`.
- pattern (`string|string[]`): optional. Override default glob pattern matching `**/*.<transform.inputFormats>*`. Useful to limit the scope of the transform by path or glob to a subfolder, or to include files not matching `transform.inputFormats`.
```json
{
"source": "src",
"destination": "build",
"plugins": {
"@metalsmith/in-place": {
"suppressNoFilesError": true
}
}
}
```
### Extension handling
Would suppress the error if there aren't any files to process. Note that when this option is turned on, if you're logging [debug messages](#errors-and-debugging), you’ll still see a message denoting when there aren't any files for metalsmith-layouts to process.
By default in-place will apply smart default extension handling based on `transform.inputFormats` and `transform.outputFormat`.
For example, any of the source files below processed through `inPlace({ transform: 'handlebars' })` will yield `index.html`.
### `setFilename`
| source | output |
| ------------------ | ---------------- |
| src/index.hbs | build/index.html |
| src/index.hbs.html | build/index.html |
| src/index.html.hbs | build/index.html |
Set this option to `true` if you want to pass the current filename to each jstransformer. The default is `false`. So this `metalsmith.json`:
The example demonstrates that:
- order of extensions doesn't matter, _order of plugin execution does!_: you can pick the final extension to match the most suitable editor syntax highlighting
- a single in-place run only alters the rightmost extension matching `transform.inputFormats`
- you may choose to include or omit the `transform.outputFormat` in the source file name (.html in this case).
### `engineOptions`
Pass options to the jstransformer that's rendering your templates via `engineOptions`. The
`metalsmith.json`:
```json

@@ -117,3 +128,6 @@ {

"@metalsmith/in-place": {
"setFilename": true
"transform": "ejs",
"engineOptions": {
"cache": false
}
}

@@ -125,34 +139,72 @@ }

Would overwrite `engineOptions.filename` with the absolute path for the file that's currently being processed, and pass that to the jstransformer. For now we're just passing `filename`, but if you encounter a jstransformer that requires a different property, like `path` or something else, let us know and we can add it.
..would pass `{ "cache": false }` to `jstransformer-ejs`.
## Errors and debugging
If you use [Pug](https://pugjs.org/api/getting-started.html), make sure to pass `engineOptions: { filename: true }`. This will ensure the filename of each processed file is passed to the render method as expected by this engine.
If you're encountering problems you can use [debug](https://www.npmjs.com/package/debug) to enable verbose logging. To enable `debug` prefix your build command with `DEBUG=@metalsmith/in-place`. So if you normally run `metalsmith` to build, use `DEBUG=@metalsmith/in-place metalsmith` (on windows the syntax is [slightly different](https://www.npmjs.com/package/debug#windows-note)).
### Multiple transforms per file
### No files to process
Suppose a file `tags.hbs` that lists all the article tags used on your website
There are several things that might cause you to get a `no files to process` error:
```hbs
---
title: Tags
description: Browse articles by tag
---
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<ul>
{{#each tags}}
<li><a href="/tags/{{ . }}">{{ . }}</a></li>
{{/each}}
</ul>
```
- Your [pattern](#pattern) does not match any files
- None of your files pass validation, validation fails for files that:
- Have no extension
- Are not utf-8
- Need a jstransformer that hasn't been installed
To reduce Handlebars noise, you could add `metalsmith.use(inPlace({ transform: 'marked' })` to your build and change the filename to `tags.hbs.md` to generate markdown syntax with Handlebars!
### Debug
```hbs
---
title: Tags
description: Browse articles by tag
---
To enable debug logs, set the `DEBUG` environment variable to `@metalsmith/in-place`:
# {{ title }}
Linux/Mac:
{{ description }}
```bash
export DEBUG=@metalsmith/in-place
{{#each tags}}
- [{{.}}](/tags/{{ . }})
{{/each}}
More markdown here..
```
Windows:
**Caution**: when using multiple templating transforms per file, make sure there is no conflicting syntax.
For example markdown will transform blocks indented by 4 spaces to `<pre>` tags, and marked's `smartypants` can potentially garble the result.
```bat
set "DEBUG=@metalsmith/in-place"
### Usage with @metalsmith/layouts
In most cases `@metalsmith/in-place` is intended to be used _before_ `@metalsmith/layouts`.
You can easily share `engineOptions` configs between both plugins:
```js
import inPlace from '@metalsmith/in-place'
import layouts from '@metalsmith/layouts'
const engineOptions = {}
metalsmith // index.hbs.hbs
.use(inPlace({ transform: 'handlebars', extname: '', engineOptions })) // -> index.hbs
.use(layouts({ engineOptions })) // -> index.html
```
@metalsmith/layouts uses a similar mechanism targeting `transform.inputFormats` file extensions by default.
The example requires files ending in `.hbs.hbs` extension, but if you don't like this, you can just have a single `.hbs` extension, and change the in-place invocation to `inPlace({ engineOptions, transform, extname: '.hbs' })` for the same result.
### Debug
To enable debug logs, set the `DEBUG` environment variable to `@metalsmith/in-place*`:
```js
metalsmith.env('DEBUG', '@metalsmith/in-place*')
```
Alternatively you can set `DEBUG` to `@metalsmith/*` to debug all Metalsmith core plugins.

@@ -159,0 +211,0 @@

const jstransformer = require('jstransformer')
const toTransformer = require('inputformat-to-jstransformer')
/**
* Gets jstransformer for an extension, and caches them
*/
const cache = {}
function getTransformer(ext) {
if (ext in cache) {
return cache[ext]
}
const transformer = toTransformer(ext)
cache[ext] = transformer ? jstransformer(transformer) : false
return cache[ext]
}
module.exports = getTransformer