|  | 
|  | 1 | +const mjml2html = require("mjml"); | 
|  | 2 | +const { registerComponent } = require("mjml-core"); | 
|  | 3 | +const fs = require("fs"); | 
|  | 4 | +const path = require("path"); | 
|  | 5 | +const glob = require("glob"); | 
|  | 6 | + | 
|  | 7 | +// Parse command line arguments | 
|  | 8 | +const args = process.argv.slice(2); // Remove 'node' and script path | 
|  | 9 | + | 
|  | 10 | +// Parse flags | 
|  | 11 | +const flags = { | 
|  | 12 | +  minify: args.includes("--minify") || args.includes("-m"), | 
|  | 13 | +  watch: args.includes("--watch") || args.includes("-w"), | 
|  | 14 | +  hbs: args.includes("--hbs") || args.includes("-h"), | 
|  | 15 | +  trace: args.includes("--trace") || args.includes("-t"), | 
|  | 16 | +  clean: args.includes("--clean") || args.includes("-c"), | 
|  | 17 | +  help: args.includes("--help"), | 
|  | 18 | +}; | 
|  | 19 | + | 
|  | 20 | +// Use __dirname to get absolute paths relative to the script location | 
|  | 21 | +const config = { | 
|  | 22 | +  inputDir: path.join(__dirname, "emails"), | 
|  | 23 | +  outputDir: path.join(__dirname, "out"), | 
|  | 24 | +  minify: flags.minify, | 
|  | 25 | +  validationLevel: "strict", | 
|  | 26 | +  hbsOutput: flags.hbs, | 
|  | 27 | +}; | 
|  | 28 | + | 
|  | 29 | +// Debug output | 
|  | 30 | +if (flags.trace) { | 
|  | 31 | +  console.log("[DEBUG] Script location:", __dirname); | 
|  | 32 | +  console.log("[DEBUG] Input directory:", config.inputDir); | 
|  | 33 | +  console.log("[DEBUG] Output directory:", config.outputDir); | 
|  | 34 | +} | 
|  | 35 | + | 
|  | 36 | +// Ensure output directory exists | 
|  | 37 | +if (!fs.existsSync(config.outputDir)) { | 
|  | 38 | +  fs.mkdirSync(config.outputDir, { recursive: true }); | 
|  | 39 | +  if (flags.trace) { | 
|  | 40 | +    console.log("[INFO] Created output directory:", config.outputDir); | 
|  | 41 | +  } | 
|  | 42 | +} | 
|  | 43 | + | 
|  | 44 | +// Find all MJML files with absolute path | 
|  | 45 | +const mjmlFiles = glob.sync(`${config.inputDir}/**/*.mjml`); | 
|  | 46 | + | 
|  | 47 | +console.log(`\n[INFO] Found ${mjmlFiles.length} MJML file(s) to compile...`); | 
|  | 48 | + | 
|  | 49 | +if (mjmlFiles.length === 0) { | 
|  | 50 | +  console.error("[ERROR] No MJML files found!"); | 
|  | 51 | +  console.error("[ERROR] Looked in:", config.inputDir); | 
|  | 52 | +  console.error( | 
|  | 53 | +    "[ERROR] Does this directory exist?", | 
|  | 54 | +    fs.existsSync(config.inputDir), | 
|  | 55 | +  ); | 
|  | 56 | +  process.exit(1); | 
|  | 57 | +} | 
|  | 58 | + | 
|  | 59 | +// Compile each MJML file | 
|  | 60 | +let successCount = 0; | 
|  | 61 | +let errorCount = 0; | 
|  | 62 | + | 
|  | 63 | +mjmlFiles.forEach((filePath) => { | 
|  | 64 | +  try { | 
|  | 65 | +    const mjmlContent = fs.readFileSync(filePath, "utf8"); | 
|  | 66 | +    const fileName = path.basename(filePath, ".mjml"); | 
|  | 67 | +    const relativePath = path.relative(config.inputDir, filePath); | 
|  | 68 | + | 
|  | 69 | +    console.log(`\n[BUILD] Compiling: ${relativePath}`); | 
|  | 70 | + | 
|  | 71 | +    // Compile MJML to HTML | 
|  | 72 | +    const result = mjml2html(mjmlContent, { | 
|  | 73 | +      minify: config.minify, | 
|  | 74 | +      validationLevel: config.validationLevel, | 
|  | 75 | +      filePath: filePath, // Important: tells MJML where the file is for resolving includes | 
|  | 76 | +      mjmlConfigPath: __dirname, // Point to the directory with .mjmlconfig | 
|  | 77 | +    }); | 
|  | 78 | + | 
|  | 79 | +    // Check for errors | 
|  | 80 | +    if (result.errors.length > 0) { | 
|  | 81 | +      console.error(`[ERROR] Failed to compile ${fileName}.mjml:`); | 
|  | 82 | +      result.errors.forEach((err) => | 
|  | 83 | +        console.error(`        ${err.formattedMessage}`), | 
|  | 84 | +      ); | 
|  | 85 | +      errorCount++; | 
|  | 86 | +      return; | 
|  | 87 | +    } | 
|  | 88 | + | 
|  | 89 | +    // Calculate output path preserving directory structure | 
|  | 90 | +    const relativeDir = path.dirname(relativePath); | 
|  | 91 | +    const outputDir = path.join(config.outputDir, relativeDir); | 
|  | 92 | + | 
|  | 93 | +    // Ensure subdirectory exists | 
|  | 94 | +    if (!fs.existsSync(outputDir)) { | 
|  | 95 | +      fs.mkdirSync(outputDir, { recursive: true }); | 
|  | 96 | +    } | 
|  | 97 | + | 
|  | 98 | +    const outputExtension = config.hbsOutput ? ".html.hbs" : ".html"; | 
|  | 99 | +    const outputPath = path.join(outputDir, `${fileName}${outputExtension}`); | 
|  | 100 | +    fs.writeFileSync(outputPath, result.html); | 
|  | 101 | + | 
|  | 102 | +    console.log( | 
|  | 103 | +      `[OK] Built: ${fileName}.mjml → ${path.relative(__dirname, outputPath)}`, | 
|  | 104 | +    ); | 
|  | 105 | +    successCount++; | 
|  | 106 | + | 
|  | 107 | +    // Log warnings if any | 
|  | 108 | +    if (result.warnings && result.warnings.length > 0) { | 
|  | 109 | +      console.warn(`[WARN] Warnings for ${fileName}.mjml:`); | 
|  | 110 | +      result.warnings.forEach((warn) => | 
|  | 111 | +        console.warn(`       ${warn.formattedMessage}`), | 
|  | 112 | +      ); | 
|  | 113 | +    } | 
|  | 114 | +  } catch (error) { | 
|  | 115 | +    console.error(`[ERROR] Exception processing ${path.basename(filePath)}:`); | 
|  | 116 | +    console.error(`        ${error.message}`); | 
|  | 117 | +    errorCount++; | 
|  | 118 | +  } | 
|  | 119 | +}); | 
|  | 120 | + | 
|  | 121 | +console.log(`\n[SUMMARY] Compilation complete!`); | 
|  | 122 | +console.log(`          Success: ${successCount}`); | 
|  | 123 | +console.log(`          Failed:  ${errorCount}`); | 
|  | 124 | +console.log(`          Output:  ${config.outputDir}`); | 
|  | 125 | + | 
|  | 126 | +if (errorCount > 0) { | 
|  | 127 | +  process.exit(1); | 
|  | 128 | +} | 
0 commit comments