applyTemplate function exported

Last updated: 2026-03-04T23:21:38.398Z

Metrics

LOC: 140 Complexity: 54 Params: 2

Signature

applyTemplate( form: SavedForm, ): : Promise<{ filled: number }>

Summary

Applies a saved form template to the current page. Uses templateFields (new format) if available, otherwise falls back to legacy fields. For generator-mode fields, calls the appropriate generator to produce a fresh value.

Architecture violations

View all

  • [warning] max-cyclomatic-complexity: 'applyTemplate' has cyclomatic complexity 54 (max 10)
  • [warning] max-lines: 'applyTemplate' has 140 lines (max 80)

Source Code

export async function applyTemplate(
  form: SavedForm,
): Promise<{ filled: number }> {
  const { fields: allDetectedFields } = await detectAllFieldsAsync();
  const settings = await getSettings();
  const url = window.location.href;

  // Skip fields the user has marked as ignored
  const ignoredFields = await getIgnoredFieldsForUrl(url);
  const ignoredSelectors = new Set(ignoredFields.map((f) => f.selector));
  const detectedFields = allDetectedFields.filter(
    (f) => !ignoredSelectors.has(f.selector),
  );

  let filled = 0;

  if (form.templateFields && form.templateFields.length > 0) {
    // Pre-generate ONE consistent value per field type (fixed or generator).
    // Generator fields are produced once so that derivations remain coherent —
    // e.g. first-name and last-name derived from the same generated full-name.
    const templateValueMap = new Map<FieldType, string>();
    for (const tField of form.templateFields) {
      if (!tField.matchByFieldType) continue;
      if (tField.mode === "generator" && tField.generatorType) {
        templateValueMap.set(
          tField.matchByFieldType,
          generate(tField.generatorType, tField.generatorParams ?? undefined),
        );
      } else if (tField.mode === "fixed" && tField.fixedValue) {
        templateValueMap.set(tField.matchByFieldType, tField.fixedValue);
      }
    }

    // For type-based templates (matchByFieldType), track which fields were already handled
    const handledSelectors = new Set<string>();

    for (const tField of form.templateFields) {
      // Type-based matching: find ALL fields of the given type
      if (tField.matchByFieldType) {
        const matchedFields = detectedFields.filter(
          (f) =>
            f.fieldType === tField.matchByFieldType &&
            !handledSelectors.has(f.selector),
        );
        for (const matchedField of matchedFields) {
          // Use the pre-generated value to keep derived fields coherent
          const value = templateValueMap.get(tField.matchByFieldType) ?? "";
          if (!value) continue;
          await applyValueToField(matchedField, value);
          if (settings.highlightFilled) {
            highlightField(
              matchedField.element,
              matchedField.label ?? matchedField.fieldType ?? undefined,
            );
          }
          handledSelectors.add(matchedField.selector);
          filled++;
        }
        continue;
      }

      // Selector-based matching (legacy / saved-from-page templates)
      const matchedField = detectedFields.find(
        (f) =>
          f.selector === tField.key ||
          f.id === tField.key ||
          f.name === tField.key,
      );
      if (!matchedField) continue;

      let value: string;
      if (tField.mode === "generator" && tField.generatorType) {
        value = generate(
          tField.generatorType,
          tField.generatorParams ?? undefined,
        );
      } else {
        value = tField.fixedValue ?? "";
      }

      if (!value && tField.mode === "fixed") continue;

      await applyValueToField(matchedField, value);
      if (settings.highlightFilled) {
        highlightField(
          matchedField.element,
          matchedField.label ?? matchedField.fieldType ?? undefined,
        );
      }
      handledSelectors.add(matchedField.selector);
      filled++;
    }

    // Fallback: fill detected fields not covered by the template.
    // Priority: (1) direct match in map (same type, missed the loop somehow)
    //           (2) smart derivation from a related type (e.g. first-name ← full-name)
    for (const field of detectedFields) {
      if (handledSelectors.has(field.selector) || !field.fieldType) continue;

      const value =
        templateValueMap.get(field.fieldType) ??
        deriveFieldValueFromTemplate(field.fieldType, templateValueMap);

      if (!value) continue;
      await applyValueToField(field, value);
      if (settings.highlightFilled) {
        highlightField(
          field.element,
          field.label ?? field.fieldType ?? undefined,
        );
      }
      filled++;
    }
  } else {
    // Legacy format: fields Record<string, string>
    for (const detectedField of detectedFields) {
      const key =
        detectedField.id || detectedField.name || detectedField.selector;
      const value =
        form.fields[detectedField.selector] ??
        form.fields[key] ??
        (detectedField.name ? form.fields[detectedField.name] : undefined) ??
        (detectedField.id ? form.fields[detectedField.id] : undefined);

      if (value === undefined) continue;

      await applyValueToField(detectedField, value);
      if (settings.highlightFilled) {
        highlightField(
          detectedField.element,
          detectedField.label ?? detectedField.fieldType ?? undefined,
        );
      }
      filled++;
    }
  }

  log.info(`Template "${form.name}" aplicado: ${filled} campos`);
  return { filled };
}

Dependencies (Outgoing)

graph LR applyTemplate["applyTemplate"] detectAllFieldsAsync["detectAllFieldsAsync"] applyValueToField["applyValueToField"] highlightField["highlightField"] applyTemplate -->|calls| detectAllFieldsAsync applyTemplate -->|calls| applyValueToField applyTemplate -->|calls| highlightField style applyTemplate fill:#dbeafe,stroke:#2563eb,stroke-width:2px click applyTemplate "2ec007fc3b6a3513.html" click detectAllFieldsAsync "1b422b3353cdbe22.html" click applyValueToField "59a962012828c5cb.html" click highlightField "d079b946779ebfc9.html"

Impact (Incoming)

graph LR applyTemplate["applyTemplate"] FillableElement["FillableElement"] makeInput["makeInput"] FillableElement -->|uses| applyTemplate makeInput -->|uses| applyTemplate style applyTemplate fill:#dbeafe,stroke:#2563eb,stroke-width:2px click applyTemplate "2ec007fc3b6a3513.html" click FillableElement "2ecf5aaac3f668a8.html" click makeInput "3b463c3c3297bb7c.html"
SourceType
FillableElement uses
makeInput uses