File size: 2,221 Bytes
bf8b26e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { EditorView, Decoration, type DecorationSet, ViewPlugin, WidgetType } from '@codemirror/view';

// Create a proper WidgetType class for the masked text
class MaskedTextWidget extends WidgetType {
  constructor(private readonly _value: string) {
    super();
  }

  eq(other: MaskedTextWidget) {
    return other._value === this._value;
  }

  toDOM() {
    const span = document.createElement('span');
    span.textContent = '*'.repeat(this._value.length);
    span.className = 'cm-masked-text';

    return span;
  }

  ignoreEvent() {
    return false;
  }
}

export function createEnvMaskingExtension(getFilePath: () => string | undefined) {
  return ViewPlugin.fromClass(
    class {
      decorations: DecorationSet;

      constructor(view: EditorView) {
        this.decorations = this.buildDecorations(view);
      }

      update(update: { docChanged: boolean; view: EditorView; viewportChanged: boolean }) {
        if (update.docChanged || update.viewportChanged) {
          this.decorations = this.buildDecorations(update.view);
        }
      }

      buildDecorations(view: EditorView) {
        const filePath = getFilePath();
        const isEnvFile = filePath?.endsWith('.env') || filePath?.includes('.env.') || filePath?.includes('/.env');

        if (!isEnvFile) {
          return Decoration.none;
        }

        const decorations: any[] = [];
        const doc = view.state.doc;

        for (let i = 1; i <= doc.lines; i++) {
          const line = doc.line(i);
          const text = line.text;

          // Match lines with KEY=VALUE format
          const match = text.match(/^([^=]+)=(.+)$/);

          if (match && !text.trim().startsWith('#')) {
            const [, key, value] = match;
            const valueStart = line.from + key.length + 1;

            // Create a decoration that replaces the value with asterisks
            decorations.push(
              Decoration.replace({
                inclusive: true,
                widget: new MaskedTextWidget(value),
              }).range(valueStart, line.to),
            );
          }
        }

        return Decoration.set(decorations);
      }
    },
    {
      decorations: (v) => v.decorations,
    },
  );
}