Comparing version 6.1.0 to 7.0.0
# Changelog | ||
## v7.0.0 | ||
- Drop support for Node.js < 10. | ||
- Add .yaspellerrc.js and .yaspellerrc.json for project config #153, #150 | ||
## v6.1.0 | ||
@@ -4,0 +8,0 @@ - Fix lost symlink #145, #128. |
@@ -18,2 +18,4 @@ 'use strict'; | ||
} | ||
process.exit(); | ||
} | ||
@@ -20,0 +22,0 @@ |
@@ -7,14 +7,12 @@ 'use strict'; | ||
const programOptions = require('../options'); | ||
const dict = require('../dictionary'); | ||
const { cliActionInit } = require('./actions'); | ||
const { defaultConfig } = require('../config'); | ||
const dictionary = require('../dictionary'); | ||
const { setCliOptions, getMergedOptions } = require('./options'); | ||
const reports = require('../reports'); | ||
const tasks = require('../tasks'); | ||
const { prepareRegExpToIgnoreText } = require('../helpers/ignore'); | ||
const { getConfig, defaultConfig } = require('../config'); | ||
const { setDebugMode } = require('../helpers/debug'); | ||
const { cliActionInit } = require('./actions'); | ||
programOptions.init({defaultIgnoreTags: defaultConfig.ignoreTags.join(',')}); | ||
setCliOptions(defaultConfig); | ||
program.parse(process.argv); | ||
@@ -27,40 +25,8 @@ | ||
const jsonConfig = getConfig(program.config); | ||
const json = Object.assign({}, defaultConfig, jsonConfig.data); | ||
const mergedOptions = getMergedOptions(program.config); | ||
const settings = { | ||
excludeFiles: json.excludeFiles, | ||
options: json.options || {} | ||
}; | ||
dictionary.loadDictionaries(program.dictionary, mergedOptions.configDictionary); | ||
[ | ||
'checkYo', | ||
'fileExtensions', | ||
'format', | ||
'ignoreTags', | ||
'ignoreText', | ||
'lang', | ||
'maxRequests' | ||
].forEach(function(key) { | ||
settings[key] = program[key] || json[key]; | ||
}); | ||
settings.ignoreText = prepareRegExpToIgnoreText(settings.ignoreText); | ||
programOptions.apiOptions.forEach(function(el) { | ||
const key = el[0]; | ||
if (program[key]) { | ||
settings.options[key] = true; | ||
} else if (typeof json[key] !== 'undefined') { | ||
settings.options[key] = json[key]; | ||
} | ||
}); | ||
dict.set(program.dictionary, json.dictionary); | ||
reports.addReports(program.report || json.report); | ||
if (program.init) { | ||
cliActionInit(); | ||
process.exit(); | ||
} | ||
@@ -73,12 +39,12 @@ | ||
reports.onstart(); | ||
reports.set(mergedOptions.report); | ||
reports.onStart(); | ||
async.series( | ||
isStdin ? | ||
tasks.forStdin(settings, program.stdinFilename) : | ||
tasks.forResources(program.args, settings), | ||
function() { | ||
reports.onend(jsonConfig.relativePath); | ||
process.exit(); | ||
} | ||
); | ||
tasks.forStdin(program.stdinFilename, mergedOptions) : | ||
tasks.forResources(program.args, mergedOptions) | ||
).then(() => { | ||
reports.onComplete(mergedOptions.configRelativePath); | ||
process.exit(); | ||
}); |
@@ -40,4 +40,6 @@ 'use strict'; | ||
'package.json', | ||
'.yaspeller.json', | ||
'.yaspellerrc', | ||
'.yaspeller.json' | ||
'.yaspellerrc.js', | ||
'.yaspellerrc.json', | ||
] | ||
@@ -77,4 +79,18 @@ }); | ||
/** | ||
* Get merged config. | ||
* | ||
* @param {string} filename | ||
* @returns {Object} | ||
*/ | ||
function getMergedConfig(filename) { | ||
const config = getConfig(filename); | ||
return Object.assign({ | ||
configRelativePath: config.relativePath, | ||
}, defaultConfig, config.data); | ||
} | ||
/** | ||
* Check config properties. | ||
* | ||
* | ||
* @param {*} obj | ||
@@ -102,4 +118,5 @@ * @param {string|undefined} file | ||
module.exports = { | ||
defaultConfig, | ||
getConfig, | ||
defaultConfig, | ||
getMergedConfig, | ||
}; |
@@ -10,3 +10,3 @@ 'use strict'; | ||
const { loadFileAsJson } = require('./helpers/file'); | ||
const { uniq } = require('./helpers/array'); | ||
const { uniq, notUniq } = require('./helpers/array'); | ||
const { consoleError, consoleWarn, consoleLog, consoleDebug } = require('./helpers/console'); | ||
@@ -19,47 +19,24 @@ | ||
module.exports = { | ||
class Dictionary { | ||
constructor() { | ||
this.dict = []; | ||
} | ||
/** | ||
* Set dictionary. | ||
* | ||
* @param {Array} files | ||
* @param {Array} configDictionary - Dictionary from .yaspellerrc | ||
* @param {string[]} words | ||
*/ | ||
set(files, configDictionary) { | ||
let commonUniqueWords = []; | ||
let count = 0; | ||
let result = []; | ||
set(words) { | ||
this.dict = this.prepareDictionaryWords(words); | ||
} | ||
const prepare = (words, file) => { | ||
result = result.concat(words); | ||
this.checkDuplicates(words, `Dictionary duplicate words in "${file}":`); | ||
this.checkTyposInDictionary(words, file); | ||
commonUniqueWords = commonUniqueWords.concat(uniq(words)); | ||
count++; | ||
}; | ||
if (configDictionary) { | ||
prepare(configDictionary, '.yaspellerrc'); | ||
} | ||
files && files.forEach(function(file) { | ||
prepare(this.loadDictionary(file), file); | ||
}, this); | ||
if (count > 1) { | ||
this.checkDuplicates(commonUniqueWords, 'Duplicate words in dictionaries:'); | ||
} | ||
this._dict = this.prepareDictionary(result); | ||
}, | ||
/** | ||
* Get dictionary. | ||
* | ||
* @returns {Array} | ||
* @returns {RegExp[]} | ||
*/ | ||
get() { | ||
return this._dict; | ||
}, | ||
_dict: [], | ||
return this.dict; | ||
} | ||
/** | ||
@@ -69,3 +46,3 @@ * Load dictionary. | ||
* @param {string} file - JSON file. | ||
* @returns {Array} | ||
* @returns {string[]} | ||
*/ | ||
@@ -75,3 +52,3 @@ loadDictionary(file) { | ||
consoleDebug('Get/check dictionary: ' + file); | ||
consoleDebug(`Get/check dictionary: ${file}`); | ||
@@ -81,3 +58,3 @@ try { | ||
consoleDebug('Use dictionary: ' + file); | ||
consoleDebug(`Use dictionary: ${file}`); | ||
} catch (e) { | ||
@@ -89,4 +66,38 @@ consoleError(e); | ||
return data; | ||
}, | ||
} | ||
/** | ||
* Load dictionaries. | ||
* | ||
* @param {string[]} files | ||
* @param {string[]} configDictionary - Dictionary from .yaspellerrc | ||
*/ | ||
loadDictionaries(files, configDictionary) { | ||
let count = 0; | ||
let result = []; | ||
const prepare = (words, file) => { | ||
result = result.concat(uniq(words)); | ||
this.checkDuplicateWords(words, `Dictionary duplicate words in "${file}":`); | ||
this.checkTyposInDictionary(words, file); | ||
count++; | ||
}; | ||
if (configDictionary) { | ||
prepare(configDictionary, '.yaspellerrc'); | ||
} | ||
files && files.forEach(file => { | ||
prepare(this.loadDictionary(file), file); | ||
}); | ||
if (count >= 2) { | ||
this.checkDuplicateWords(result, 'Duplicate words in dictionaries:'); | ||
} | ||
this.set(result); | ||
} | ||
/** | ||
* Check duplicate words in dictionary. | ||
@@ -98,4 +109,4 @@ * | ||
*/ | ||
checkDuplicates(words, title) { | ||
const duplicates = this.getDuplicates(words); | ||
checkDuplicateWords(words, title) { | ||
const duplicates = notUniq(words); | ||
if (duplicates.length) { | ||
@@ -108,3 +119,4 @@ consoleWarn(title + '\n' + duplicates.join('\n') + '\n'); | ||
return false; | ||
}, | ||
} | ||
/** | ||
@@ -119,11 +131,11 @@ * Check typos in dictionary. | ||
const typos = []; | ||
words.forEach(function(word) { | ||
if (hasEngRusLetters(word)) { | ||
typos.push(word); | ||
words.forEach(item => { | ||
if (hasEngRusLetters(item)) { | ||
typos.push(item); | ||
} | ||
}); | ||
const hasTypos = typos.length ? true : false; | ||
const hasTypos = Boolean(typos.length); | ||
if (hasTypos) { | ||
consoleWarn('Has typos in "' + file + '":'); | ||
consoleWarn(`Has typos in "${file}":`); | ||
typos.forEach(item => { | ||
@@ -139,3 +151,4 @@ consoleWarn(item + | ||
return hasTypos; | ||
}, | ||
} | ||
/** | ||
@@ -147,14 +160,15 @@ * Remove typos that is in the dictionary. | ||
*/ | ||
removeDictWords(data) { | ||
removeDictionaryWordsFromData(data) { | ||
const result = []; | ||
const dictionary = this.get(); | ||
data.forEach(function(typo) { | ||
data.forEach(typo => { | ||
if (typo.code === ERROR_TOO_MANY_ERRORS || this.isTypo(typo.word, dictionary)) { | ||
result.push(typo); | ||
} | ||
}, this); | ||
}); | ||
return result; | ||
}, | ||
} | ||
/** | ||
@@ -164,19 +178,19 @@ * It's a typo? | ||
* @param {string} word | ||
* @param {string[]|RegExp[]} dictionary | ||
* @param {RegExp[]} dictionary | ||
* @returns {boolean} | ||
*/ | ||
isTypo(word, dictionary) { | ||
return !dictionary.some(function(dictWord) { | ||
return dictWord.test(word); | ||
}); | ||
}, | ||
return !dictionary.some(item => item.test(word)); | ||
} | ||
/** | ||
* Prepare dictionary. | ||
* Prepare dictionary words. | ||
* | ||
* @param {string[]} dict | ||
* @returns {Array} | ||
* @param {string[]} dictionaryWords | ||
* @returns {RegExp[]} | ||
*/ | ||
prepareDictionary(dict) { | ||
prepareDictionaryWords(dictionaryWords) { | ||
const result = []; | ||
dict.forEach(function(word) { | ||
dictionaryWords.forEach(word => { | ||
if (this.isNotOptimizedRegExp(word)) { | ||
@@ -190,7 +204,7 @@ consoleWarn(`Not optimized dictionary RegExp in "${word}"`); | ||
let preparedWord = word.replace(rePrepare, ($, $1, $2) => '[' + $1 + $1.toUpperCase() + ']' + $2); | ||
if (preparedWord.search(/\^/) !== 0) { | ||
preparedWord = '^' + preparedWord; | ||
} | ||
if (preparedWord.search(/\$/) !== preparedWord.length - 1) { | ||
@@ -205,6 +219,7 @@ preparedWord += '$'; | ||
} | ||
}, this); | ||
}); | ||
return result; | ||
}, | ||
} | ||
/** | ||
@@ -226,24 +241,5 @@ * Is not optimized RegExp? | ||
return false; | ||
}, | ||
/** | ||
* Get duplicate words. | ||
* | ||
* @param {string[]} words | ||
* @returns {string[]} | ||
*/ | ||
getDuplicates(words) { | ||
const buffer = {}; | ||
const result = []; | ||
} | ||
} | ||
words.forEach(function(word) { | ||
if (!buffer[word]) { | ||
buffer[word] = 1; | ||
} else if (buffer[word] === 1) { | ||
buffer[word]++; | ||
result.push(word); | ||
} | ||
}); | ||
return result; | ||
}, | ||
}; | ||
module.exports = new Dictionary(); |
@@ -11,4 +11,27 @@ /** | ||
/** | ||
* Get a array with not unique values. | ||
* | ||
* @param {Array} arr | ||
* @returns {Array} | ||
*/ | ||
function notUniq(arr) { | ||
const buffer = {}; | ||
const result = []; | ||
arr.forEach(item => { | ||
if (!buffer[item]) { | ||
buffer[item] = 1; | ||
} else if (buffer[item] === 1) { | ||
buffer[item]++; | ||
result.push(item); | ||
} | ||
}); | ||
return result; | ||
} | ||
module.exports = { | ||
uniq, | ||
notUniq, | ||
}; |
@@ -63,6 +63,7 @@ 'use strict'; | ||
module.exports = { | ||
onstart() { | ||
name: 'console', | ||
onStart() { | ||
consoleLog('Spelling check:'); | ||
}, | ||
oneach(err, data) { | ||
onResourceComplete(err, data) { | ||
const errors = []; | ||
@@ -102,3 +103,3 @@ if (err) { | ||
}, | ||
onend(data, stats, configPath) { | ||
onComplete(data, stats, configPath) { | ||
if (!program.onlyErrors && stats.total) { | ||
@@ -114,3 +115,3 @@ if (stats.hasTypos) { | ||
} | ||
if (!stats.errors) { | ||
@@ -117,0 +118,0 @@ consoleOk('No errors.'); |
@@ -12,3 +12,4 @@ 'use strict'; | ||
module.exports = { | ||
onend(data) { | ||
name: 'error_dictionary', | ||
onComplete(data) { | ||
let buffer = []; | ||
@@ -15,0 +16,0 @@ |
'use strict'; | ||
module.exports = { | ||
onstart() { | ||
console.log('onstart'); | ||
name: 'example', | ||
onStart() { | ||
console.log('onStart'); | ||
}, | ||
oneach(err, data) { | ||
console.log('oneach: ', err, data); | ||
onResourceComplete(error, data) { | ||
console.log('onResourceComplete', error, data); | ||
}, | ||
onend(data, stats) { | ||
console.log('onend: ', data, stats); | ||
onComplete(data, stats) { | ||
console.log('onComplete', data, stats); | ||
} | ||
}; |
@@ -69,3 +69,4 @@ 'use strict'; | ||
module.exports = { | ||
oneach(err, data) { | ||
name: 'html', | ||
onResourceComplete(err, data) { | ||
const html = []; | ||
@@ -126,3 +127,3 @@ if (err) { | ||
}, | ||
onend(data, stats) { | ||
onComplete(data, stats) { | ||
const content = '<div class="total">Processed resources: ' + stats.total + | ||
@@ -129,0 +130,0 @@ ' (<span class="sym-err">χ</span>– ' + stats.errors + |
'use strict'; | ||
const program = require('commander'); | ||
const pth = require('path'); | ||
const { defaultConfig } = require('../config'); | ||
const { uniq } = require('../helpers/array'); | ||
const { consoleError } = require('../helpers/console'); | ||
const { splitByCommas } = require('../helpers/string'); | ||
const stats = { | ||
errors: 0, | ||
hasTypos: false, | ||
ok: 0, | ||
total: 0, | ||
}; | ||
const reports = []; | ||
const reportNames = new Set(); | ||
const buffer = []; | ||
const consoleReport = require('./console'); | ||
const errorDictionaryReport = require('./error_dictionary'); | ||
const htmlReport = require('./html'); | ||
const jsonReport = require('./json'); | ||
const markdownReport = require('./markdown'); | ||
module.exports = { | ||
addReports(names) { | ||
names.forEach(function(name) { | ||
const moduleName = pth.extname(name) === '.js' ? name : './' + name; | ||
class Reports { | ||
constructor() { | ||
this.buffer = []; | ||
if (reportNames.has(moduleName)) { | ||
this.innerReports = [ | ||
consoleReport, | ||
errorDictionaryReport, | ||
htmlReport, | ||
jsonReport, | ||
markdownReport, | ||
]; | ||
this.innerReportsByName = this.innerReports.reduce((acc, current) => { | ||
acc[current.name] = current; | ||
return acc; | ||
}, {}); | ||
this.stats = { | ||
errors: 0, | ||
hasTypos: false, | ||
ok: 0, | ||
total: 0, | ||
}; | ||
this.reports = []; | ||
} | ||
/** | ||
* Set reports. | ||
* | ||
* @param {string|string[]|undefined} names | ||
*/ | ||
set(names) { | ||
this.reports = []; | ||
if (typeof names === 'string') { | ||
names = splitByCommas(names); | ||
} else if (Array.isArray(names)) { | ||
names = names.map(item => item.trim()); | ||
} else { | ||
names = []; | ||
} | ||
names = uniq(names).filter(Boolean); | ||
if (!names.length) { | ||
names = defaultConfig.report; | ||
} | ||
names.forEach(name => { | ||
const report = this.innerReportsByName[name] || this.loadExternalReport(name); | ||
if (report) { | ||
this.reports.push(report); | ||
} | ||
}); | ||
} | ||
/** | ||
* Load external report. | ||
* | ||
* @param {string} name | ||
* @returns {Report|undefined} | ||
*/ | ||
loadExternalReport(name) { | ||
try { | ||
const report = require(require.resolve(name, { | ||
paths: ['./'] | ||
})); | ||
if (!report.name) { | ||
consoleError(`Missing "name" property in report module "${name}".`); | ||
return; | ||
} | ||
try { | ||
reports.push(require(moduleName)); | ||
reportNames.add(moduleName); | ||
} catch (e) { | ||
consoleError(`Can't load report module "${moduleName}".`); | ||
consoleError(e); | ||
if (!report.onStart && !report.onComplete && !report.onResourceComplete) { | ||
consoleError(`Missing methods (onStart, onResourceComplete or onComplete) in report module "${name}".`); | ||
return; | ||
} | ||
return report; | ||
} catch (e) { | ||
consoleError(e); | ||
} | ||
} | ||
onStart() { | ||
this.reports.forEach(report => { | ||
report.onStart && report.onStart(); | ||
}); | ||
}, | ||
onstart() { | ||
reports.forEach(function(name) { | ||
name.onstart && name.onstart(); | ||
}); | ||
}, | ||
oneach(hasError, data, dictionary) { | ||
stats.total++; | ||
} | ||
onResourceComplete(hasError, data, dictionary) { | ||
this.stats.total++; | ||
const hasTypos = Boolean(data && data.data && data.data.length); | ||
if (hasTypos) { | ||
stats.hasTypos = true; | ||
this.stats.hasTypos = true; | ||
} | ||
if (hasError || hasTypos) { | ||
stats.errors++; | ||
this.stats.errors++; | ||
buffer.push([hasError, data]); | ||
this.buffer.push([hasError, data]); | ||
} else { | ||
stats.ok++; | ||
this.stats.ok++; | ||
if (!program.onlyErrors) { | ||
buffer.push([hasError, data]); | ||
this.buffer.push([hasError, data]); | ||
} | ||
@@ -63,12 +130,24 @@ } | ||
if (!program.onlyErrors || hasError || hasTypos) { | ||
reports.forEach(function(name) { | ||
name.oneach && name.oneach(hasError, data, dictionary); | ||
this.reports.forEach(report => { | ||
report.onResourceComplete && report.onResourceComplete(hasError, data, dictionary); | ||
}); | ||
} | ||
}, | ||
onend(configPath) { | ||
reports.forEach(function(name) { | ||
name.onend && name.onend(buffer, stats, configPath); | ||
} | ||
onComplete(configPath) { | ||
this.reports.forEach(report => { | ||
report.onComplete && report.onComplete(this.buffer, this.stats, configPath); | ||
}); | ||
} | ||
}; | ||
} | ||
module.exports = new Reports(); | ||
/** | ||
@typedef Report | ||
@type {Object} | ||
@property {string} name | ||
@property {Function?} onStart | ||
@property {Function?} onResourceComplete | ||
@property {Function?} onComplete | ||
*/ |
@@ -11,3 +11,4 @@ 'use strict'; | ||
module.exports = { | ||
onend(data) { | ||
name: 'json', | ||
onComplete(data) { | ||
try { | ||
@@ -14,0 +15,0 @@ fs.writeFileSync(filename, jsonStringify(data)); |
@@ -35,3 +35,4 @@ 'use strict'; | ||
module.exports = { | ||
oneach(err, data) { | ||
name: 'markdown', | ||
onResourceComplete(err, data) { | ||
const text = []; | ||
@@ -81,3 +82,3 @@ if (err) { | ||
}, | ||
onend(data, stats) { | ||
onComplete(data, stats) { | ||
const text = '<br/><br/>Processed resources: ' + stats.total + | ||
@@ -84,0 +85,0 @@ ' (χ – ' + stats.errors + ', ✓ – ' + stats.ok + ')<br/>' + |
@@ -8,3 +8,3 @@ 'use strict'; | ||
const dict = require('./dictionary'); | ||
const dictionary = require('./dictionary'); | ||
const exitCodes = require('./exit-codes'); | ||
@@ -22,3 +22,3 @@ const reports = require('./reports'); | ||
if (hasData(err, data)) { | ||
data.data = dict.removeDictWords(data.data); | ||
data.data = dictionary.removeDictionaryWordsFromData(data.data); | ||
data.data = yaspeller.removeDuplicates(data.data); | ||
@@ -39,3 +39,3 @@ | ||
reports.oneach(err, data); | ||
reports.onResourceComplete(err, data); | ||
} | ||
@@ -90,7 +90,7 @@ | ||
* | ||
* @param {string} filename | ||
* @param {Object} settings | ||
* @param {string} [filename] | ||
* @returns {Array} | ||
*/ | ||
forStdin(settings, filename) { | ||
forStdin(filename, settings) { | ||
return [function(callback) { | ||
@@ -97,0 +97,0 @@ let text = ''; |
The MIT License (MIT) | ||
© 2018 Denis Seleznev, <hcodes@yandex.ru> | ||
© 2020 Denis Seleznev, <hcodes@yandex.ru> | ||
@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy |
@@ -13,3 +13,3 @@ { | ||
"description": "Search tool typos in the text, files and websites", | ||
"version": "6.1.0", | ||
"version": "7.0.0", | ||
"license": "MIT", | ||
@@ -37,3 +37,3 @@ "homepage": "https://github.com/hcodes/yaspeller", | ||
"async": "^3.2.0", | ||
"chalk": "^3.0.0", | ||
"chalk": "^4.0.0", | ||
"commander": "^3.0.0", | ||
@@ -62,3 +62,3 @@ "cosmiconfig": "^6.0.0", | ||
"engines": { | ||
"node": ">=8" | ||
"node": ">=10" | ||
}, | ||
@@ -76,4 +76,7 @@ "scripts": { | ||
".yaspellerrc.default.json", | ||
"LICENSE.md" | ||
"CHANGELOG.md", | ||
"LICENSE.md", | ||
"README.md", | ||
"README.ru.md" | ||
] | ||
} |
@@ -7,3 +7,2 @@ yaspeller | ||
[![Dependency Status](https://img.shields.io/david/hcodes/yaspeller.svg)](https://david-dm.org/hcodes/yaspeller) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/hcodes/yaspeller/badge.svg)](https://snyk.io/test/github/hcodes/yaspeller) | ||
@@ -164,2 +163,4 @@ <img align="right" width="200" src="https://raw.githubusercontent.com/hcodes/yaspeller/master/images/logo.png" /> | ||
- `.yaspellerrc` | ||
- `.yaspellerrc.js` | ||
- `.yaspellerrc.json` | ||
- `.yaspeller.json` | ||
@@ -166,0 +167,0 @@ - `package.json`, field `yaspeller` |
@@ -7,3 +7,2 @@ yaspeller | ||
[![Dependency Status](https://img.shields.io/david/hcodes/yaspeller.svg)](https://david-dm.org/hcodes/yaspeller) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/hcodes/yaspeller/badge.svg)](https://snyk.io/test/github/hcodes/yaspeller) | ||
@@ -155,2 +154,4 @@ <img align="right" width="200" src="https://raw.githubusercontent.com/hcodes/yaspeller/master/images/logo.png" /> | ||
- `.yaspellerrc` | ||
- `.yaspellerrc.js` | ||
- `.yaspellerrc.json` | ||
- `.yaspeller.json` | ||
@@ -157,0 +158,0 @@ - `package.json`, поле `yaspeller` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
93690
2267
307
2
11
+ Addedchalk@4.1.2(transitive)
- Removedchalk@3.0.0(transitive)
Updatedchalk@^4.0.0