@sap/eslint-plugin-cds 2.3.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/lib/api/index.js +9 -8
  3. package/lib/conf/all.js +21 -0
  4. package/lib/conf/index.js +22 -0
  5. package/lib/conf/recommended.js +18 -0
  6. package/lib/constants.js +10 -8
  7. package/lib/index.js +11 -32
  8. package/lib/parser.js +158 -7
  9. package/lib/rules/assoc2many-ambiguous-key.js +21 -38
  10. package/lib/rules/auth-no-empty-restrictions.js +37 -0
  11. package/lib/rules/auth-use-requires.js +38 -0
  12. package/lib/rules/auth-valid-restrict-grant.js +105 -0
  13. package/lib/rules/auth-valid-restrict-keys.js +43 -0
  14. package/lib/rules/auth-valid-restrict-to.js +130 -0
  15. package/lib/rules/auth-valid-restrict-where.js +89 -0
  16. package/lib/rules/index.js +26 -0
  17. package/lib/rules/latest-cds-version.js +8 -7
  18. package/lib/rules/min-node-version.js +10 -9
  19. package/lib/rules/no-db-keywords.js +16 -17
  20. package/lib/rules/no-dollar-prefixed-names.js +7 -33
  21. package/lib/rules/no-join-on-draft-enabled-entities.js +9 -24
  22. package/lib/rules/require-2many-oncond.js +9 -14
  23. package/lib/rules/sql-cast-suggestion.js +6 -20
  24. package/lib/rules/start-elements-lowercase.js +7 -10
  25. package/lib/rules/start-entities-uppercase.js +6 -9
  26. package/lib/rules/valid-csv-header.js +66 -66
  27. package/lib/{api/lint.d.ts → types.d.ts} +4 -7
  28. package/lib/utils/Cache.js +33 -0
  29. package/lib/utils/Colors.js +9 -0
  30. package/lib/utils/createRule.js +304 -0
  31. package/lib/utils/createRuleDocs.js +361 -0
  32. package/lib/utils/{fuzzySearch.js → findFuzzy.js} +9 -4
  33. package/lib/utils/genDocs.js +363 -0
  34. package/lib/utils/getConfigPath.js +33 -0
  35. package/lib/utils/getConfiguredFileTypes.js +10 -0
  36. package/lib/utils/getFileExtensions.js +8 -0
  37. package/lib/utils/isConfiguredFileType.js +20 -0
  38. package/lib/utils/jsonc.js +1 -1
  39. package/lib/utils/jsoncParser.js +1 -0
  40. package/lib/utils/rules.js +85 -945
  41. package/lib/utils/runRuleTester.js +111 -0
  42. package/package.json +8 -5
  43. package/lib/processor.js +0 -47
  44. package/lib/utils/helpers.js +0 -47
  45. package/lib/utils/model.js +0 -387
  46. package/lib/utils/ruleHelpers.js +0 -56
  47. package/lib/utils/ruleTester.js +0 -79
  48. package/lib/utils/validate.js +0 -36
@@ -0,0 +1,111 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const { RuleTester } = require("eslint");
5
+ const Cache = require("./Cache");
6
+ const createRule = require("./createRule");
7
+ const isConfiguredFileType = require("./isConfiguredFileType");
8
+ const { compileModelFromDict } = require("../parser");
9
+
10
+ /**
11
+ * ESLint RuleTester (used by custom rule creator api)
12
+ * Calls ESLint's RuleTester with custom cds parser and input for
13
+ * valid/invalid checks:
14
+ * Model checks require input 'code' entries
15
+ * Env checks require input 'options' with selected parameters
16
+ * @param {CDSRuleTestOpts} options RuleTester input options
17
+ * @returns RuleTester results
18
+ */
19
+ module.exports = (options) => {
20
+ let parser;
21
+ let rule = {};
22
+ Cache.set("rules", require(path.join(__dirname, "../rules")));
23
+ const rulename = path.basename(options.root);
24
+ if (options.root.startsWith(path.resolve(__dirname, "../.."))) {
25
+ // For plugin's internal tests, resolve parser from here
26
+ parser = require.resolve("../parser");
27
+ const pluginPath = path.join(path.dirname(options.root), "../..");
28
+ rule = createRule(require(`../rules/${path.basename(options.root)}`));
29
+ Cache.set("pluginpath", pluginPath);
30
+ } else {
31
+ // Otherwise from project root
32
+ const resolvedPlugin = require.resolve("@sap/eslint-plugin-cds", {
33
+ paths: [options.root],
34
+ });
35
+ parser = path.join(path.dirname(resolvedPlugin), "parser");
36
+ rule = require(path.join(options.root, `../rules/${path.basename(options.root)}`));
37
+ const pluginPath = path.join(path.dirname(options.root), "../../..");
38
+ Cache.set("pluginpath", pluginPath);
39
+ }
40
+ let tester = new RuleTester({});
41
+ if (parser) {
42
+ tester = new RuleTester({ parser });
43
+ }
44
+ const testerCases = {};
45
+ ["valid", "invalid"].forEach((type) => {
46
+ const filePath = path.join(options.root, `${type}/${options.filename}`);
47
+ Cache.set("rootpath", path.dirname(filePath));
48
+ _initModelRuleTester(filePath);
49
+ testerCases[type] = [
50
+ {
51
+ filename: filePath,
52
+ },
53
+ ];
54
+ if (!isConfiguredFileType(options.filename, "FILES")) {
55
+ const fileContents = JSON.parse(fs.readFileSync(filePath, "utf8"));
56
+ testerCases[type][0].code = "";
57
+ testerCases[type][0].filename = "<text>";
58
+ testerCases[type][0].options = [{ environment: fileContents }];
59
+ } else {
60
+ testerCases[type][0].code = fs.readFileSync(filePath, "utf8");
61
+ if (options.options) {
62
+ testerCases[type][0].options = options.options;
63
+ }
64
+ }
65
+ if (type === "invalid") {
66
+ testerCases[type][0].errors = options.errors;
67
+ const fileFixed = path.join(options.root, `fixed/${options.filename}`);
68
+ if (fs.existsSync(fileFixed) && rule.meta.type !== "suggestion") {
69
+ testerCases[type][0].output = fs.readFileSync(fileFixed, "utf8");
70
+ }
71
+ }
72
+ });
73
+ return tester.run(rulename, rule, testerCases);
74
+ }
75
+
76
+ /**
77
+ * Creates a model for ESLint unit tests
78
+ */
79
+ function _initModelRuleTester(filePath) {
80
+ Cache.set("test", true);
81
+ const rootPath = path.dirname(filePath);
82
+ Cache.set("rootpath", rootPath);
83
+ let files = fs.readdirSync(rootPath);
84
+ const modelfiles = files.map((f) => path.join(rootPath, f)).filter((fp) => isConfiguredFileType(fp, "MODEL_FILES"));
85
+ Cache.set(`modelfiles:${rootPath}`, modelfiles);
86
+ const dictFiles = _getDictFiles(rootPath, modelfiles);
87
+ Cache.set(`dictfiles:${rootPath}`, dictFiles);
88
+ const reflectedModel = compileModelFromDict(dictFiles);
89
+ Cache.set(`model:${rootPath}`, reflectedModel);
90
+ }
91
+
92
+ /**
93
+ * Creates or updates a dictionary of files/file contents for a given
94
+ * project path.
95
+ * @param input
96
+ * @param files
97
+ * @returns dictFiles
98
+ */
99
+ function _getDictFiles(input, files) {
100
+ let dictFiles = {};
101
+ if (Cache.has(`dictfiles:${input}`)) {
102
+ dictFiles = Cache.get(`dictfiles:${input}`);
103
+ } else {
104
+ files.forEach((file) => {
105
+ dictFiles[file] = Cache.has(`file:${file}`)
106
+ ? Cache.get(`file:${file}`)
107
+ : fs.readFileSync(file, "utf8")
108
+ });
109
+ }
110
+ return dictFiles;
111
+ }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@sap/eslint-plugin-cds",
3
- "version": "2.3.4",
3
+ "version": "2.5.0",
4
4
  "description": "ESLint plugin including recommended SAP Cloud Application Programming model and environment rules",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
7
7
  "eslint",
8
- "eslintplugin",
9
- "typescript",
8
+ "eslint-plugin",
10
9
  "cds",
11
- "cds-lint"
10
+ "cds-lint",
11
+ "cds-lint-plugin"
12
12
  ],
13
13
  "author": "SAP SE (https://www.sap.com)",
14
14
  "license": "See LICENSE file",
@@ -20,10 +20,13 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@sap/cds": "^5.6.0",
23
+ "@sap/cds": ">=5.6.0",
24
24
  "semver": "^7.3.4"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "eslint": ">=7"
28
+ },
29
+ "engines": {
30
+ "node": ">=14"
28
31
  }
29
32
  }
package/lib/processor.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * Custom ESLint processor:
3
- * https://eslint.org/docs/developer-guide/working-with-plugins#processors-in-plugins
4
- * This processor is used to avoid parsing errors when this plugin is extended
5
- * in ESLint alongside other plugins, such as prettier which then also try to
6
- * read the new file types exposed via globs.
7
- *
8
- * Note, that because we cache the file contents and return files contents,
9
- * the plugin's parser is bypassed so we must retrieve the file contents (see createRule()
10
- * in utils/rules.js).
11
- */
12
-
13
- const { Cache } = require("./utils/model");
14
- const { isValidFile } = require("./utils/helpers");
15
-
16
- module.exports = {
17
- preprocess: function (text, filename) {
18
- if (isValidFile(filename, 'FILES')) {
19
- Cache.set(`file:${filename}`, text);
20
- }
21
- return [{ text: "", filename }];
22
- },
23
- /**
24
- * Returns message objects as defined by ESLint:
25
- * https://eslint.org/docs/developer-guide/working-with-custom-formatters#the-message-object
26
- * @param {*} messages
27
- * @returns
28
- */
29
- postprocess: function (messages) {
30
- const messagesSanitized = [];
31
- messages.forEach(fileMessages => {
32
- const fileMessagesSanitized = [];
33
- fileMessages.forEach(r => {
34
- if (r.message.startsWith(`CompilationError:`)) {
35
- r.message = r.message.replace(`CompilationError: `,
36
- 'CDS model could not be compiled!\n');
37
- r.ruleId = `❗${r.ruleId}`;
38
- r.severity = 2;
39
- }
40
- fileMessagesSanitized.push(r);
41
- })
42
- messagesSanitized.push(fileMessagesSanitized);
43
- })
44
- return [].concat(...messagesSanitized);
45
- },
46
- supportsAutofix: true,
47
- };
@@ -1,47 +0,0 @@
1
- const { FILES, MODEL_FILES } = require("../constants");
2
-
3
- module.exports = {
4
- /**
5
- * Checks whether the given filePath matches a regex `files`
6
- * @param {string} filePath
7
- * @returns boolean
8
- */
9
- isValidFile: function (filePath, fileType) {
10
- const genRegex = (key) => new RegExp(`${key.map((file) => file.replace("*", "")).join("$|")}$`);
11
- let isValid = false;
12
- switch(fileType) {
13
- case 'MODEL_FILES':
14
- isValid = genRegex(MODEL_FILES).test(filePath);
15
- break;
16
- case 'FILES':
17
- isValid = genRegex(FILES).test(filePath);
18
- break;
19
- }
20
- return isValid;
21
- },
22
- /**
23
- * Checks whether the plugin is run via VS Code ESLint extension
24
- * @returns boolean
25
- */
26
- isVSCodeEditor() {
27
- return process.argv.join(" ").includes("dbaeumer.vscode-eslint");
28
- },
29
-
30
- /**
31
- * Checks whether ESLint is running in debug mode
32
- * @returns boolean
33
- */
34
- hasDebugFlag() {
35
- return process.argv.includes("--debug");
36
- },
37
-
38
- /**
39
- * Returns an array of allowed file extensions
40
- * the plugin can parse of the form "*.ext"
41
- * @returns {ConfigOverrideFiles} Array of file extensions
42
- */
43
- getFileExtensions: function() {
44
- return FILES;
45
- },
46
-
47
- };
@@ -1,387 +0,0 @@
1
- /**
2
- * @typedef { import("eslint").AST.SourceLocation } SourceLocation
3
- */
4
-
5
- const fs = require("fs");
6
- const path = require("path");
7
- const cds = require("@sap/cds");
8
- const { SourceCode } = require("eslint");
9
- const { isValidFile } = require("./helpers");
10
-
11
- const cache = new Map();
12
-
13
- module.exports = {
14
- /**
15
- * Simple cache to store model and any cds calls made in the rule creation
16
- * api to modify the model
17
- */
18
- Cache: {
19
- has(key) {
20
- return cache.has(key);
21
- },
22
- set(key, value) {
23
- return cache.set(key, [value, Date.now()]);
24
- },
25
- get(key) {
26
- return cache.get(key) ? cache.get(key)[0] : undefined
27
- },
28
- dump() {
29
- const dump = {};
30
- for (const [key, value] of cache.entries()) {
31
- const timestamp = new Date(value[1]);
32
- dump[key] = { key, value: JSON.stringify(value[0]), timestamp };
33
- }
34
- return dump;
35
- },
36
- remove(key) {
37
- if (cache.has(key)) {
38
- cache.delete(key);
39
- }
40
- },
41
- clear() {
42
- cache.clear();
43
- },
44
- },
45
-
46
- /**
47
- * Checks whether the path where the nearest ESLint configuration
48
- * file has changed
49
- * @param {*} configPath
50
- * @returns boolean
51
- */
52
- isNewConfigPath: function (configPath) {
53
- return !module.exports.Cache.has("configpath")
54
- && (configPath !== module.exports.Cache.get("configpath"))
55
- },
56
-
57
- /**
58
- * Gets directory of the nearest ESLint config files associated
59
- * Within this plugin, this is equivalent to the cds project's directory
60
- * @param filePath
61
- * @returns Directory of ESLint config file
62
- */
63
- loadConfigPath: function (filePath) {
64
- let configPath = path.dirname(module.exports.getConfigPath(filePath));
65
- if (configPath) {
66
- module.exports.Cache.set("configpath", configPath);
67
- } else {
68
- throw new Error("Failed to find an ESLint configuration file!");
69
- }
70
- return configPath;
71
- },
72
-
73
- /**
74
- * Generates dummy AST with just single Program node
75
- * @param code Parse file contents
76
- * @returns AST
77
- */
78
- getAST: function (code) {
79
- return {
80
- type: "Program",
81
- body: [],
82
- sourceType: "module",
83
- tokens: [],
84
- comments: [],
85
- range: [0, code.length],
86
- loc: {
87
- start: {
88
- line: 1,
89
- column: 0,
90
- },
91
- end: {
92
- line: 1,
93
- column: 0,
94
- },
95
- },
96
- };
97
- },
98
-
99
- /**
100
- * Converts code with {line, column} to ESLint's 'range' property:
101
- * https://eslint.org/docs/developer-guide/working-with-custom-parsers#all-nodes
102
- * code.slice(node.range[0], node.range[1]) must be the text of the node!
103
- * @param code source code
104
- * @param line line number
105
- * @param column column number
106
- * @returns ESLint range
107
- */
108
- getRange: function (code, line, column) {
109
- const lines = typeof code === "string" ? SourceCode.splitLines(code) : code
110
- const ranges = [0];
111
- lines.forEach((line, i) => {
112
- ranges[i + 1] = i === 0 ? line.length + 1 : ranges[i] + line.length + 1
113
- });
114
- return line > 1 ? ranges[line - 1] + column : column
115
- },
116
-
117
- /**
118
- * Uses ESLint's static function splitLines() to split the source code text
119
- * into an array of lines:
120
- * https://eslint.org/docs/developer-guide/nodejs-api#sourcecodesplitlines
121
- * Returns the index of the last line
122
- * @param code
123
- * @returns Last line index
124
- */
125
- getLastLine: function (code) {
126
- const lines = typeof code === "string" ? SourceCode.splitLines(code) : code
127
- return lines.length - 1;
128
- },
129
-
130
- /**
131
- * Generates ESlint's 'loc' from artifact string and cds $location property:
132
- * https://eslint.org/docs/developer-guide/working-with-rules-deprecated#contextreport
133
- * @param name
134
- * @param {SoureLocation} obj
135
- * @returns ESLint's 'loc' object
136
- */
137
- getLocation: function (name, obj, model) {
138
- let loc;
139
- const defaultLoc = {
140
- start: { line: 0, column: 0 },
141
- end: { line: 1, column: 0 },
142
- };
143
- if (obj.$location) {
144
- const nameloc = obj.$location;
145
- if (nameloc) {
146
- // CSN entry with column 0 is equivalent to 'undefined'
147
- // It means that the column in that line cannot be determined,
148
- // so we assign a value 1 to get a column location of 0
149
- if (nameloc.col === 0) {
150
- nameloc.col = 1;
151
- }
152
- loc = defaultLoc;
153
- loc.start.column = nameloc.col - 1;
154
- loc.start.line = nameloc.line;
155
- loc.end.column = nameloc.col - 1 + name.length;
156
- loc.end.line = nameloc.line;
157
- } else if (obj.parent) {
158
- this.getLocation(name, obj.parent, model);
159
- }
160
- }
161
- // Empty locations default to line 0, column 0
162
- if (!loc) {
163
- loc = defaultLoc;
164
- }
165
- return loc;
166
- },
167
-
168
- /**
169
- * Searches for ESLint config file types (in order or precedence)
170
- * and returns corresponding directory (usually project's root dir)
171
- * https://eslint.org/docs/user-guide/configuring#configuration-file-formats
172
- * @param {string} currentDir start here and search until root dir
173
- * @returns {string} dir containing ESLint config file (empty if not exists)
174
- */
175
- getConfigPath: function (currentDir = ".") {
176
- const configFiles = [
177
- ".eslintrc.js",
178
- ".eslintrc.cjs",
179
- ".eslintrc.yaml",
180
- ".eslintrc.yml",
181
- ".eslintrc.json",
182
- ".eslintrc",
183
- "package.json",
184
- ];
185
- let configDir = path.resolve(currentDir);
186
- while (configDir !== path.resolve(configDir, "..")) {
187
- for (let i = 0; i < configFiles.length; i++) {
188
- const configPath = path.join(configDir, configFiles[i]);
189
- if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
190
- return configPath;
191
- }
192
- }
193
- configDir = path.join(configDir, "..");
194
- }
195
- return "";
196
- },
197
-
198
- /**
199
- * Compiles reflected model for a given project directory
200
- * Note, that to support monorepos, the cache (in @sap/cds) must be cleared
201
- * to also change the roots with every changed configPath.
202
- * @param configPath
203
- * @returns reflected model
204
- */
205
- compileModelFromPath: function (configPath) {
206
- let reflectedModel;
207
- cds.resolve.cache = {};
208
- const roots = cds.resolve("*", { root: configPath });
209
- const messages = [];
210
- if (roots) {
211
- const compiledModel = cds.load(roots, {
212
- cwd: configPath,
213
- sync: true,
214
- locations: true,
215
- messages,
216
- });
217
- if (compiledModel) {
218
- reflectedModel = cds.linked(compiledModel);
219
- if (messages) {
220
- reflectedModel.messages = messages;
221
- }
222
- }
223
- }
224
- return reflectedModel;
225
- },
226
-
227
- /**
228
- * Compiles reflected model for a dictionary of files/file contents
229
- * Note, that this method is used to account for editor type events
230
- * and hence, model updates.
231
- * WARNING: Only use if cds roots are defined prior to this step
232
- * and the dictionary is complete (the compiler will not resolve
233
- * any missing files)!
234
- * @param dictFiles
235
- * @returns reflected model
236
- */
237
- compileModelFromDict: function (dictFiles, options) {
238
- let reflectedModel;
239
- const messages = [];
240
- const compiledModel = cds.compile(dictFiles, {
241
- sync: true,
242
- locations: true,
243
- messages,
244
- ...options,
245
- });
246
- if (compiledModel) {
247
- reflectedModel = cds.linked(compiledModel);
248
- if (messages) {
249
- reflectedModel.messages = messages;
250
- }
251
- }
252
- return reflectedModel;
253
- },
254
-
255
- compileModelFromFile: function (code, filePath) {
256
- let compiledModel;
257
- let reflectedModel;
258
- const dictFiles = {};
259
- dictFiles[filePath] = code;
260
- let flavor = "inferred";
261
- try {
262
- compiledModel = module.exports.compileModelFromDict(dictFiles, {
263
- flavor,
264
- });
265
- } catch (err) {
266
- // Supress errors from parked files
267
- }
268
- if (compiledModel) {
269
- reflectedModel = cds.linked(compiledModel);
270
- }
271
- return reflectedModel;
272
- },
273
-
274
- /**
275
- * Initiates and stores new reflected model, it's corresponding project path,
276
- * as well as a list and dictionary of files comprising the model.
277
- * @param configPath
278
- * @param filePath
279
- */
280
- initRootModel: function (configPath) {
281
- module.exports.Cache.set("configpath", configPath);
282
- const reflectedModel = module.exports.compileModelFromPath(configPath);
283
- module.exports.Cache.set(`model:${configPath}`, reflectedModel);
284
- let files;
285
- if (reflectedModel && reflectedModel.$sources) {
286
- files = reflectedModel.$sources;
287
- if (files) {
288
- module.exports.Cache.set(`modelfiles:${configPath}`, files);
289
- } else {
290
- files = [];
291
- }
292
- const dictFiles = module.exports.getDictFiles(configPath, files);
293
- module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
294
- }
295
- return reflectedModel;
296
- },
297
-
298
- /**
299
- * Creates a model for ESLint unit tests
300
- */
301
- initModelRuleTester: function (filePath) {
302
- module.exports.Cache.set("test", true);
303
- const configPath = path.dirname(filePath);
304
- module.exports.Cache.set('configpath', configPath);
305
- let files = fs.readdirSync(configPath);
306
- const modelfiles = files.map(f => path.join(configPath, f))
307
- .filter(fp => isValidFile(fp, 'MODEL_FILES'))
308
- module.exports.Cache.set(`modelfiles:${configPath}`, modelfiles);
309
- const dictFiles = module.exports.getDictFiles(configPath, modelfiles);
310
- module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
311
- const reflectedModel = module.exports.compileModelFromDict(dictFiles);
312
- module.exports.Cache.set(`model:${configPath}`, reflectedModel);
313
- },
314
-
315
- /**
316
- * Creates or updates a dictionary of files/file contents for a given
317
- * project path.
318
- * @param configPath
319
- * @param files
320
- * @returns dictFiles
321
- */
322
- getDictFiles: function (input, files = []) {
323
- let dictFiles = {};
324
- if (module.exports.Cache.has(`dictfiles:${input}`)) {
325
- dictFiles = module.exports.Cache.get(`dictfiles:${input}`);
326
- } else {
327
- files.forEach((file) => {
328
- dictFiles[file] = module.exports.Cache.has(`file:${file}`)
329
- ? module.exports.Cache.get(`file:${file}`)
330
- : fs.readFileSync(file, "utf8")
331
- });
332
- }
333
- return dictFiles;
334
- },
335
-
336
- /**
337
- * Determines whether an incoming file has changed contents
338
- * @param context cds context object
339
- * @returns boolean
340
- */
341
- hasFileChanged: function (code, filePath, configPath) {
342
- let result = false
343
- const files = module.exports.Cache.get(`modelfiles:${configPath}`);
344
- const dictFiles = module.exports.getDictFiles(configPath, files);
345
- const isFileInModel = module.exports.isFileInModel(filePath, configPath);
346
- // If incoming file is a 'model' file
347
- if (isFileInModel) {
348
- // Only update on detected changes
349
- if (dictFiles[filePath] !== code) {
350
- dictFiles[filePath] = code;
351
- module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
352
- result = true
353
- }
354
- } else if (dictFiles[filePath] !== code) {
355
- result = true
356
- }
357
- return result;
358
- },
359
-
360
- /**
361
- * Checks whether a file is part of the model for a given project
362
- * @param context
363
- * @param files
364
- * @returns boolean
365
- */
366
- isFileInModel(filePath, configPath) {
367
- let files = module.exports.Cache.get(`modelfiles:${configPath}`) || [];
368
- return files && files.length > 0 && files.includes(filePath)
369
- },
370
-
371
- /**
372
- * Updates and stores reflected model on file changes. Model compilation
373
- * us handled separately for 'model' files (part of model) and 'outsider'
374
- * files.
375
- * @param context cds context object
376
- */
377
- updateModel: function (code, filePath, configPath) {
378
- let reflectedModel;
379
- const dictFiles = module.exports.Cache.get(`dictfiles:${configPath}`);
380
- dictFiles[filePath] = code;
381
- module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
382
- reflectedModel = module.exports.compileModelFromDict(dictFiles, { flavor: "inferred" });
383
- module.exports.Cache.set(`model:${configPath}`, reflectedModel);
384
- return reflectedModel;
385
- }
386
-
387
- };
@@ -1,56 +0,0 @@
1
- const SEP = "[,;\t]";
2
- const EOL = "\\r?\\n";
3
-
4
- const findFuzzy = require("./fuzzySearch");
5
-
6
- module.exports = {
7
- /**
8
- *
9
- * @param {*} e
10
- */
11
- splitEntityName: function (e) {
12
- // Entity names from CSN are of the form:
13
- // <namespace>.<service>.<entity>.<'texts'|'localized'>|<composition value>
14
- let prefix = "";
15
- let suffix = "";
16
- let entityName = e.name;
17
- const names = entityName.split(".");
18
- entityName = names[names.length - 1];
19
-
20
- if (entityName) {
21
- // Managed composition get compiler tag `_up`
22
- let isManagedComposition = false;
23
- if (e.elements) {
24
- isManagedComposition = Object.keys(e.elements).some((k) => k === "up_");
25
- }
26
- // Check for compiler tags
27
- let compilerTagsToExclude = ["texts", "localized"];
28
- const isCompilerTag = compilerTagsToExclude.includes(entityName);
29
-
30
- if (isManagedComposition || isCompilerTag) {
31
- suffix = names[names.length - 1];
32
- entityName = names[names.length - 2];
33
- }
34
- prefix = e.name.split(`.${entityName}`)[0];
35
- }
36
- return { prefix, entity: entityName, suffix };
37
- },
38
-
39
-
40
-
41
- _findInCode: function (miss, code) {
42
- // middle
43
- let match = new RegExp(SEP + miss + SEP).exec(code);
44
- if (match) return match.index + 1;
45
- // end of line
46
- match = new RegExp(SEP + miss + EOL).exec(code);
47
- if (match) return match.index + 1;
48
- // start of doc
49
- match = new RegExp("^" + miss + SEP).exec(code);
50
- if (match) return match.index;
51
- // somewhere (fallback)
52
- return code.indexOf(miss);
53
- },
54
-
55
-
56
- };