• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

i18next / i18next / #70074

19 Sep 2021 08:27AM UTC coverage: 93.9% (+3.1%) from 90.778%
#70074

push

adrai
remove deprecated whitelist features

1016 of 1082 relevant lines covered (93.9%)

108.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.89
/src/ResourceStore.js
1
import EventEmitter from './EventEmitter.js';
1✔
2
import * as utils from './utils.js';
1✔
3

4
function deepFind(obj, path, keySeparator = '.') {
185✔
5
  if (!obj) return undefined;
185✔
6
  if (obj[path]) return obj[path];
95✔
7
  const paths = path.split(keySeparator);
86✔
8
  let current = obj;
86✔
9
  for (let i = 0; i < paths.length; ++i) {
86✔
10
    if (!current) return undefined;
101✔
11
    if (typeof current[paths[i]] === 'string' && i + 1 < paths.length) {
101✔
12
      return undefined;
10✔
13
    }
14
    if (current[paths[i]] === undefined) {
91✔
15
      let j = 2;
73✔
16
      let p = paths.slice(i, i + j).join(keySeparator);
73✔
17
      let mix = current[p];
73✔
18
      while (mix === undefined && paths.length > i + j) {
73✔
19
        j++;
1✔
20
        p = paths.slice(i, i + j).join(keySeparator);
1✔
21
        mix = current[p];
1✔
22
      }
23
      if (mix === undefined) return undefined;
73✔
24
      if (typeof mix === 'string') return mix;
10✔
25
      if (p && typeof mix[p] === 'string') return mix[p];
8✔
26
      const joinedPath = paths.slice(i + j).join(keySeparator);
8✔
27
      if (joinedPath) return deepFind(mix, joinedPath, keySeparator);
8✔
28
      return undefined;
×
29
    }
30
    current = current[paths[i]];
18✔
31
  }
32
  return current;
3✔
33
}
34

35
class ResourceStore extends EventEmitter {
36
  constructor(data, options = { ns: ['translation'], defaultNS: 'translation' }) {
63✔
37
    super();
63✔
38
    if (utils.isIE10) {
63✔
39
      EventEmitter.call(this); // <=IE10 fix (unable to call parent constructor)
×
40
    }
41

42
    this.data = data || {};
63✔
43
    this.options = options;
63✔
44
    if (this.options.keySeparator === undefined) {
63✔
45
      this.options.keySeparator = '.';
47✔
46
    }
47
    if (this.options.ignoreJSONStructure === undefined) {
63✔
48
      this.options.ignoreJSONStructure = true;
58✔
49
    }
50
  }
51

52
  addNamespaces(ns) {
53
    if (this.options.ns.indexOf(ns) < 0) {
31✔
54
      this.options.ns.push(ns);
2✔
55
    }
56
  }
57

58
  removeNamespaces(ns) {
59
    const index = this.options.ns.indexOf(ns);
2✔
60
    if (index > -1) {
2✔
61
      this.options.ns.splice(index, 1);
2✔
62
    }
63
  }
64

65
  getResource(lng, ns, key, options = {}) {
562✔
66
    const keySeparator =
562✔
67
      options.keySeparator !== undefined ? options.keySeparator : this.options.keySeparator;
68

69
    const ignoreJSONStructure =
562✔
70
      options.ignoreJSONStructure !== undefined
71
        ? options.ignoreJSONStructure
72
        : this.options.ignoreJSONStructure;
73

74
    let path = [lng, ns];
562✔
75
    if (key && typeof key !== 'string') path = path.concat(key);
562✔
76
    if (key && typeof key === 'string')
562✔
77
      path = path.concat(keySeparator ? key.split(keySeparator) : key);
429✔
78

79
    if (lng.indexOf('.') > -1) {
562✔
80
      path = lng.split('.');
5✔
81
    }
82

83
    const result = utils.getPath(this.data, path);
562✔
84
    if (result || !ignoreJSONStructure || typeof key !== 'string') return result;
562✔
85

86
    return deepFind(this.data && this.data[lng] && this.data[lng][ns], key, keySeparator);
177✔
87
  }
88

89
  addResource(lng, ns, key, value, options = { silent: false }) {
17✔
90
    let keySeparator = this.options.keySeparator;
17✔
91
    if (keySeparator === undefined) keySeparator = '.';
17✔
92

93
    let path = [lng, ns];
17✔
94
    if (key) path = path.concat(keySeparator ? key.split(keySeparator) : key);
17✔
95

96
    if (lng.indexOf('.') > -1) {
17✔
97
      path = lng.split('.');
2✔
98
      value = ns;
2✔
99
      ns = path[1];
2✔
100
    }
101

102
    this.addNamespaces(ns);
17✔
103

104
    utils.setPath(this.data, path, value);
17✔
105

106
    if (!options.silent) this.emit('added', lng, ns, key, value);
17✔
107
  }
108

109
  addResources(lng, ns, resources, options = { silent: false }) {
4✔
110
    /* eslint no-restricted-syntax: 0 */
111
    for (const m in resources) {
4✔
112
      if (
7✔
113
        typeof resources[m] === 'string' ||
114
        Object.prototype.toString.apply(resources[m]) === '[object Array]'
115
      )
116
        this.addResource(lng, ns, m, resources[m], { silent: true });
7✔
117
    }
118
    if (!options.silent) this.emit('added', lng, ns, resources);
4✔
119
  }
120

121
  addResourceBundle(lng, ns, resources, deep, overwrite, options = { silent: false }) {
14✔
122
    let path = [lng, ns];
14✔
123
    if (lng.indexOf('.') > -1) {
14✔
124
      path = lng.split('.');
3✔
125
      deep = resources;
3✔
126
      resources = ns;
3✔
127
      ns = path[1];
3✔
128
    }
129

130
    this.addNamespaces(ns);
14✔
131

132
    let pack = utils.getPath(this.data, path) || {};
14✔
133

134
    if (deep) {
14✔
135
      utils.deepExtend(pack, resources, overwrite);
4✔
136
    } else {
137
      pack = { ...pack, ...resources };
10✔
138
    }
139

140
    utils.setPath(this.data, path, pack);
14✔
141

142
    if (!options.silent) this.emit('added', lng, ns, resources);
14✔
143
  }
144

145
  removeResourceBundle(lng, ns) {
146
    if (this.hasResourceBundle(lng, ns)) {
2✔
147
      delete this.data[lng][ns];
2✔
148
    }
149
    this.removeNamespaces(ns);
2✔
150

151
    this.emit('removed', lng, ns);
2✔
152
  }
153

154
  hasResourceBundle(lng, ns) {
155
    return this.getResource(lng, ns) !== undefined;
122✔
156
  }
157

158
  getResourceBundle(lng, ns) {
159
    if (!ns) ns = this.options.defaultNS;
5✔
160

161
    // COMPATIBILITY: remove extend in v2.1.0
162
    if (this.options.compatibilityAPI === 'v1') return { ...{}, ...this.getResource(lng, ns) };
5✔
163

164
    return this.getResource(lng, ns);
5✔
165
  }
166

167
  getDataByLanguage(lng) {
168
    return this.data[lng];
1✔
169
  }
170

171
  toJSON() {
172
    return this.data;
4✔
173
  }
174
}
175

176
export default ResourceStore;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc