@sap/cds-dk
Version:
Command line client and development toolkit for the SAP Cloud Application Programming Model
159 lines (142 loc) • 5.37 kB
JavaScript
const { write, exists } = require('../cds').utils
const cds = require('../cds')
const { readJSONC } = require('../util/fs')
const path = require('node:path')
const term = require('../util/term')
const { pathToFileURL } = require('node:url')
const { COMMAND_INIT } = require('../init/constants')
const installationHint = (logger, configFile, importStatement) => logger.log(
`${term.warn(
`\n\nPlease add the following to your '${configFile}':` +
'\n\n 1. At the top of your file, import the following:')}` +
`\n\n ${term.bold(importStatement)}` +
`${term.warn(
'\n\n 2. In your export, add the following to the front of the array:')}` +
`\n\n ${term.bold('cdsPlugin.configs.recommended')}`
)
module.exports = {
/**
* Reads ESLint config contents from file
* @param {import('fs').PathLike} configPath config file path to read from
* @returns {Promise<object>} config file contents
*/
async readEslintConfig(configPath) {
let config
switch (path.basename(configPath)) {
case 'eslint.config.mjs':
// pathToFileURL is used to convert the path to a file URL on Windows
try { config = (await import (pathToFileURL(configPath))).default } catch { config = null }
break
case 'eslint.config.js':
case 'eslint.config.cjs':
try { config = require (configPath) } catch { config = null }
}
return config
},
/**
* Checks ESLint config file contents for locally installed
* plugin with:
* (1) All CDS recommended rules 'on'
* (2) Add's custom rule example 'no-entity-moo' if requested
* @param {object | string} configIn - ESLint config file or path
* @param {import('console').Console} logger - logger to use
* @returns {Promise<object>} ESLint config contents (also writes to file if path provided)
*/
async sanitizeEslintConfig(configIn, logger = console) {
let configContents
const isFilePath = typeof configIn === 'string'
if (isFilePath) {
configContents = await this.readEslintConfig(configIn)
}
configContents ??= { ...configIn }
if (isFilePath) {
await this.writeEslintConfig(configIn, cds.cli?.command === COMMAND_INIT, logger)
}
return configContents
},
/**
* Suggest ESLint config contents to file
* @param {import('node:fs').PathLike} configPath config file path to write to
* @param {boolean} force whether to overwrite existing config file or not
* @param {import('console').Console} logger logger to use for output
* @returns {Promise<void>}
*/
async writeEslintConfig(configPath, force = false, logger = console) {
let configFile = path.basename(configPath)
switch (configFile) {
case 'eslint.config.js':
case 'eslint.config.cjs':
if (!force && exists(configPath)) {
installationHint(logger, configFile, 'const cdsPlugin = require(\'@sap/eslint-plugin-cds\')')
} else {
await write(configPath, `
const cds = require('@sap/eslint-plugin-cds')
module.exports = [...cds.recommended, cdsPlugin.configs.recommended]
`)
}
logger.log(term.warn('\nMake sure to also run npm i to install the newly added dependencies.'))
break
case 'eslint.config.mjs':
if (!force && exists(configPath)) {
installationHint(logger, configFile, 'import cdsPlugin from \'@sap/eslint-plugin-cds\'')
} else {
await write(configPath, `
import cds from '@sap/cds/eslint.config.mjs'
import cdsPlugin from '@sap/eslint-plugin-cds'
export default [...cds.recommended, cdsPlugin.configs.recommended]
`)
}
logger.log(term.warn('\nMake sure to also run npm i to install the newly added dependencies.'))
break
}
},
/**
* Read VS Code settings from file
* @param {*} settingsPath settings file path to read from
* @returns settings file contents
*/
async readVscodeSettings(settingsPath) {
let settings
if (exists(settingsPath)) {
try {
settings = await readJSONC(settingsPath)
} catch {
settings = {}
}
}
return settings
},
/**
* Merges two arrays into one
* @param {*} array input array
* @param {*} arrayToMerge array to merge with
* @returns merged array (without duplicates)
*/
mergeArrays(array, arrayToMerge) {
let arrayMerged = array.concat(arrayToMerge)
if (typeof array === 'string') {
array = [array]
}
if (typeof arrayToMerge === 'string') {
arrayToMerge = [arrayToMerge]
}
arrayMerged = [...new Set([...array, ...arrayToMerge])]
return arrayMerged
},
/**
* Add VS Code ESLint extension settings required for CDS linting:
* (1) Extension file types: [cds, csn]
* (2) 'Custom' case adds rulePaths and removes configFile
* (3) 'Global' case adds configFile and removes rulePaths
* @param {*} settingsPath VS Code settings file
* @param {*} lintFileTypes array of file types to add to eslint.validate setting
* @returns void
*/
async sanitizeVscodeSettings(settingsPath, lintFileTypes) {
const settings = (await this.readVscodeSettings(settingsPath)) ?? {}
settings['eslint.validate'] = settings['eslint.validate']
? this.mergeArrays(settings['eslint.validate'], lintFileTypes)
: lintFileTypes
await write(settingsPath, settings, { spaces: 2 })
}
}