diff --git a/README.md b/README.md
index d7e6f52..13d89d8 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,30 @@ Configure the rules you want to use under the rules section.
}
```
+### Limit the Nextcloud version to report
+By default all removed or deprecated API is reported, but if your app targets an older version of Nextcloud then you can limit the reported issues to changes before and with that version.
+
+For example you target Nextcloud 25 and you use `OC.L10n` which was deprecated with Nextcloud 26. To disable reporting that deprecation you can set the target version to *25*:
+
+```json
+{
+ "rules": {
+ "nextcloud/no-deprecations": ["warn", { "targetVersion": "25.0.0" }],
+ "nextcloud/no-removed-apis": ["error", { "targetVersion": "25.0.0" }],
+ }
+}
+```
+
+It is also possible to detect that your supported Nextcloud version from your `appinfo/info.xml` (`max-version` of your `nextcloud` dependency):
+```json
+{
+ "rules": {
+ "nextcloud/no-deprecations": ["warn", { "parseAppInfo": true }],
+ "nextcloud/no-removed-apis": ["error", { "parseAppInfo": true }],
+ }
+}
+```
+
## Supported Shared Configurations
* `nextcloud/recommended`: Recommended configuration that loads the Nextcloud ESlint plugin, adds the Nextcloud environment and configures all recommended Nextcloud rules.
diff --git a/__mocks__/node:fs.js b/__mocks__/node:fs.js
new file mode 100644
index 0000000..e5f40ad
--- /dev/null
+++ b/__mocks__/node:fs.js
@@ -0,0 +1,3 @@
+const { fs } = require('memfs')
+
+module.exports = fs
diff --git a/lib/rules/no-deprecations.js b/lib/rules/no-deprecations.js
index 865184d..023a035 100644
--- a/lib/rules/no-deprecations.js
+++ b/lib/rules/no-deprecations.js
@@ -1,5 +1,7 @@
"use strict";
+const { createVersionValidator } = require('../utils/version-parser.js')
+
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@@ -88,7 +90,21 @@ module.exports = {
},
fixable: null, // or "code" or "whitespace"
schema: [
- // fill in your schema
+ {
+ // We accept one option which is an object
+ type: "object",
+ properties: {
+ // if we should try to find an appinfo and only handle APIs removed before the max-version
+ parseAppInfo: {
+ type: "boolean"
+ },
+ // Set a Nextcloud target version, only APIs removed before that versions are checked
+ targetVersion: {
+ type: "string"
+ }
+ },
+ "additionalProperties": false
+ }
],
messages: {
deprecatedGlobal: "The global property or function {{name}} was deprecated in Nextcloud {{version}}"
@@ -96,21 +112,26 @@ module.exports = {
},
create: function (context) {
+ const checkTargetVersion = createVersionValidator(context)
+
return {
MemberExpression: function (node) {
// OC.x
if (node.object.name === 'OC'
- && oc.hasOwnProperty(node.property.name)) {
+ && oc.hasOwnProperty(node.property.name)
+ && checkTargetVersion(oc[node.property.name])) {
context.report(node, "The property or function OC." + node.property.name + " was deprecated in Nextcloud " + oc[node.property.name]);
}
// OCA.x
if (node.object.name === 'OCA'
- && oca.hasOwnProperty(node.property.name)) {
+ && oca.hasOwnProperty(node.property.name)
+ && checkTargetVersion(oca[node.property.name])) {
context.report(node, "The property or function OCA." + node.property.name + " was deprecated in Nextcloud " + oca[node.property.name]);
}
// OCP.x
if (node.object.name === 'OCP'
- && ocp.hasOwnProperty(node.property.name)) {
+ && ocp.hasOwnProperty(node.property.name)
+ && checkTargetVersion(ocp[node.property.name])) {
context.report(node, "The property or function OCP." + node.property.name + " was deprecated in Nextcloud " + ocp[node.property.name]);
}
@@ -119,13 +140,16 @@ module.exports = {
&& node.object.object.name === 'OC'
&& oc_sub.hasOwnProperty(node.object.property.name)
&& oc_sub[node.object.property.name].hasOwnProperty(node.property.name)) {
- const prop = [
- "OC",
- node.object.property.name,
- node.property.name,
- ].join('.');
const version = oc_sub[node.object.property.name][node.property.name]
- context.report(node, "The property or function " + prop + " was deprecated in Nextcloud " + version);
+ if (checkTargetVersion(version)) {
+ const prop = [
+ "OC",
+ node.object.property.name,
+ node.property.name,
+ ].join('.');
+ const version = oc_sub[node.object.property.name][node.property.name]
+ context.report(node, "The property or function " + prop + " was deprecated in Nextcloud " + version);
+ }
}
},
Program() {
@@ -133,14 +157,16 @@ module.exports = {
const scope = context.getScope();
const report = ref => {
const node = ref.identifier;
- context.report({
- node,
- messageId: 'deprecatedGlobal',
- data: {
- name: node.name,
- version: global[node.name]
- },
- });
+ if (checkTargetVersion(global[node.name])) {
+ context.report({
+ node,
+ messageId: 'deprecatedGlobal',
+ data: {
+ name: node.name,
+ version: global[node.name]
+ },
+ });
+ }
}
// Report variables declared elsewhere (ex: variables defined as "global" by eslint)
diff --git a/lib/rules/no-removed-apis.js b/lib/rules/no-removed-apis.js
index ff0ff32..2e880e7 100644
--- a/lib/rules/no-removed-apis.js
+++ b/lib/rules/no-removed-apis.js
@@ -1,4 +1,6 @@
-"use strict";
+"use strict"
+
+const { createVersionValidator } = require('../utils/version-parser.js')
//------------------------------------------------------------------------------
// Rule Definition
@@ -52,7 +54,21 @@ module.exports = {
},
fixable: null, // or "code" or "whitespace"
schema: [
- // fill in your schema
+ {
+ // We accept one option which is an object
+ type: "object",
+ properties: {
+ // if we should try to find an appinfo and only handle APIs removed before the max-version
+ parseAppInfo: {
+ type: "boolean"
+ },
+ // Set a Nextcloud target version, only APIs removed before that versions are checked
+ targetVersion: {
+ type: "string"
+ }
+ },
+ "additionalProperties": false
+ }
],
messages: {
removedGlobal: "The global property or function {{name}} was removed in Nextcloud {{version}}"
@@ -60,6 +76,8 @@ module.exports = {
},
create: function (context) {
+ const checkTargetVersion = createVersionValidator(context)
+
return {
MemberExpression: function (node) {
// OCA.x
@@ -70,8 +88,9 @@ module.exports = {
// OC.x
if (node.object.name === 'OC'
- && oc.hasOwnProperty(node.property.name)) {
- context.report(node, "The property or function OC." + node.property.name + " was removed in Nextcloud " + oc[node.property.name]);
+ && oc.hasOwnProperty(node.property.name)
+ && checkTargetVersion(oc[node.property.name])) {
+ context.report(node, "The property or function OC." + node.property.name + " was removed in Nextcloud " + oc[node.property.name])
}
// OC.x.y
@@ -79,44 +98,48 @@ module.exports = {
&& node.object.object.name === 'OC'
&& oc_sub.hasOwnProperty(node.object.property.name)
&& oc_sub[node.object.property.name].hasOwnProperty(node.property.name)) {
- const prop = [
- "OC",
- node.object.property.name,
- node.property.name,
- ].join('.');
const version = oc_sub[node.object.property.name][node.property.name]
- context.report(node, "The property or function " + prop + " was removed in Nextcloud " + version);
+ if (checkTargetVersion(version)) {
+ const prop = [
+ "OC",
+ node.object.property.name,
+ node.property.name,
+ ].join('.')
+ context.report(node, "The property or function " + prop + " was removed in Nextcloud " + version)
+ }
}
},
Program() {
// Logic adapted from https://github.com/eslint/eslint/blob/master/lib/rules/no-restricted-globals.js
- const scope = context.getScope();
+ const scope = context.getScope()
const report = ref => {
- const node = ref.identifier;
- context.report({
- node,
- messageId: 'removedGlobal',
- data: {
- name: node.name,
- version: global[node.name]
- },
- });
+ const node = ref.identifier
+ if (checkTargetVersion(global[node.name])) {
+ context.report({
+ node,
+ messageId: 'removedGlobal',
+ data: {
+ name: node.name,
+ version: global[node.name]
+ },
+ })
+ }
}
// Report variables declared elsewhere (ex: variables defined as "global" by eslint)
scope.variables.forEach(variable => {
if (!variable.defs.length && global.hasOwnProperty(variable.name)) {
- variable.references.forEach(report);
+ variable.references.forEach(report)
}
- });
+ })
// Report variables not declared at all
scope.through.forEach(reference => {
if (global.hasOwnProperty(reference.identifier.name)) {
- report(reference);
+ report(reference)
}
- });
+ })
}
- };
+ }
}
-};
+}
diff --git a/lib/utils/version-parser.js b/lib/utils/version-parser.js
new file mode 100644
index 0000000..db8c5de
--- /dev/null
+++ b/lib/utils/version-parser.js
@@ -0,0 +1,108 @@
+const { XMLParser } = require('fast-xml-parser')
+const fs = require('node:fs')
+const path = require('node:path')
+const semver = require('semver')
+
+/**
+ * Check if a given path exists and is a directory
+ *
+ * @param {string} filePath The path
+ * @return {boolean}
+ */
+const isDirectory = (filePath) => {
+ const stats = fs.lstatSync(filePath, { throwIfNoEntry: false })
+ return stats !== undefined && stats.isDirectory()
+}
+
+/**
+ * Check if a given path exists and is a directory
+ *
+ * @param {string} filePath The path
+ * @return {boolean}
+ */
+const isFile = (filePath) => {
+ const stats = fs.lstatSync(filePath, { throwIfNoEntry: false })
+ return stats !== undefined && stats.isFile()
+}
+
+/**
+ * Find the path of nearest `appinfo/info.xml` relative to given path
+ *
+ * @param {string} currentPath
+ * @return {string|undefined} Either the full path including the `info.xml` part or `undefined` if no found
+ */
+const findAppinfo = (currentPath) => {
+ while (currentPath && currentPath !== path.sep) {
+ const appinfoPath = `${currentPath}${path.sep}appinfo`
+ if (isDirectory(appinfoPath) && isFile(`${appinfoPath}${path.sep}info.xml`)) {
+ return `${appinfoPath}${path.sep}info.xml`
+ }
+ currentPath = path.resolve(currentPath, '..')
+ }
+ return undefined
+}
+
+/**
+ * Make sure that versions like '25' can be handled by semver
+ *
+ * @param {string} version The pure version string
+ * @return {string} Sanitized version string
+ */
+const sanitizeTargetVersion = (version) => {
+ let sanitizedVersion = version
+ const sections = sanitizedVersion.split('.').length
+ if (sections < 3) {
+ sanitizedVersion = sanitizedVersion + '.0'.repeat(3 - sections)
+ }
+ // now version should look like '25.0.0'
+ if (!semver.valid(sanitizedVersion)) {
+ throw Error(`[@nextcloud/eslint-plugin] Invalid target version ${version} found`)
+ }
+ return sanitizedVersion
+}
+
+function createVersionValidator({ cwd, physicalFilename, options }) {
+ const settings = options[0]
+
+ if (settings?.targetVersion) {
+ // check if the rule version is lower than the current target version
+ const maxVersion = sanitizeTargetVersion(settings.targetVersion)
+ return (version) => semver.lte(version, maxVersion)
+ }
+
+ // Try to find appinfo and parse the supported version
+ if (settings?.parseAppInfo) {
+ // Current working directory, either the filename (can be empty) or the cwd property
+ const currentDirectory = path.isAbsolute(physicalFilename)
+ ? path.resolve(path.dirname(physicalFilename))
+ : path.dirname(path.resolve(cwd, physicalFilename))
+
+ // The nearest appinfo
+ const appinfoPath = findAppinfo(currentDirectory)
+ if (appinfoPath) {
+ const parser = new XMLParser({
+ attributeNamePrefix: '@',
+ ignoreAttributes: false,
+ })
+ const xml = parser.parse(fs.readFileSync(appinfoPath))
+ let maxVersion = xml?.info?.dependencies?.nextcloud?.['@max-version']
+ if (typeof maxVersion !== 'string') {
+ throw Error(`[@nextcloud/eslint-plugin] AppInfo does not contain a max-version (location: ${appinfoPath})`)
+ }
+ maxVersion = sanitizeTargetVersion(maxVersion)
+ return (version) => semver.lte(version, maxVersion)
+ }
+ throw Error('[@nextcloud/eslint-plugin] AppInfo parsing was enabled, but no `appinfo/info.xml` was found.')
+ }
+
+ // If not configured or parsing is disabled, every rule should be handled
+ return () => true
+}
+
+module.exports = {
+ createVersionValidator,
+ findAppinfo,
+ isDirectory,
+ isFile,
+ sanitizeTargetVersion,
+}
diff --git a/package-lock.json b/package-lock.json
index 6ac73a4..7f10c01 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,12 +9,14 @@
"version": "2.1.0",
"license": "ISC",
"dependencies": {
- "requireindex": "^1.2.0"
+ "fast-xml-parser": "^4.2.5",
+ "requireindex": "^1.2.0",
+ "semver": "^7.5.3"
},
"devDependencies": {
"eslint": "^8.46.0",
"jest": "^29.6.2",
- "mocha": "^10.2.0"
+ "memfs": "^4.8.0"
},
"engines": {
"node": "^20.0.0",
@@ -98,6 +100,15 @@
"url": "https://opencollective.com/babel"
}
},
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/@babel/generator": {
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz",
@@ -129,6 +140,15 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
@@ -1330,15 +1350,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/ansi-escapes": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -1459,6 +1470,15 @@
"node": ">=8"
}
},
+ "node_modules/babel-plugin-istanbul/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
"node_modules/babel-plugin-jest-hoist": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
@@ -1519,15 +1539,6 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
- "node_modules/binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -1550,12 +1561,6 @@
"node": ">=8"
}
},
- "node_modules/browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true
- },
"node_modules/browserslist": {
"version": "4.23.0",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
@@ -1702,33 +1707,6 @@
"node": ">=10"
}
},
- "node_modules/chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -1750,17 +1728,6 @@
"integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==",
"dev": true
},
- "node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "dev": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -1856,18 +1823,6 @@
}
}
},
- "node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/dedent": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz",
@@ -1906,15 +1861,6 @@
"node": ">=8"
}
},
- "node_modules/diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
- "dev": true,
- "engines": {
- "node": ">=0.3.1"
- }
- },
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -2235,6 +2181,27 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
+ "node_modules/fast-xml-parser": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz",
+ "integrity": "sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/naturalintelligence"
+ }
+ ],
+ "dependencies": {
+ "strnum": "^1.0.5"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
"node_modules/fastq": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -2293,15 +2260,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "dev": true,
- "bin": {
- "flat": "cli.js"
- }
- },
"node_modules/flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -2409,18 +2367,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
@@ -2469,15 +2415,6 @@
"node": ">= 0.4"
}
},
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true,
- "bin": {
- "he": "bin/he"
- }
- },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -2568,18 +2505,6 @@
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true
},
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-core-module": {
"version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
@@ -2649,15 +2574,6 @@
"node": ">=8"
}
},
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -2670,18 +2586,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2713,39 +2617,6 @@
"node": ">=10"
}
},
- "node_modules/istanbul-lib-instrument/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/istanbul-lib-report": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
@@ -3288,39 +3159,6 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
- "node_modules/jest-snapshot/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/jest-snapshot/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/jest-snapshot/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/jest-util": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
@@ -3522,22 +3360,6 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
- "node_modules/log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -3562,46 +3384,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/make-dir/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "node_modules/makeerror": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
+ "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
"dev": true,
"dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
+ "tmpl": "1.0.5"
}
},
- "node_modules/make-dir/node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "node_modules/memfs": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.8.0.tgz",
+ "integrity": "sha512-fcs7trFxZlOMadmTw5nyfOwS3il9pr3y+6xzLfXNwmuR/D0i4wz6rJURxArAbcJDGalbpbMvQ/IFI0NojRZgRg==",
"dev": true,
"dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
+ "tslib": "^2.0.0"
},
"engines": {
- "node": ">=10"
- }
- },
- "node_modules/make-dir/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
- "node_modules/makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "dev": true,
- "dependencies": {
- "tmpl": "1.0.5"
+ "node": ">= 4.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/streamich"
}
},
"node_modules/merge-stream": {
@@ -3644,102 +3449,6 @@
"node": "*"
}
},
- "node_modules/mocha": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz",
- "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==",
- "dev": true,
- "dependencies": {
- "ansi-colors": "4.1.1",
- "browser-stdout": "1.3.1",
- "chokidar": "3.5.3",
- "debug": "4.3.4",
- "diff": "5.0.0",
- "escape-string-regexp": "4.0.0",
- "find-up": "5.0.0",
- "glob": "8.1.0",
- "he": "1.2.0",
- "js-yaml": "4.1.0",
- "log-symbols": "4.1.0",
- "minimatch": "5.0.1",
- "ms": "2.1.3",
- "serialize-javascript": "6.0.0",
- "strip-json-comments": "3.1.1",
- "supports-color": "8.1.1",
- "workerpool": "6.2.1",
- "yargs": "16.2.0",
- "yargs-parser": "20.2.4",
- "yargs-unparser": "2.0.0"
- },
- "bin": {
- "_mocha": "bin/_mocha",
- "mocha": "bin/mocha.js"
- },
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/mocha/node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/mocha/node_modules/minimatch": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
- "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/mocha/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "node_modules/mocha/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -4100,33 +3809,12 @@
}
]
},
- "node_modules/randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -4248,44 +3936,36 @@
"queue-microtask": "^1.2.2"
}
},
- "node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ]
- },
"node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
"bin": {
"semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
- "node_modules/serialize-javascript": {
+ "node_modules/semver/node_modules/lru-cache": {
"version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
- "dev": true,
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
- "randombytes": "^2.1.0"
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
}
},
+ "node_modules/semver/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4443,6 +4123,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strnum": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
+ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -4514,6 +4199,12 @@
"node": ">=8.0"
}
},
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+ "dev": true
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -4630,12 +4321,6 @@
"node": ">= 8"
}
},
- "node_modules/workerpool": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
- "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
- "dev": true
- },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -4720,48 +4405,6 @@
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
- "node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dev": true,
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs-parser": {
- "version": "20.2.4",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
- "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
- "dev": true,
- "dependencies": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 7299af7..076926e 100644
--- a/package.json
+++ b/package.json
@@ -18,16 +18,18 @@
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ },
"dependencies": {
- "requireindex": "^1.2.0"
+ "fast-xml-parser": "^4.2.5",
+ "requireindex": "^1.2.0",
+ "semver": "^7.5.3"
},
"devDependencies": {
"eslint": "^8.46.0",
"jest": "^29.6.2",
- "mocha": "^10.2.0"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
+ "memfs": "^4.8.0"
},
"engines": {
"node": "^20.0.0",
diff --git a/tests/fixtures/valid-appinfo/appinfo/info.xml b/tests/fixtures/valid-appinfo/appinfo/info.xml
new file mode 100644
index 0000000..7adb50b
--- /dev/null
+++ b/tests/fixtures/valid-appinfo/appinfo/info.xml
@@ -0,0 +1,10 @@
+
+
+ someapp
+ Some App
+
+
+
+
+
diff --git a/tests/lib/rules/no-deprecations.test.js b/tests/lib/rules/no-deprecations.test.js
index 6a2f71b..2d912eb 100644
--- a/tests/lib/rules/no-deprecations.test.js
+++ b/tests/lib/rules/no-deprecations.test.js
@@ -64,4 +64,39 @@ describe('no-deprecations', () => {
}
]
})
+
+ ruleTester.run('no-deprecations with target version', rule, {
+ valid: [
+ {
+ name: 'manual target version lower than deprecation',
+ code: "OCP.Toast.success('hello')",
+ options: [{ targetVersion: '18.0.0' }],
+ },
+ {
+ name: 'manual target version lower than deprecation (short version)',
+ code: "OCP.Toast.success('hello')",
+ options: [{ targetVersion: '18' }],
+ },
+ {
+ name: 'appinfo max-version lower than deprecation',
+ code: 'OC.L10n.translate()',
+ options: [{ parseAppInfo: true }],
+ filename: 'tests/fixtures/valid-appinfo/some-file.js',
+ },
+ ],
+
+ invalid: [
+ {
+ name: 'manual target version higher than deprecation',
+ code: 'OC.L10n.translate()',
+ options: [{ targetVersion: '27.0.0' }],
+ errors: [
+ {
+ message: 'The property or function OC.L10n was deprecated in Nextcloud 26.0.0',
+ type: 'MemberExpression',
+ },
+ ],
+ },
+ ],
+ })
})
diff --git a/tests/lib/rules/no-removed-apis.test.js b/tests/lib/rules/no-removed-apis.test.js
index 039edfd..24c15dd 100644
--- a/tests/lib/rules/no-removed-apis.test.js
+++ b/tests/lib/rules/no-removed-apis.test.js
@@ -50,4 +50,48 @@ describe('no-removed-api', () => {
}
]
})
+
+ ruleTester.run('no-removed-api (with target version parsing)', rule, {
+ valid: [
+ // Removed in 26, but accept if our target is 25
+ {
+ name: 'manual target version lower than removed',
+ code: 'OC.addTranslations()',
+ options: [{ targetVersion: '25.0.0' }],
+ },
+ {
+ name: 'appinfo max-version lower than removed',
+ code: 'OC.addTranslations()',
+ // enable appinfo parsing for testing only the target versions
+ options: [{ parseAppInfo: true }],
+ filename: 'tests/fixtures/valid-appinfo/some-file.js',
+ },
+ ],
+
+ invalid: [
+ // Removed in 26, so also fail if target is set to 26
+ {
+ name: 'manual target version equals removed',
+ code: 'OC.addTranslations()',
+ options: [{ targetVersion: '26.0.0' }],
+ errors: [
+ {
+ message: 'The property or function OC.addTranslations was removed in Nextcloud 26.0.0',
+ type: 'MemberExpression',
+ },
+ ],
+ },
+ {
+ name: 'manual target version greater than removed',
+ code: 'OC.addTranslations()',
+ options: [{ targetVersion: '27.0.0' }],
+ errors: [
+ {
+ message: 'The property or function OC.addTranslations was removed in Nextcloud 26.0.0',
+ type: 'MemberExpression',
+ },
+ ],
+ },
+ ],
+ })
})
diff --git a/tests/lib/utils/version-parser.test.js b/tests/lib/utils/version-parser.test.js
new file mode 100644
index 0000000..0245d78
--- /dev/null
+++ b/tests/lib/utils/version-parser.test.js
@@ -0,0 +1,187 @@
+const path = require('node:path')
+const utils = require('../../../lib/utils/version-parser.js')
+const { vol } = require('memfs')
+
+jest.mock('node:fs')
+
+describe('version-parser', () => {
+ beforeAll(() => vol.fromJSON({ [__dirname]: {}, [__filename]: '...'}))
+ afterAll(() => vol.reset())
+
+ test('isDirectory', () => {
+ expect(utils.isDirectory(__dirname)).toBe(true)
+ expect(utils.isDirectory(__filename)).toBe(false)
+ expect(utils.isDirectory(path.join(__dirname, 'does-not-exists.invalid'))).toBe(false)
+ })
+
+ test('isFile', () => {
+ expect(utils.isFile(__dirname)).toBe(false)
+ expect(utils.isFile(__filename)).toBe(true)
+ expect(utils.isFile(path.join(__dirname, 'does-not-exists.invalid'))).toBe(false)
+ })
+
+ test('sanitizeTargetVersion', () => {
+ expect(utils.sanitizeTargetVersion('25')).toBe('25.0.0')
+ expect(utils.sanitizeTargetVersion('25.0')).toBe('25.0.0')
+ expect(utils.sanitizeTargetVersion('25.0.1')).toBe('25.0.1')
+
+ try {
+ const output = utils.sanitizeTargetVersion('a.b.c')
+ expect(output).toBe('Should not be reached')
+ } catch (e) {
+ expect(e.message).toMatch(/Invalid target version/)
+ }
+
+ try {
+ const output = utils.sanitizeTargetVersion('25.0.0.1')
+ expect(output).toBe('Should not be reached')
+ } catch (e) {
+ expect(e.message).toMatch(/Invalid target version/)
+ }
+ })
+
+ describe('findAppinfo', () => {
+ afterEach(() => vol.reset())
+
+ it('finds an appinfo if provided', () => {
+ vol.fromNestedJSON(
+ {
+ '/a': {
+ appinfo: {
+ 'info.xml': '...',
+ },
+ src: {},
+ },
+ },
+ )
+
+ expect(utils.findAppinfo('/a/src')).toBe('/a/appinfo/info.xml')
+ })
+
+ it('finds an appinfo if provided on lower directory', () => {
+ vol.fromNestedJSON(
+ {
+ '/a': {
+ appinfo: {
+ 'info.xml': '...',
+ },
+ src: {
+ b: {
+ c: {},
+ },
+ },
+ },
+ },
+ )
+
+ expect(utils.findAppinfo('/a/src/b/c')).toBe('/a/appinfo/info.xml')
+ })
+
+ it('returns undefined if not found', () => {
+ vol.fromNestedJSON(
+ {
+ '/a/src/b/c': {},
+ },
+ )
+
+ expect(utils.findAppinfo('/a/src/b/c')).toBe(undefined)
+ })
+
+ it('required info.xml to exist', () => {
+ vol.fromNestedJSON({
+ '/a': {
+ 'appinfo': { },
+ 'src': { },
+ }
+ })
+
+ expect(utils.findAppinfo('/a/src')).toBe(undefined)
+ })
+ })
+
+ describe('createVersionValidator', () => {
+ it('static target version', () => {
+ const fn = utils.createVersionValidator({ cwd: '', physicalFilename: '', options: [{ targetVersion: '25.0.0' }] })
+ expect(fn('26.0.0')).toBe(false)
+ expect(fn('25.0.1')).toBe(false)
+ expect(fn('25.0.0')).toBe(true)
+ expect(fn('24.0.0')).toBe(true)
+ })
+
+ it('no config', () => {
+ const fn = utils.createVersionValidator({ cwd: '', physicalFilename: '', options: [] })
+ expect(fn('26.0.0')).toBe(true)
+ expect(fn('25.0.1')).toBe(true)
+ expect(fn('25.0.0')).toBe(true)
+ expect(fn('24.0.0')).toBe(true)
+ })
+
+ describe('app info', () => {
+ afterEach(() => vol.reset())
+
+ it('works with physical filename', () => {
+ vol.fromNestedJSON({
+ '/a': {
+ appinfo: {
+ 'info.xml': '',
+ },
+ src: { },
+ },
+ })
+ const fn = utils.createVersionValidator({ cwd: '', physicalFilename: '/a/src/b.js', options: [{ parseAppInfo: true }] })
+ expect(fn('28.0.0')).toBe(false)
+ expect(fn('27.0.0')).toBe(true)
+ expect(fn('26.0.0')).toBe(true)
+ expect(fn('25.0.0')).toBe(true)
+ })
+
+ it('works with cwd', () => {
+ vol.fromNestedJSON({
+ '/a': {
+ appinfo: {
+ 'info.xml': '',
+ },
+ src: { },
+ },
+ })
+ const fn = utils.createVersionValidator({ cwd: '/a', physicalFilename: 'src/b.js', options: [{ parseAppInfo: true }] })
+ expect(fn('28.0.0')).toBe(false)
+ expect(fn('27.0.0')).toBe(true)
+ expect(fn('26.0.0')).toBe(true)
+ expect(fn('25.0.0')).toBe(true)
+ })
+
+ it('throws error on missing max-version', () => {
+ vol.fromNestedJSON({
+ '/a': {
+ appinfo: {
+ 'info.xml': '',
+ },
+ src: { },
+ },
+ })
+ try {
+ const fn = utils.createVersionValidator({ cwd: '/a', physicalFilename: 'src/b.js', options: [{ parseAppInfo: true }] })
+ expect(typeof fn).beBe('Should not be reached')
+ } catch (e) {
+ expect(e.message).toMatch('AppInfo does not contain a max-version')
+ }
+ })
+
+ it('throws an error if appinfo was not found', () => {
+ vol.fromNestedJSON({
+ '/a': {
+ appinfo: { },
+ src: { },
+ },
+ })
+ try {
+ const fn = utils.createVersionValidator({ cwd: '/a', physicalFilename: 'src/b.js', options: [{ parseAppInfo: true }] })
+ expect(typeof fn).beBe('Should not be reached')
+ } catch (e) {
+ expect(e.message).toMatch('AppInfo parsing was enabled, but no `appinfo/info.xml` was found')
+ }
+ })
+ })
+ })
+})