@sap/eslint-plugin-cds 2.7.0 → 3.0.3

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.
package/CHANGELOG.md CHANGED
@@ -6,7 +6,43 @@ This project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](http://keepachangelog.com/).
8
8
 
9
- ## [2.7.0] - 2024-04-09
9
+ ## [3.0.3] - 2024-05-08
10
+
11
+ ### Fixed
12
+
13
+ - Disabling ESLint for the next line via `eslint-disable-next-line` now works properly in _cds_ files
14
+
15
+ ## [3.0.2] - 2024-04-29
16
+
17
+ ### Fixed
18
+
19
+ - Internal parser call now handles `ESLint` version 8 and 9
20
+
21
+ ### Changed
22
+
23
+ - Requires `ESLint` version 8 or above
24
+
25
+ ## [3.0.1] - 2024-04-25
26
+
27
+ ### Fixed
28
+
29
+ - Add namespace `@sap/cds` to plugin configuration
30
+
31
+ ## [3.0.0] - 2024-04-23
32
+
33
+ ### Added
34
+
35
+ - Support ESLint flat configurations (`eslint@v9`) and make them available as *recommended*, *all*.
36
+
37
+ ### Changed
38
+
39
+ - Plugin configurations (*recommended*, *all*) for `eslint@<v9` are now available with the `-legacy` suffix.
40
+
41
+ ### Fixed
42
+
43
+ - In _latest-cds-version_, get output from `npm outdated` on exit code 1.
44
+
45
+ ## [2.7.0] - 2024-04-12
10
46
 
11
47
  ### Added
12
48
 
package/lib/conf/index.js CHANGED
@@ -1,22 +1,44 @@
1
+ const path = require('path')
1
2
  const { FILES, GLOBALS, PLUGIN_NAME } = require('../constants')
2
3
  const { parserPath } = require('../api')
3
4
 
4
- function _createConfig (config) {
5
+ function _createConfig (plugin, configName, legacy = false) {
6
+ const config = require(path.join(__dirname, configName))
7
+ if (legacy) {
8
+ return {
9
+ root: true,
10
+ globals: GLOBALS,
11
+ plugins: [PLUGIN_NAME],
12
+ overrides: [
13
+ {
14
+ files: FILES,
15
+ parser: parserPath
16
+ }
17
+ ],
18
+ rules: config
19
+ }
20
+ }
5
21
  return {
6
- root: true,
7
- globals: GLOBALS,
8
- plugins: [PLUGIN_NAME],
9
- overrides: [
10
- {
11
- files: FILES,
12
- parser: parserPath
13
- }
14
- ],
22
+ languageOptions: {
23
+ globals: GLOBALS,
24
+ parser: require(parserPath)
25
+ },
26
+ plugins: {
27
+ '@sap/cds': plugin
28
+ },
29
+ files: FILES.map(file => file.replace('*.', '**/*.')),
15
30
  rules: config
16
31
  }
17
32
  }
18
33
 
19
- module.exports = {
20
- all: _createConfig(require('./all')),
21
- recommended: _createConfig(require('./recommended'))
34
+ // Export both eslintrc and flat configs to ensure backwards compatibility (<eslint@v9):
35
+ // https://eslint.org/docs/latest/extend/plugin-migration-flat-config#backwards-compatibility
36
+ module.exports = function (plugin) {
37
+ return {
38
+ all: _createConfig(plugin, 'all'),
39
+ recommended: _createConfig(plugin, 'recommended'),
40
+ // Legacy configs (for backwards compatibility)
41
+ 'all-legacy': _createConfig(plugin, 'all', true),
42
+ 'recommended-legacy': _createConfig(plugin, 'recommended', true)
43
+ }
22
44
  }
package/lib/index.js CHANGED
@@ -15,15 +15,30 @@
15
15
  * - Expose any 'rules' for use in ESLint
16
16
  */
17
17
 
18
+ const path = require('node:path')
19
+
18
20
  const api = require('./api')
21
+ const getConfigs = require('./conf')
19
22
  const rules = Object.assign(
20
23
  {},
21
24
  ...Object.entries(require('./rules')).map(([k, v]) => ({ [k]: v() }))
22
25
  )
23
- const configs = require('./conf')
24
26
 
25
- module.exports = {
26
- configs,
27
+ const packageJson = require(path.join(__dirname, '../package.json'))
28
+
29
+ const plugin = {
30
+ meta: {
31
+ name: packageJson.name,
32
+ version: packageJson.version
33
+ },
34
+ configs: {},
27
35
  rules,
28
36
  ...api
29
37
  }
38
+
39
+ // Assign configs here so we can reference `plugin`
40
+ Object.assign(plugin.configs, getConfigs(plugin))
41
+
42
+ // Use commonJS entry point to ensure backwards compatibility (<eslint@v9):
43
+ // https://eslint.org/docs/latest/extend/plugin-migration-flat-config#backwards-compatibility
44
+ module.exports = plugin
package/lib/parser.js CHANGED
@@ -28,7 +28,7 @@ module.exports = {
28
28
  const messages = []
29
29
  try {
30
30
  compiledModel = cds.parse(code)
31
- } catch (err) {
31
+ } catch (_err) {
32
32
  // Do nothing
33
33
  }
34
34
  if (compiledModel) {
@@ -57,7 +57,7 @@ module.exports = {
57
57
  if (whereValues && typeof whereValues === 'string') {
58
58
  try {
59
59
  cds.parse.expr(entry.where)
60
- } catch (err) {
60
+ } catch (_err) {
61
61
  context.report({
62
62
  message: 'Invalid `where` expression, CDS compilation failed.',
63
63
  node,
@@ -2,7 +2,6 @@ module.exports = {
2
2
  meta: {
3
3
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
4
4
  docs: {
5
- // eslint-disable-next-line quotes
6
5
  description: "Draft-enabled entities shall not be used in views that make use of `JOIN`.",
7
6
  recommended: true
8
7
  },
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-undef */
1
+
2
2
  module.exports = {
3
3
  meta: {
4
4
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
@@ -62,12 +62,16 @@ module.exports = (spec) => {
62
62
  break
63
63
 
64
64
  case 'inferred':
65
- if (isValidFile && doRootModelChecks) {
65
+ if (isValidFile && (doRootModelChecks)) {
66
+ if (showInEditor) {
67
+ Cache.remove(`model:${Cache.get('rootpath')}`)
68
+ Cache.remove(`rule:${cdscontext.id}:${Cache.get('rootpath')}`)
69
+ Cache.remove(`report:${context.getFilename()}:${context.id}`)
70
+ }
66
71
  if (isTest || showInEditor || Cache.has(`rule:${cdscontext.id}:${Cache.get('rootpath')}`)) {
67
72
  LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
68
73
  if (!showInEditor) {
69
74
  Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
70
- Cache.remove(`report:${context.getFilename()}:${context.id}`)
71
75
  }
72
76
  createReport(node, cdscontext, meta, create)
73
77
  } else {
@@ -113,7 +117,7 @@ function checkEntryCriteria (meta, cdscontext) {
113
117
  const showInEditor = cdscontext.options.includes('show')
114
118
  const hasProjectRoots = Cache.has(`roots:${Cache.get('rootpath')}`)
115
119
  const isValidFile = isConfiguredFileType(cdscontext.getFilename(), 'FILES')
116
- const doRootModelChecks = isTest || (hasProjectRoots && (isRunningWithCDSLint() || isRunningWithESLint() || showInEditor))
120
+ const doRootModelChecks = isTest || (hasProjectRoots && (isRunningWithCDSLint() || isRunningWithESLint()) || showInEditor)
117
121
  // Lint all env rules independent of any parsed file (i.e. 'cds lint' uses the lintText "" API)
118
122
  const doEnvironmentChecks =
119
123
  isTest || (isRunningWithCDSLint() && cdscontext.getFilename() === '<text>')
@@ -162,8 +166,8 @@ function createReport (node, cdscontext, meta, create) {
162
166
  const isValidLocation = (meta.model === 'parsed' && d.$location) ||
163
167
  (meta.model === 'inferred' && d.$location?.file)
164
168
  Object.entries(handlers)
165
- .filter(([type, lazy]) => d.is(type) && isValidLocation)
166
- .forEach(([lazy, handler]) => {
169
+ .filter(([type, _lazy]) => d.is(type) && isValidLocation)
170
+ .forEach(([_lazy, handler]) => {
167
171
  try {
168
172
  handler(d)
169
173
  } catch (err) {
@@ -319,7 +323,7 @@ function getDisabled (code, sourcecode, line) {
319
323
  }
320
324
  })
321
325
  for (const el of listDisabled.filter(
322
- (d) => d.lineComment > line && (d.lineDisabled === 'EOF' || d.lineDisabled === line)
326
+ (d) => d.lineComment < line && (d.lineDisabled === 'EOF' || d.lineDisabled === line)
323
327
  )) {
324
328
  if (el.lineDisabled === 'EOF') {
325
329
  el.lineDisabled = getLastLine(code)
@@ -145,7 +145,7 @@ function genDocFiles (rules, docsPath, release = false) {
145
145
 
146
146
  // Get rule details
147
147
  let mdRules = ''
148
- /* eslint-disable-next-line no-unused-vars */
148
+
149
149
  rules.forEach(rule => {
150
150
  mdRules += `${rule.contents}\n\n${rule.sources}\n\n---\n\n`
151
151
  })
@@ -166,7 +166,7 @@ function getPackageVersion (registry) {
166
166
  stdio: 'pipe'
167
167
  })
168
168
  .toString()
169
- } catch (err) {
169
+ } catch (_err) {
170
170
  LOG?.(`Failed to connect to ${registry} - check your connection and try again.`)
171
171
  exit(0)
172
172
  }
@@ -231,7 +231,7 @@ function getRules (docsPath, rulePath, testPath, versionRequired = '0.0.0', rele
231
231
  }
232
232
  try {
233
233
  mdRule = getRuleExamples(rule, ruleTestPath, testPath, rulesEntry)
234
- } catch (err) {
234
+ } catch (_err) {
235
235
  // Just continue
236
236
  }
237
237
  mdRuleContents = ''
@@ -9,16 +9,23 @@
9
9
  const fs = require('fs')
10
10
  const path = require('path')
11
11
 
12
- module.exports = (currentDir = '.') => {
13
- const configFiles = [
14
- '.eslintrc.js',
15
- '.eslintrc.cjs',
16
- '.eslintrc.yaml',
17
- '.eslintrc.yml',
18
- '.eslintrc.json',
19
- '.eslintrc',
20
- 'package.json'
12
+ module.exports = (currentDir = '.', legacy=false) => {
13
+ let configFiles = [
14
+ 'eslint.config.js',
15
+ 'eslint.config.cjs',
16
+ 'eslint.config.mjs'
21
17
  ]
18
+ if (legacy) {
19
+ configFiles = [
20
+ '.eslintrc.js',
21
+ '.eslintrc.cjs',
22
+ '.eslintrc.yaml',
23
+ '.eslintrc.yml',
24
+ '.eslintrc.json',
25
+ '.eslintrc',
26
+ 'package.json',
27
+ ]
28
+ }
22
29
  let configDir = path.resolve(currentDir)
23
30
  while (configDir !== path.resolve(configDir, '..')) {
24
31
  for (const configFile of configFiles) {
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
- const { RuleTester } = require('eslint')
4
+ const { Linter, RuleTester } = require('eslint')
5
5
  const Cache = require('./Cache')
6
6
  const createRule = require('./createRule')
7
7
  const isConfiguredFileType = require('./isConfiguredFileType')
@@ -17,13 +17,13 @@ const { compileModelFromDict } = require('../parser')
17
17
  * @returns RuleTester results
18
18
  */
19
19
  module.exports = (options) => {
20
- let parser
20
+ let parserPath
21
21
  let rule = {}
22
22
  Cache.set('rules', require(path.join(__dirname, '../rules')))
23
23
  const rulename = path.basename(options.root)
24
24
  if (options.root.startsWith(path.resolve(__dirname, '../..'))) {
25
25
  // For plugin's internal tests, resolve parser from here
26
- parser = require.resolve('../parser')
26
+ parserPath = require.resolve('../parser')
27
27
  const pluginPath = path.join(path.dirname(options.root), '../..')
28
28
  rule = createRule(require(`../rules/${path.basename(options.root)}`))
29
29
  Cache.set('pluginpath', pluginPath)
@@ -32,15 +32,22 @@ module.exports = (options) => {
32
32
  const resolvedPlugin = require.resolve('@sap/eslint-plugin-cds', {
33
33
  paths: [options.root]
34
34
  })
35
- parser = path.join(path.dirname(resolvedPlugin), 'parser')
35
+ parserPath = path.join(path.dirname(resolvedPlugin), 'parser')
36
36
  rule = require(path.join(options.root, `../../rules/${path.basename(options.root)}`))
37
37
  const pluginPath = path.join(path.dirname(options.root), '../../..')
38
38
  Cache.set('pluginpath', pluginPath)
39
39
  }
40
40
  let tester = new RuleTester({})
41
- if (parser) {
42
- tester = new RuleTester({ parser })
41
+ if (parserPath) {
42
+ let options
43
+ if (Number(Linter.version.split('.')[0]) >= 9) {
44
+ options = { languageOptions: { parser: require(parserPath) } }
45
+ } else {
46
+ options = { parser: parserPath }
47
+ }
48
+ tester = new RuleTester(options)
43
49
  }
50
+
44
51
  const testerCases = {};
45
52
  ['valid', 'invalid'].forEach((type) => {
46
53
  const filePath = path.join(options.root, `${type}/${options.filename}`)
@@ -79,7 +86,7 @@ module.exports = (options) => {
79
86
  /**
80
87
  * Creates a model for ESLint unit tests
81
88
  */
82
- function _initModelRuleTester (filePath, flavor) {
89
+ function _initModelRuleTester(filePath, flavor) {
83
90
  Cache.set('test', true)
84
91
  const rootPath = path.dirname(filePath)
85
92
  Cache.set('rootpath', rootPath)
@@ -101,7 +108,7 @@ function _initModelRuleTester (filePath, flavor) {
101
108
  * @param files
102
109
  * @returns dictFiles
103
110
  */
104
- function _getDictFiles (input, files) {
111
+ function _getDictFiles(input, files) {
105
112
  let dictFiles = {}
106
113
  if (Cache.has(`dictfiles:${input}`)) {
107
114
  dictFiles = Cache.get(`dictfiles:${input}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/eslint-plugin-cds",
3
- "version": "2.7.0",
3
+ "version": "3.0.3",
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": [
@@ -23,14 +23,8 @@
23
23
  "@sap/cds": ">=5.6.0",
24
24
  "semver": "^7.3.4"
25
25
  },
26
- "eslintConfig": {
27
- "extends": [
28
- "eslint:recommended",
29
- "standard"
30
- ]
31
- },
32
26
  "peerDependencies": {
33
- "eslint": ">=7"
27
+ "eslint": ">=8"
34
28
  },
35
29
  "engines": {
36
30
  "node": ">=18"