@nextcloud/eslint-config 8.3.0-beta.0 → 8.3.0-beta.1

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
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## [v8.3.0-beta.1](https://github.com/nextcloud/eslint-config/tree/v8.3.0-beta.1) (2023-06-27)
6
+
7
+ [Full Changelog](https://github.com/nextcloud/eslint-config/compare/v8.3.0-beta.0...v8.3.0-beta.1)
8
+
9
+ **Features:**
10
+ - Provide config for vue files written in Typescript, use `extends: "@nextcloud/eslint-config/typescript"`.
11
+ - Fully support vue files using the Composition API `<script setup>`.
12
+
13
+ **Fixed:**
14
+ - fix: Add Typescript overrides for all valid Typescript file extensions by @susnux in https://github.com/nextcloud/eslint-config/pull/567
15
+
3
16
  ## [v8.3.0-beta.0](https://github.com/nextcloud/eslint-config/tree/v8.3.0-beta.0) (2023-05-12)
4
17
 
5
18
  [Full Changelog](https://github.com/nextcloud/eslint-config/compare/v8.2.1...v8.3.0-beta.0)
package/README.md CHANGED
@@ -28,6 +28,20 @@ module.exports = {
28
28
  }
29
29
  ```
30
30
 
31
+ ### Usage with Typescript projects
32
+
33
+ If your projects uses Typescript for vue files, like `<script lang="ts">` then use the Typescript config instead:
34
+
35
+ Add a file `.eslintrc.js` in the root directory of your app repository with the following content:
36
+
37
+ ```js
38
+ module.exports = {
39
+ extends: [
40
+ '@nextcloud/eslint-config/typescript',
41
+ ],
42
+ }
43
+ ```
44
+
31
45
  ## Release new version
32
46
 
33
47
  1. Update CHANGELOG.md file with the latest changes
package/index.js CHANGED
@@ -1,157 +1,21 @@
1
+ const base = require('./parts/base.js')
2
+ const typescriptOverrides = require('./parts/typescript.js')
3
+ const vueOverrides = require('./parts/vue.js')
4
+
5
+ /**
6
+ * Config for Vue + Javascript projects (optionally with parts, except vue files, written in Typescript)
7
+ */
1
8
  module.exports = {
2
- root: true,
3
- env: {
4
- browser: true,
5
- commonjs: true,
6
- es6: true,
7
- node: true,
8
- // Allow jest syntax in the src folder
9
- jest: true,
10
- },
11
- parserOptions: {
12
- parser: '@babel/eslint-parser',
13
- ecmaVersion: 6,
14
- requireConfigFile: false,
15
- },
16
- extends: [
17
- 'eslint:recommended',
18
- 'plugin:import/errors',
19
- 'plugin:import/warnings',
20
- 'plugin:n/recommended',
21
- 'plugin:vue/recommended',
22
- 'plugin:@nextcloud/recommended',
23
- 'plugin:jsdoc/recommended',
24
- 'standard',
25
- ],
26
- settings: {
27
- 'import/resolver': {
28
- node: {
29
- paths: ['src'],
30
- extensions: ['.js', '.vue'],
31
- },
32
- exports: {
33
- conditions: ['import'],
34
- },
35
- },
36
- jsdoc: {
37
- tagNamePreference: {
38
- returns: 'return',
39
- },
40
- mode: 'typescript',
41
- },
42
- },
43
- plugins: ['vue', 'n', 'jsdoc'],
44
- rules: {
45
- // space before function ()
46
- 'space-before-function-paren': ['error', {
47
- anonymous: 'never',
48
- named: 'never',
49
- asyncArrow: 'always',
50
- }],
51
- // stay consistent with array brackets
52
- 'array-bracket-newline': ['error', 'consistent'],
53
- // tabs only for indentation
54
- indent: ['error', 'tab'],
55
- 'no-tabs': ['error', { allowIndentationTabs: true }],
56
- 'vue/html-indent': ['error', 'tab'],
57
- // allow spaces after tabs for alignment
58
- 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
59
- // only debug console
60
- 'no-console': ['error', { allow: ['error', 'warn', 'info', 'debug'] }],
61
- // classes blocks
62
- 'padded-blocks': ['error', { classes: 'always' }],
63
- // always have the operator in front
64
- 'operator-linebreak': ['error', 'before'],
65
- // ternary on multiline
66
- 'multiline-ternary': ['error', 'always-multiline'],
67
- // force proper JSDocs
68
- 'jsdoc/require-returns': 0,
69
- 'jsdoc/require-returns-description': 0,
70
- 'jsdoc/tag-lines': ['off'],
71
- // disallow use of "var"
72
- 'no-var': 'error',
73
- // suggest using const
74
- 'prefer-const': 'error',
75
- // es6 import/export and require
76
- 'n/no-unpublished-require': ['off'],
77
- 'n/no-unsupported-features/es-syntax': ['off'],
78
- // PascalCase components names for vuejs
79
- // https://vuejs.org/v2/style-guide/#Single-file-component-filename-casing-strongly-recommended
80
- 'vue/component-name-in-template-casing': ['error', 'PascalCase'],
81
- // force name
82
- 'vue/match-component-file-name': ['error', {
83
- extensions: ['jsx', 'vue', 'js'],
84
- shouldMatchCase: true,
85
- }],
86
- // space before self-closing elements
87
- 'vue/html-closing-bracket-spacing': 'error',
88
- // no ending html tag on a new line
89
- 'vue/html-closing-bracket-newline': ['error', { multiline: 'never' }],
90
- // check vue files too
91
- 'n/no-missing-import': ['error', {}],
92
- // code spacing with attributes
93
- 'vue/max-attributes-per-line': ['error', {
94
- singleline: 3,
95
- multiline: 1,
96
- }],
97
- 'vue/first-attribute-linebreak': ['error', {
98
- singleline: 'beside',
99
- multiline: 'beside',
100
- }],
101
- // Allow single-word components names
102
- 'vue/multi-word-component-names': ['off'],
103
- // custom event naming convention
104
- 'vue/custom-event-name-casing': ['error', 'kebab-case', {
105
- // allows custom xxxx:xxx events formats
106
- ignores: ['/^[a-z]+(?:-[a-z]+)*:[a-z]+(?:-[a-z]+)*$/u'],
107
- }],
108
- // always add a trailing comma (for diff readability)
109
- 'comma-dangle': ['warn', 'always-multiline'],
110
- // Allow shallow import of @vue/test-utils and @testing-library/vue in order to be able to use it in
111
- // the src folder
112
- 'n/no-unpublished-import': ['error', {
113
- allowModules: ['@vue/test-utils', '@testing-library/vue'],
114
- }],
115
- // require object literal shorthand syntax
116
- 'object-shorthand': ['error', 'always'],
117
- // Warn when file extensions are not used on import paths
118
- 'import/extensions': ['warn', 'always', {
119
- ignorePackages: true,
120
- }],
121
- },
9
+ // Base rules
10
+ ...base,
11
+ // basic Typescript rules
122
12
  overrides: [
123
13
  {
124
- files: ['**/*.ts', '**/*.tsx'],
125
- extends: [
126
- '@vue/eslint-config-typescript/recommended',
127
- 'plugin:import/typescript',
128
- ],
129
- parserOptions: {
130
- parser: '@typescript-eslint/parser',
131
- },
132
- rules: {
133
- 'n/no-missing-import': 'off',
134
- 'import/extensions': 'off',
135
- 'jsdoc/check-tag-names': [
136
- 'warn', {
137
- // for projects using typedoc
138
- definedTags: [
139
- 'notExported',
140
- 'packageDocumentation',
141
- ],
142
- },
143
- ],
144
- // Does not make sense with TypeScript
145
- 'jsdoc/require-param-type': 'off',
146
- },
147
- settings: {
148
- 'import/resolver': {
149
- node: {
150
- paths: ['src'],
151
- extensions: ['.(m|c)?js', '.ts', '.tsx', '.vue'],
152
- },
153
- },
154
- },
14
+ ...typescriptOverrides,
15
+ },
16
+ // Setup different vue parser to support `<script setup>` correctly
17
+ {
18
+ ...vueOverrides,
155
19
  },
156
20
  ],
157
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextcloud/eslint-config",
3
- "version": "8.3.0-beta.0",
3
+ "version": "8.3.0-beta.1",
4
4
  "description": "Eslint shared config for nextcloud vue.js apps",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -20,13 +20,13 @@
20
20
  "eslint": "^8.27.0",
21
21
  "eslint-config-standard": "^17.0.0",
22
22
  "eslint-import-resolver-exports": "^1.0.0-beta.4",
23
+ "eslint-import-resolver-typescript": "^3.5.5",
23
24
  "eslint-plugin-import": "^2.26.0",
24
- "eslint-plugin-jsdoc": "^43.0.3",
25
- "eslint-plugin-n": "^15.5.1",
25
+ "eslint-plugin-jsdoc": "^46.2.6",
26
+ "eslint-plugin-n": "^16.0.0",
26
27
  "eslint-plugin-promise": "^6.1.1",
27
28
  "eslint-plugin-vue": "^9.7.0",
28
- "typescript": "^5.0.2",
29
- "webpack": "^5.4.0"
29
+ "typescript": "^5.0.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@babel/core": "^7.13.10",
@@ -37,15 +37,16 @@
37
37
  "eslint": "^8.27.0",
38
38
  "eslint-config-standard": "^17.0.0",
39
39
  "eslint-import-resolver-exports": "^1.0.0-beta.4",
40
+ "eslint-import-resolver-typescript": "^3.5.5",
40
41
  "eslint-plugin-import": "^2.26.0",
41
- "eslint-plugin-jsdoc": "^43.0.3",
42
- "eslint-plugin-n": "^15.5.1",
42
+ "eslint-plugin-jsdoc": "^46.2.6",
43
+ "eslint-plugin-n": "^16.0.0",
43
44
  "eslint-plugin-promise": "^6.1.1",
44
45
  "eslint-plugin-vue": "^9.7.0",
45
46
  "jest": "^29.4.1",
46
47
  "ts-jest": "^29.0.5",
47
48
  "typescript": "^5.0.2",
48
- "webpack": "^5.4.0"
49
+ "vue-eslint-parser": "^9.3.1"
49
50
  },
50
51
  "keywords": [
51
52
  "eslint",
@@ -61,8 +62,8 @@
61
62
  },
62
63
  "homepage": "https://github.com/nextcloud/eslint-config#readme",
63
64
  "engines": {
64
- "node": "^16.0.0",
65
- "npm": "^7.0.0 || ^8.0.0"
65
+ "node": "^20.0.0",
66
+ "npm": "^9.0.0"
66
67
  },
67
68
  "jest": {
68
69
  "preset": "ts-jest",
package/parts/base.js ADDED
@@ -0,0 +1,96 @@
1
+ /** Base rules */
2
+ module.exports = {
3
+ root: true,
4
+ env: {
5
+ browser: true,
6
+ commonjs: true,
7
+ es6: true,
8
+ node: true,
9
+ // Allow jest syntax in the src folder
10
+ jest: true,
11
+ },
12
+ parser: '@babel/eslint-parser',
13
+ parserOptions: {
14
+ requireConfigFile: false,
15
+ },
16
+ extends: [
17
+ 'eslint:recommended',
18
+ 'plugin:import/errors',
19
+ 'plugin:import/warnings',
20
+ 'plugin:n/recommended',
21
+ 'plugin:@nextcloud/recommended',
22
+ 'plugin:jsdoc/recommended',
23
+ 'standard',
24
+ ],
25
+ settings: {
26
+ 'import/resolver': {
27
+ node: {
28
+ paths: ['src'],
29
+ extensions: ['.js', '.vue'],
30
+ },
31
+ exports: {
32
+ conditions: ['import'],
33
+ },
34
+ },
35
+ jsdoc: {
36
+ tagNamePreference: {
37
+ returns: 'return',
38
+ },
39
+ mode: 'typescript',
40
+ },
41
+ },
42
+ plugins: ['vue', 'n', 'jsdoc'],
43
+ rules: {
44
+ // space before function ()
45
+ 'space-before-function-paren': ['error', {
46
+ anonymous: 'never',
47
+ named: 'never',
48
+ asyncArrow: 'always',
49
+ }],
50
+ // stay consistent with array brackets
51
+ 'array-bracket-newline': ['error', 'consistent'],
52
+ // tabs only for indentation
53
+ indent: ['error', 'tab'],
54
+ 'no-tabs': ['error', { allowIndentationTabs: true }],
55
+ // allow spaces after tabs for alignment
56
+ 'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
57
+ // only debug console
58
+ 'no-console': ['error', { allow: ['error', 'warn', 'info', 'debug'] }],
59
+ // classes blocks
60
+ 'padded-blocks': ['error', { classes: 'always' }],
61
+ // always have the operator in front
62
+ 'operator-linebreak': ['error', 'before'],
63
+ // ternary on multiline
64
+ 'multiline-ternary': ['error', 'always-multiline'],
65
+ // force proper JSDocs
66
+ 'jsdoc/require-returns': 0,
67
+ 'jsdoc/require-returns-description': 0,
68
+ 'jsdoc/tag-lines': ['off'],
69
+ // disallow use of "var"
70
+ 'no-var': 'error',
71
+ // suggest using const
72
+ 'prefer-const': 'error',
73
+ // es6 import/export and require
74
+ 'n/no-unpublished-require': ['off'],
75
+ 'n/no-unsupported-features/es-syntax': ['off'],
76
+ // always add a trailing comma (for diff readability)
77
+ 'comma-dangle': ['warn', 'always-multiline'],
78
+ // Allow shallow import of @vue/test-utils and @testing-library/vue in order to be able to use it in
79
+ // the src folder
80
+ 'n/no-unpublished-import': ['error', {
81
+ allowModules: ['@vue/test-utils', '@testing-library/vue'],
82
+ }],
83
+ // require object literal shorthand syntax
84
+ 'object-shorthand': ['error', 'always'],
85
+ // Warn when file extensions are not used on import paths
86
+ 'import/extensions': ['warn', 'always', {
87
+ ignorePackages: true,
88
+ }],
89
+ // ignore camelcase for __webpack variables
90
+ camelcase: ['error', {
91
+ allow: ['^UNSAFE_', '^__webpack_'],
92
+ properties: 'never',
93
+ ignoreGlobals: true,
94
+ }],
95
+ },
96
+ }
@@ -0,0 +1,36 @@
1
+ /** Rules for typescript */
2
+ module.exports = {
3
+ files: ['**/*.ts', '**/*.cts', '**/*.mts', '**/*.tsx'],
4
+ extends: [
5
+ '@vue/eslint-config-typescript/recommended',
6
+ 'plugin:import/typescript',
7
+ ],
8
+ parser: '@typescript-eslint/parser',
9
+ parserOptions: {},
10
+ rules: {
11
+ 'n/no-missing-import': 'off',
12
+ 'import/extensions': 'off',
13
+ 'jsdoc/check-tag-names': [
14
+ 'warn', {
15
+ // for projects using typedoc
16
+ definedTags: [
17
+ 'notExported',
18
+ 'packageDocumentation',
19
+ ],
20
+ },
21
+ ],
22
+ // Does not make sense with TypeScript
23
+ 'jsdoc/require-param-type': 'off',
24
+ },
25
+ settings: {
26
+ 'import/resolver': {
27
+ typescript: {
28
+ alwaysTryTypes: true,
29
+ },
30
+ node: {
31
+ paths: ['src'],
32
+ extensions: ['.(m|c)?js', '.ts', '.tsx', '.vue'],
33
+ },
34
+ },
35
+ },
36
+ }
package/parts/vue.js ADDED
@@ -0,0 +1,42 @@
1
+
2
+ module.exports = {
3
+ files: ['**/*.vue'],
4
+ parser: 'vue-eslint-parser',
5
+ parserOptions: {
6
+ parser: '@babel/eslint-parser',
7
+ },
8
+ extends: ['plugin:vue/recommended'],
9
+ rules: {
10
+ 'vue/html-indent': ['error', 'tab'],
11
+ // PascalCase components names for vuejs
12
+ // https://vuejs.org/v2/style-guide/#Single-file-component-filename-casing-strongly-recommended
13
+ 'vue/component-name-in-template-casing': ['error', 'PascalCase'],
14
+ // force name
15
+ 'vue/match-component-file-name': ['error', {
16
+ extensions: ['jsx', 'vue', 'js'],
17
+ shouldMatchCase: true,
18
+ }],
19
+ // space before self-closing elements
20
+ 'vue/html-closing-bracket-spacing': 'error',
21
+ // no ending html tag on a new line
22
+ 'vue/html-closing-bracket-newline': ['error', { multiline: 'never' }],
23
+ // check vue files too
24
+ 'n/no-missing-import': ['error', {}],
25
+ // code spacing with attributes
26
+ 'vue/max-attributes-per-line': ['error', {
27
+ singleline: 3,
28
+ multiline: 1,
29
+ }],
30
+ 'vue/first-attribute-linebreak': ['error', {
31
+ singleline: 'beside',
32
+ multiline: 'beside',
33
+ }],
34
+ // Allow single-word components names
35
+ 'vue/multi-word-component-names': ['off'],
36
+ // custom event naming convention
37
+ 'vue/custom-event-name-casing': ['error', 'kebab-case', {
38
+ // allows custom xxxx:xxx events formats
39
+ ignores: ['/^[a-z]+(?:-[a-z]+)*:[a-z]+(?:-[a-z]+)*$/u'],
40
+ }],
41
+ },
42
+ }
package/typescript.js ADDED
@@ -0,0 +1,25 @@
1
+ const base = require('./parts/base.js')
2
+ const typescriptOverrides = require('./parts/typescript.js')
3
+ const vueOverrides = require('./parts/vue.js')
4
+
5
+ /**
6
+ * Config for projects written in Typescript + vue including vue files written in Typescript (`<script lang='ts'>`)
7
+ */
8
+ module.exports = {
9
+ ...base,
10
+ overrides: [
11
+ // Add Typescript rules also for vue files
12
+ {
13
+ ...typescriptOverrides,
14
+ files: ['**/*.ts', '**/*.tsx', '**/*.vue'],
15
+ },
16
+ // Use different parser for vue files script section
17
+ {
18
+ ...vueOverrides,
19
+ parserOptions: {
20
+ parser: '@typescript-eslint/parser',
21
+ sourceType: 'module',
22
+ },
23
+ },
24
+ ],
25
+ }
@@ -1,31 +0,0 @@
1
- import { ESLint } from "eslint"
2
- import type { Linter } from "eslint"
3
- import * as path from 'path'
4
- import * as eslintConfig from '../index.js'
5
-
6
-
7
- const eslint = new ESLint({
8
- baseConfig: eslintConfig as unknown as Linter.Config<Linter.RulesRecord>
9
- })
10
-
11
- const lintFile = async (file) => {
12
- const real = path.resolve(path.join(__dirname, file))
13
- return await eslint.lintFiles(real)
14
- }
15
-
16
- test('some basic issues should fail', async () => {
17
- const results = await lintFile('fixtures/example-fail.js')
18
- expect(results).toHaveIssueCount(3)
19
- expect(results).toHaveIssue('spaced-comment')
20
- expect(results).toHaveIssue({ ruleId: 'no-console', line: 3 })
21
- })
22
-
23
- test('TSX is linted', async () => {
24
- const ignored = await eslint.isPathIgnored('./fixtures/some.tsx')
25
- expect(ignored).toBe(false)
26
-
27
- const results = await lintFile('fixtures/some.tsx')
28
- expect(results).toHaveIssue({ruleId: 'jsdoc/check-tag-names', line: 5})
29
- expect(results).toHaveIssue({ruleId: '@typescript-eslint/no-unused-vars', line: 7})
30
- expect(results).toHaveIssueCount(2)
31
- })
package/tests/eslint.d.ts DELETED
@@ -1,11 +0,0 @@
1
- export { }
2
-
3
- declare global {
4
- namespace jest {
5
- interface Matchers<R> {
6
- toPass(): R;
7
- toHaveIssueCount(n: number): R;
8
- toHaveIssue(issue: string | {ruleId: string, line?: number}): R;
9
- }
10
- }
11
- }
@@ -1,3 +0,0 @@
1
- //should fail because of missing space on this comment
2
- // and usage of `console` and using a semicolon
3
- console.log('some');
@@ -1,7 +0,0 @@
1
- // TSX
2
-
3
- /**
4
- * @notExported <- should work
5
- * @aaabbbcccddd <- should fail
6
- */
7
- const foo = '' // <- should fail, as unused
@@ -1,128 +0,0 @@
1
- import { ESLint, Linter } from "eslint"
2
-
3
- /**
4
- * Add some custom matchers for ESLint to jest
5
- */
6
- expect.extend({
7
- toPass: assertLintingPassed,
8
- toHaveIssueCount: assertHavingNIssues,
9
- toHaveIssue: assertHavingIssue
10
- })
11
-
12
- /**
13
- * Check if linting a file did not throw any errors or warnings
14
- *
15
- * @param received Lint result
16
- */
17
- function hasNoIssues(received: ESLint.LintResult) {
18
- // dirty type check
19
- if (received?.errorCount === undefined) throw new Error('Expected ESLintResult')
20
-
21
- return received.errorCount === 0 && received.warningCount === 0
22
- }
23
-
24
- /**
25
- * Check if linting of multiple fils
26
- *
27
- * @param received
28
- * @returns {}
29
- */
30
- function assertLintingPassed(received: ESLint.LintResult | ESLint.LintResult[]) {
31
- // allow single ESLintResult
32
- if (!Array.isArray(received)) {
33
- received = [received]
34
- }
35
-
36
- const errors = [] as {file: string, errors: Linter.LintMessage[]}[]
37
- const pass = received.every((result) => {
38
- // save issues
39
- errors.push({file: result.filePath, errors: result.messages})
40
- return hasNoIssues(result)
41
- })
42
-
43
- return {
44
- pass,
45
- message: () => {
46
- if (pass) {
47
- return 'Expected file to not pass eslint, but got no issues'
48
- } else {
49
- const errorMessages = errors.map((m) =>
50
- `file: ${m.file}\n` + m.errors.map((e) => 'line: ' + e.line + ': ' + (e.ruleId || e.message))
51
- )
52
- return 'Expected file to pass eslint, got issues:\n' + errorMessages.join('\n')
53
- }
54
- }
55
- }
56
- }
57
-
58
- /**
59
- * Count the total amount of issues
60
- *
61
- * @param received lint result
62
- * @returns total amount of issues
63
- */
64
- function countIssues(received: ESLint.LintResult) {
65
- return received.errorCount + received.warningCount
66
- }
67
-
68
- /**
69
- * Check if exactly the same number of issues is reported
70
- *
71
- * @param received the lint result
72
- * @param expected number of expected issues
73
- * @returns jest matcher result
74
- */
75
- function assertHavingNIssues(received: ESLint.LintResult | ESLint.LintResult[], expected: number) {
76
- if (!(typeof expected === 'number')) throw new Error('Expected a number as expected value')
77
-
78
- if (!Array.isArray(received)) {
79
- received = [received]
80
- }
81
-
82
- const issues = received.map(countIssues).reduce((p, c) => p + c)
83
- const pass = issues === expected
84
-
85
- return {
86
- pass,
87
- message: () => pass ? `Expected not to find exactly ${expected} issues.` : `Expected ${expected} issues, found ${issues}`
88
- }
89
- }
90
-
91
- /**
92
- * Check if result contains the expected issue
93
- *
94
- * @param received the lint result
95
- * @param issue the expected issue
96
- * @returns jest matcher result
97
- */
98
- function assertHavingIssue(received: ESLint.LintResult | ESLint.LintResult[], issue: string | {ruleId: string, line?: number}) {
99
- if (!Array.isArray(received)) {
100
- received = [received]
101
- }
102
-
103
- // Should not pass
104
- if (assertLintingPassed(received).pass) {
105
- return {
106
- pass: false,
107
- message: () => 'Expected issue, but no linting issues found.'
108
- }
109
- }
110
-
111
- const name = typeof issue === 'string' ? issue : issue.ruleId
112
- const result = received.some((result) => {
113
- return result.messages.some((message) => {
114
- // ensure name matches
115
- if (message.ruleId !== name) return false
116
- // if line is requested ignore not matching ones
117
- if (typeof issue === 'object' && issue.line !== undefined && issue.line !== message.line) return false
118
- // otherwise matched
119
- return true
120
- })
121
- });
122
-
123
- const onLine = typeof issue === 'string' ? '' : ` on line ${issue.line}`
124
- return {
125
- pass: result,
126
- message: () => result ? `Unexpected error '${name}'${onLine} found.` : `Expected error '${name}'${onLine} not found.`
127
- }
128
- }