<commit_before>import { BuildContext, BuildState, ChangedFile, File } from './util/interfaces';
import { changeExtension } from './util/helpers';
import { Logger } from './logger/logger';
import { getJsOutputDest } from './bundle';
import { invalidateCache } from './rollup';
import { dirname, extname, join, parse, resolve } from 'path';
import { readFileSync, writeFileSync } from 'fs';


export function templateUpdate(changedFiles: ChangedFile[], context: BuildContext) {
  try {
    const changedTemplates = changedFiles.filter(changedFile => changedFile.ext === '.html');
    const start = Date.now();
    const bundleOutputDest = getJsOutputDest(context);
    let bundleSourceText = readFileSync(bundleOutputDest, 'utf8');

    // update the corresponding transpiled javascript file with the template changed (inline it)
    // as well as the bundle
    for (const changedTemplateFile of changedTemplates) {
      const file = context.fileCache.get(changedTemplateFile.filePath);
      if (! updateCorrespondingJsFile(context, file.content, changedTemplateFile.filePath)) {
        throw new Error(`Failed to inline template ${changedTemplateFile.filePath}`);
      }
      bundleSourceText = replaceExistingJsTemplate(bundleSourceText, file.content, changedTemplateFile.filePath);
    }

    // invaldiate any rollup bundles, if they're not using rollup no harm done
    invalidateCache();

    // awesome, all good and template updated in the bundle file
    const logger = new Logger(`template update`);
    logger.setStartTime(start);

    writeFileSync(bundleOutputDest, bundleSourceText, { encoding: 'utf8'});

    // congrats, all gud
    changedTemplates.forEach(changedTemplate => {
      Logger.debug(`templateUpdate, updated: ${changedTemplate.filePath}`);
    });

    context.templateState = BuildState.SuccessfulBuild;
    logger.finish();
    resolve();

  } catch (ex) {
    Logger.debug(`templateUpdate error: ${ex.message}`);
    context.transpileState = BuildState.RequiresBuild;
    context.bundleState = BuildState.RequiresUpdate;
    return Promise.resolve();
  }
}

function updateCorrespondingJsFile(context: BuildContext, newTemplateContent: string, existingHtmlTemplatePath: string) {
  const javascriptFiles = context.fileCache.getAll().filter((file: File) => dirname(file.path) === dirname(existingHtmlTemplatePath) && extname(file.path) === '.js');
  for (const javascriptFile of javascriptFiles) {
    const newContent = replaceExistingJsTemplate(javascriptFile.content, newTemplateContent, existingHtmlTemplatePath);
    if (newContent && newContent !== javascriptFile.content) {
      javascriptFile.content = newContent;
      // set the file again to generate a new timestamp
      // do the same for the typescript file just to invalidate any caches, etc.
      context.fileCache.set(javascriptFile.path, javascriptFile);
      const typescriptFilePath = changeExtension(javascriptFile.path, '.ts');
      context.fileCache.set(typescriptFilePath, context.fileCache.get(typescriptFilePath));
      return true;
    }
  }<commit_msg>refactor(template): support looking up multiple in-memory bundles for inlining templates<commit_after>import { readFileSync, writeFileSync } from 'fs';
import { dirname, extname, join, parse, resolve } from 'path';

import { BuildContext, BuildState, ChangedFile, File } from './util/interfaces';
import { changeExtension } from './util/helpers';
import { Logger } from './logger/logger';
import { invalidateCache } from './rollup';



export function templateUpdate(changedFiles: ChangedFile[], context: BuildContext) {
  try {
    const changedTemplates = changedFiles.filter(changedFile => changedFile.ext === '.html');
    const start = Date.now();

    const bundleFiles = context.fileCache.getAll().filter(file => file.path.indexOf(context.buildDir) >= 0 && extname(file.path) === '.js');

    // update the corresponding transpiled javascript file with the template changed (inline it)
    // as well as the bundle
    for (const changedTemplateFile of changedTemplates) {
      const file = context.fileCache.get(changedTemplateFile.filePath);
      if (!updateCorrespondingJsFile(context, file.content, changedTemplateFile.filePath)) {
        throw new Error(`Failed to inline template ${changedTemplateFile.filePath}`);
      }
      // find the corresponding bundle
      for (const bundleFile of bundleFiles) {
        const newContent = replaceExistingJsTemplate(bundleFile.content, file.content, changedTemplateFile.filePath);
        if (newContent && newContent !== bundleFile.content) {
          context.fileCache.set(bundleFile.path, { path: bundleFile.path, content: newContent});
          writeFileSync(bundleFile.path, newContent);
          break;
        }
      }
    }

    // invaldiate any rollup bundles, if they're not using rollup no harm done
    invalidateCache();

    // awesome, all good and template updated in the bundle file
    const logger = new Logger(`template update`);
    logger.setStartTime(start);

    // congrats, all good
    changedTemplates.forEach(changedTemplate => {
      Logger.debug(`templateUpdate, updated: ${changedTemplate.filePath}`);
    });

    context.templateState = BuildState.SuccessfulBuild;
    logger.finish();
    return Promise.resolve();

  } catch (ex) {
    Logger.debug(`templateUpdate error: ${ex.message}`);
    context.transpileState = BuildState.RequiresBuild;
    context.bundleState = BuildState.RequiresUpdate;
    return Promise.resolve();
  }
}

function updateCorrespondingJsFile(context: BuildContext, newTemplateContent: string, existingHtmlTemplatePath: string) {
  const javascriptFiles = context.fileCache.getAll().filter((file: File) => dirname(file.path) === dirname(existingHtmlTemplatePath) && extname(file.path) === '.js');
  for (const javascriptFile of javascriptFiles) {
    const newContent = replaceExistingJsTemplate(javascriptFile.content, newTemplateContent, existingHtmlTemplatePath);
    if (newContent && newContent !== javascriptFile.content) {
      javascriptFile.content = newContent;
      // set the file again to generate a new timestamp
      // do the same for the typescript file just to invalidate any caches, etc.
      context.fileCache.set(javascriptFile.path, javascriptFile);
      const typescriptFilePath = changeExtension(javascriptFile.path, '.ts');
      context.fileCache.set(typescriptFilePath, context.fileCache.get(typescriptFilePath));
      return true;
    }
  }
{'fraction_non_alphanumeric': 0.10961865060985024, 'fraction_numerical': 0.0004631773969430292, 'mean_word_length': 4.270951993490643, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3573950', 'n_tokens_mistral': 1733, 'n_tokens_neox': 1685, 'n_words': 456}