CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mem-fs-editor

File edition helpers working on top of mem-fs

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

template-processing.mddocs/

Template Processing

EJS template processing for dynamic file generation with context variables and template options, enabling creation of customized files from templates.

Capabilities

Copy with Template Processing

Copy files and process them as EJS templates, substituting variables with provided context data.

/**
 * Copy files with EJS template processing
 * @param from - Source template files or glob pattern
 * @param to - Destination path
 * @param context - Template variables for substitution
 * @param tplSettings - EJS template configuration options
 * @param options - Copy options
 */
function copyTpl(
  from: string | string[],
  to: string,
  context?: Record<string, any>,
  tplSettings?: EJSOptions,
  options?: CopySingleOptions
): void;

interface EJSOptions {
  /** Template filename for error reporting */
  filename?: string;
  /** Enable template caching for performance */
  cache?: boolean;
  /** Custom opening delimiter (default: "<%") */
  openDelimiter?: string;
  /** Custom closing delimiter (default: "%>") */
  closeDelimiter?: string;
  /** Additional EJS configuration options */
  [key: string]: any;
}

interface CopySingleOptions {
  /** Append to destination instead of overwriting */
  append?: boolean;
  /** Process file contents during copy */
  process?: (contents: string | Buffer, filepath: string, destination: string) => string | Buffer;
}

Usage Examples:

import { create as createMemFs } from "mem-fs";
import { create as createEditor } from "mem-fs-editor";

const store = createMemFs();
const fs = createEditor(store);

// Basic template processing
const context = {
  name: "MyProject",
  version: "1.0.0",
  author: "John Doe"
};

fs.copyTpl("templates/package.json.ejs", "package.json", context);

// Note: .ejs extensions are automatically removed from destination paths
fs.copyTpl("template.txt.ejs", "output/", context);  
// Creates: output/template.txt (not output/template.txt.ejs)

// Multiple template files
fs.copyTpl("templates/**/*.ejs", "output/", {
  projectName: "awesome-app",
  features: ["auth", "database", "api"],
  config: {
    port: 3000,
    database: "mongodb"
  }
});

// Template with custom delimiters
fs.copyTpl("template.txt", "output.txt", { name: "World" }, {
  openDelimiter: "{{",
  closeDelimiter: "}}"
});

Async Template Copy

Asynchronous template processing for better performance with large template sets.

/**
 * Async version of template copy
 * @param from - Source template files or glob pattern
 * @param to - Destination path
 * @param context - Template variables for substitution
 * @param tplSettings - EJS template configuration options
 * @param options - Copy options
 * @returns Promise that resolves when template processing is complete
 */
function copyTplAsync(
  from: string | string[],
  to: string,
  context?: Record<string, any>,
  tplSettings?: EJSOptions,
  options?: CopySingleOptions
): Promise<void>;

Usage Examples:

// Async template processing
await fs.copyTplAsync("templates/**/*.ejs", "generated/", {
  timestamp: new Date().toISOString(),
  environment: "production",
  features: {
    authentication: true,
    logging: true,
    monitoring: false
  }
});

// Process large template sets
const contexts = [
  { name: "frontend", type: "react" },
  { name: "backend", type: "node" },
  { name: "database", type: "postgres" }
];

for (const ctx of contexts) {
  await fs.copyTplAsync(`templates/${ctx.type}/**/*.ejs`, `services/${ctx.name}/`, ctx);
}

Append Template Content

Append templated content to existing files, useful for building up files incrementally.

/**
 * Append templated content to file
 * @param filepath - Target file path
 * @param contents - Template content string
 * @param context - Template variables for substitution
 * @param templateOptions - EJS template settings
 * @param options - Append configuration options
 */
function appendTpl(
  filepath: string,
  contents: string,
  context: Record<string, any>,
  templateOptions?: EJSOptions,
  options?: AppendOptions
): void;

interface AppendOptions {
  /** Create file if it doesn't exist (default: true) */
  create?: boolean;
  /** Remove trailing whitespace before appending (default: false) */
  trimEnd?: boolean;
  /** Separator to add between existing and new content */
  separator?: string;
}

Usage Examples:

// Build up a file with multiple template appends
const baseContext = { projectName: "MyApp" };

// Initialize file
fs.write("config.js", "// Generated configuration\n");

// Append database config
fs.appendTpl("config.js", `
const database = {
  host: '<%= host %>',
  port: <%= port %>,
  name: '<%= dbName %>'
};
`, { 
  host: "localhost", 
  port: 5432, 
  dbName: baseContext.projectName.toLowerCase() 
}, null, { separator: "\n" });

// Append server config
fs.appendTpl("config.js", `
const server = {
  port: <%= serverPort %>,
  env: '<%= environment %>'
};
`, {
  serverPort: 3000,
  environment: "development"
}, null, { separator: "\n" });

Internal Template Processing

Direct template processing function for custom workflows.

/**
 * Process file contents as EJS template
 * @param params - Template processing parameters
 * @returns Processed template content
 */
function _processTpl(params: {
  contents: string | Buffer;
  filename: string;
  context?: Record<string, any>;
  tplSettings?: EJSOptions;
}): string;

Usage Examples:

// Process template content directly
const templateContent = `
Hello <%= name %>!
Your project <%= projectName %> is ready.
Features: <% features.forEach(feature => { %>
- <%= feature %>
<% }); %>
`;

const processed = fs._processTpl({
  contents: templateContent,
  filename: "greeting.txt",
  context: {
    name: "Developer",
    projectName: "AwesomeApp",
    features: ["Authentication", "Database", "API"]
  }
});

fs.write("output.txt", processed);

Template Syntax and Examples

Basic Variable Substitution

<!-- Template: greeting.ejs -->
Hello <%= name %>!
Welcome to <%= projectName %>.
// Usage
fs.copyTpl("greeting.ejs", "greeting.txt", {
  name: "Alice",
  projectName: "Amazing App"
});
// Output: "Hello Alice!\nWelcome to Amazing App."

Conditional Content

<!-- Template: config.ejs -->
{
  "name": "<%= name %>",
  "version": "<%= version %>"<% if (features.includes('auth')) { %>,
  "authentication": {
    "provider": "<%= authProvider || 'local' %>"
  }<% } %><% if (features.includes('database')) { %>,
  "database": {
    "type": "<%= dbType || 'sqlite' %>"
  }<% } %>
}
fs.copyTpl("config.ejs", "config.json", {
  name: "my-app",
  version: "1.0.0",
  features: ["auth", "database"],
  authProvider: "oauth2",
  dbType: "postgresql"
});

Loops and Iteration

<!-- Template: routes.ejs -->
<% routes.forEach(route => { %>
app.<%= route.method %>('<%= route.path %>', <%= route.handler %>);
<% }); %>

<!-- Generated imports -->
<% dependencies.forEach(dep => { %>
const <%= dep.name %> = require('<%= dep.package %>');
<% }); %>
fs.copyTpl("routes.ejs", "routes.js", {
  routes: [
    { method: "get", path: "/users", handler: "getUsersHandler" },
    { method: "post", path: "/users", handler: "createUserHandler" },
    { method: "delete", path: "/users/:id", handler: "deleteUserHandler" }
  ],
  dependencies: [
    { name: "express", package: "express" },
    { name: "cors", package: "cors" }
  ]
});

Complex Context Objects

// Complex template context
const context = {
  project: {
    name: "e-commerce-api",
    version: "2.1.0",
    description: "REST API for e-commerce platform"
  },
  database: {
    type: "postgresql",
    host: "localhost",
    port: 5432,
    ssl: false
  },
  features: {
    authentication: { enabled: true, provider: "jwt" },
    logging: { enabled: true, level: "info" },
    monitoring: { enabled: false }
  },
  endpoints: [
    { path: "/api/products", methods: ["GET", "POST"] },
    { path: "/api/orders", methods: ["GET", "POST", "PUT"] },
    { path: "/api/users", methods: ["GET", "POST", "DELETE"] }
  ]
};

fs.copyTpl("templates/**/*.ejs", "generated/", context);

Error Handling and Debugging

// Template processing with error handling  
try {
  fs.copyTpl("template.ejs", "output.txt", context, {
    filename: "template.ejs" // Helps with error reporting
  });
} catch (error) {
  if (error.message.includes("ReferenceError")) {
    console.error("Template variable not found:", error.message);
  } else {
    console.error("Template processing failed:", error.message);
  }
}

// Debug template context
const debugContext = {
  ...context,
  _debug: true,
  _timestamp: new Date().toISOString()
};

fs.copyTpl("debug-template.ejs", "debug-output.txt", debugContext);

Custom Delimiters

// Use custom delimiters for templates that conflict with default EJS syntax
fs.copyTpl("template.txt", "output.txt", { name: "World" }, {
  openDelimiter: "{{",
  closeDelimiter: "}}"
});

// Template content with custom delimiters:
// "Hello {{ name }}! Today is {{ new Date().toDateString() }}."

docs

commit-pipeline.md

file-copy.md

file-management.md

file-reading.md

file-writing.md

index.md

state-management.md

template-processing.md

transform.md

tile.json