update "invalid helper key" error conditions and messages
parent
3de1b980ad
commit
361e878539
|
@ -4591,6 +4591,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"js-levenshtein": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
|
|
@ -173,6 +173,7 @@
|
||||||
"globrex": "^0.1.2",
|
"globrex": "^0.1.2",
|
||||||
"import-from": "^3.0.0",
|
"import-from": "^3.0.0",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
|
"js-levenshtein": "^1.1.6",
|
||||||
"line-column": "^1.0.2",
|
"line-column": "^1.0.2",
|
||||||
"mitt": "^1.2.0",
|
"mitt": "^1.2.0",
|
||||||
"mkdirp": "^1.0.3",
|
"mkdirp": "^1.0.3",
|
||||||
|
|
|
@ -15,12 +15,15 @@ import {
|
||||||
} from '../util/find'
|
} from '../util/find'
|
||||||
import { getClassNameMeta } from '../util/getClassNameMeta'
|
import { getClassNameMeta } from '../util/getClassNameMeta'
|
||||||
import { getClassNameDecls } from '../util/getClassNameDecls'
|
import { getClassNameDecls } from '../util/getClassNameDecls'
|
||||||
import { equal, flatten } from '../../util/array'
|
import { equal } from '../../util/array'
|
||||||
import { getDocumentSettings } from '../util/getDocumentSettings'
|
import { getDocumentSettings } from '../util/getDocumentSettings'
|
||||||
const dlv = require('dlv')
|
const dlv = require('dlv')
|
||||||
import semver from 'semver'
|
import semver from 'semver'
|
||||||
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
|
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
|
||||||
import { absoluteRange } from '../util/absoluteRange'
|
import { absoluteRange } from '../util/absoluteRange'
|
||||||
|
import { isObject } from '../../class-names/isObject'
|
||||||
|
import levenshtein from 'js-levenshtein'
|
||||||
|
import { stringToPath } from '../util/stringToPath'
|
||||||
|
|
||||||
function getUnsupportedApplyDiagnostics(
|
function getUnsupportedApplyDiagnostics(
|
||||||
state: State,
|
state: State,
|
||||||
|
@ -276,16 +279,84 @@ function getInvalidHelperKeyDiagnostics(
|
||||||
|
|
||||||
matches.forEach((match) => {
|
matches.forEach((match) => {
|
||||||
let base = match.groups.helper === 'theme' ? ['theme'] : []
|
let base = match.groups.helper === 'theme' ? ['theme'] : []
|
||||||
let keys = match.groups.key.split(/[.\[\]]/).filter(Boolean)
|
let keys = stringToPath(match.groups.key)
|
||||||
let value = dlv(state.config, [...base, ...keys])
|
let value = dlv(state.config, [...base, ...keys])
|
||||||
|
|
||||||
if (
|
const isValid = (val: unknown): boolean =>
|
||||||
typeof value === 'string' ||
|
typeof val === 'string' ||
|
||||||
typeof value === 'number' ||
|
typeof val === 'number' ||
|
||||||
value instanceof String ||
|
val instanceof String ||
|
||||||
value instanceof Number ||
|
val instanceof Number ||
|
||||||
Array.isArray(value)
|
Array.isArray(val)
|
||||||
) {
|
|
||||||
|
const stitch = (keys: string[]): string =>
|
||||||
|
keys.reduce((acc, cur, i) => {
|
||||||
|
if (i === 0) return cur
|
||||||
|
if (cur.includes('.')) return `${acc}[${cur}]`
|
||||||
|
return `${acc}.${cur}`
|
||||||
|
}, '')
|
||||||
|
|
||||||
|
let message: string
|
||||||
|
|
||||||
|
if (isValid(value)) {
|
||||||
|
// The value resolves successfully, but we need to check that there
|
||||||
|
// wasn't any funny business. If you have a theme object:
|
||||||
|
// { msg: 'hello' } and do theme('msg.0')
|
||||||
|
// this will resolve to 'h', which is probably not intentional, so we
|
||||||
|
// check that all of the keys are object or array keys (i.e. not string
|
||||||
|
// indexes)
|
||||||
|
let valid = true
|
||||||
|
for (let i = keys.length - 1; i >= 0; i--) {
|
||||||
|
let key = keys[i]
|
||||||
|
let parentValue = dlv(state.config, [...base, ...keys.slice(0, i)])
|
||||||
|
if (/^[0-9]+$/.test(key)) {
|
||||||
|
if (!isObject(parentValue) && !Array.isArray(parentValue)) {
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if (!isObject(parentValue)) {
|
||||||
|
valid = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
message = `'${match.groups.key}' does not exist in your theme config.`
|
||||||
|
}
|
||||||
|
} else if (typeof value === 'undefined') {
|
||||||
|
message = `'${match.groups.key}' does not exist in your theme config.`
|
||||||
|
let parentValue = dlv(state.config, [
|
||||||
|
...base,
|
||||||
|
...keys.slice(0, keys.length - 1),
|
||||||
|
])
|
||||||
|
if (isObject(parentValue)) {
|
||||||
|
let validKeys = Object.keys(parentValue)
|
||||||
|
.filter((key) => isValid(parentValue[key]))
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
levenshtein(keys[keys.length - 1], a) -
|
||||||
|
levenshtein(keys[keys.length - 1], b)
|
||||||
|
)
|
||||||
|
if (validKeys.length) {
|
||||||
|
message += ` Did you mean '${stitch([
|
||||||
|
...keys.slice(0, keys.length - 1),
|
||||||
|
validKeys[0],
|
||||||
|
])}'?`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = `'${match.groups.key}' was found but does not resolve to a string.`
|
||||||
|
|
||||||
|
if (isObject(value)) {
|
||||||
|
let firstValidKey = Object.keys(value).find((key) =>
|
||||||
|
isValid(value[key])
|
||||||
|
)
|
||||||
|
if (firstValidKey) {
|
||||||
|
message += ` Did you mean '${stitch([...keys, firstValidKey])}'?`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,10 +379,7 @@ function getInvalidHelperKeyDiagnostics(
|
||||||
severity === 'error'
|
severity === 'error'
|
||||||
? DiagnosticSeverity.Error
|
? DiagnosticSeverity.Error
|
||||||
: DiagnosticSeverity.Warning,
|
: DiagnosticSeverity.Warning,
|
||||||
message:
|
message,
|
||||||
typeof value === 'undefined'
|
|
||||||
? `'${match.groups.key}' does not exist in your theme config.`
|
|
||||||
: `'${match.groups.key}' was found but does not resolve to a string.`,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// https://github.com/lodash/lodash/blob/4.17.15/lodash.js#L6735-L6744
|
||||||
|
let rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g
|
||||||
|
let reEscapeChar = /\\(\\)?/g
|
||||||
|
|
||||||
|
export function stringToPath(string: string): string[] {
|
||||||
|
let result: string[] = []
|
||||||
|
if (string.charCodeAt(0) === 46 /* . */) {
|
||||||
|
result.push('')
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
string.replace(rePropName, (match, number, quote, subString) => {
|
||||||
|
result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match)
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in New Issue