diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 12cd4a41..5bee9a84 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,7 @@ ### 在提交PR之前,请确保你执行以下操作: + - [ ] 曾阅读过[翻译须知](https://github.com/vitest-dev/docs-cn/issues/391)。 - [ ] 检查是否已经有PR以同样的方式解决问题,以避免创建重复。 - [ ] 在此PR中描述正在解决的问题,或引用它解决的问题(例如:`fixes #123`)。 @@ -8,7 +9,9 @@ --- ### 描述 + ### 附加上下文 + diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index e6e8d01d..e8102065 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -25,6 +25,6 @@ jobs: run: nci - name: Lint - run: nr lint --fix + run: nr lint:fix - uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc new file mode 100644 index 00000000..065c6dcb --- /dev/null +++ b/.oxfmtrc.jsonc @@ -0,0 +1,48 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "semi": false, + "singleQuote": true, + "trailingComma": "all", + "sortPackageJson": false, + "sortImports": { + "groups": [ + "type-import", + ["type-parent", "type-sibling", "type-index", "type-internal"], + "value-builtin", + "value-external", + "value-internal", + ["value-parent", "value-sibling", "value-index"], + "side_effect", + "unknown", + ], + "newlinesBetween": false, + }, + "overrides": [ + { + // code blocks in the docs are written by hand to look good + "files": ["**/*.md"], + "options": { "embeddedLanguageFormatting": "off" }, + }, + ], + "ignorePatterns": [ + "**/fixtures", + "**/*.timestamp-*", + "pnpm-lock.yaml", + // generated by the build + "packages/*/LICENSE.md", + "docs/guide/cli-generated.md", + "docs/.vitepress/contributor-names.json", + "packages/browser/src/client/public/esm-client-injector.js", + "test/unit/src/wasm/wasm-bindgen*", + // test inputs that depend on the exact source text + "test/unit/src/self", + "test/unit/test/mocking/already-hoisted.test.ts", + "test/unit/test/tab-effect.spec.mjs", + "test/unit/test/mocking/external.test.ts", + "test/workspaces/results.json", + "test/workspaces-browser/results.json", + "test/e2e/deps/error", + "test/e2e/deps/malformed-source-map", + "test/e2e/deps/optimizer/unparsable", + ], +} diff --git a/.oxlintrc.jsonc b/.oxlintrc.jsonc new file mode 100644 index 00000000..39ccff8f --- /dev/null +++ b/.oxlintrc.jsonc @@ -0,0 +1,365 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc", "import", "node", "vue", "jsdoc"], + "env": { + "browser": true, + "node": true, + }, + "options": { + "reportUnusedDisableDirectives": "error", + }, + "ignorePatterns": [ + "**/fixtures", + "**/*.d.ts", + "**/*.timestamp-*", + "packages/browser/src/client/public/esm-client-injector.js", + "test/unit/src/wasm/wasm-bindgen*", + "test/unit/src/self", + "test/unit/test/mocking/already-hoisted.test.ts", + "test/*/deps", + ], + "categories": { + "correctness": "error", + }, + "rules": { + "accessor-pairs": "error", + "array-callback-return": "error", + "block-scoped-var": "error", + "curly": ["error", "all"], + "default-case-last": "error", + "dot-notation": "error", + "eqeqeq": ["error", "smart"], + "new-cap": ["error", { "capIsNew": false }], + "no-alert": "error", + "no-array-constructor": "error", + "no-async-promise-executor": "error", + "no-caller": "error", + "no-case-declarations": "error", + "no-compare-neg-zero": "error", + "no-cond-assign": ["error", "always"], + "no-console": ["error", { "allow": ["warn", "error"] }], + "no-control-regex": "error", + "no-debugger": "error", + "no-delete-var": "error", + "no-dupe-class-members": "error", + "no-duplicate-case": "error", + "no-empty": ["error", { "allowEmptyCatch": true }], + "no-eval": "error", + "no-ex-assign": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-boolean-cast": "error", + "no-fallthrough": "error", + "no-global-assign": "error", + "no-implied-eval": "error", + "no-irregular-whitespace": "error", + "no-iterator": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-loss-of-precision": "error", + "no-misleading-character-class": "error", + "no-multi-str": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-proto": "error", + "no-prototype-builtins": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-restricted-globals": [ + "error", + { "name": "global", "message": "Use `globalThis` instead." }, + { "name": "self", "message": "Use `globalThis` instead." }, + ], + "no-restricted-imports": ["error", { "paths": ["path"] }], + "no-restricted-properties": [ + "error", + { + "property": "__proto__", + "message": "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.", + }, + { "property": "__defineGetter__", "message": "Use `Object.defineProperty` instead." }, + { "property": "__defineSetter__", "message": "Use `Object.defineProperty` instead." }, + { + "property": "__lookupGetter__", + "message": "Use `Object.getOwnPropertyDescriptor` instead.", + }, + { + "property": "__lookupSetter__", + "message": "Use `Object.getOwnPropertyDescriptor` instead.", + }, + ], + "no-self-assign": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow-restricted-names": "error", + "no-sparse-arrays": "error", + "no-template-curly-in-string": "error", + "no-throw-literal": "error", + "no-unexpected-multiline": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": ["error", { "defaultAssignment": false }], + "no-unreachable-loop": "error", + "no-unsafe-finally": "error", + "no-unused-expressions": [ + "error", + { "allowShortCircuit": true, "allowTaggedTemplates": true, "allowTernary": true }, + ], + "no-unused-vars": [ + "error", + { + "args": "after-used", + "argsIgnorePattern": "^_", + "ignoreRestSiblings": true, + "vars": "all", + "varsIgnorePattern": "^_", + }, + ], + "no-use-before-define": ["error", { "classes": false, "functions": false, "variables": true }], + "no-useless-call": "error", + "no-useless-catch": "error", + "no-useless-computed-key": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-var": "error", + "object-shorthand": ["error", "always", { "avoidQuotes": true }], + "one-var": ["error", { "initialized": "never" }], + "prefer-arrow-callback": ["error", { "allowUnboundThis": true }], + "prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true }], + "prefer-exponentiation-operator": "error", + "prefer-object-has-own": "error", + "prefer-promise-reject-errors": "error", + "prefer-regex-literals": ["error", { "disallowRedundantWrapping": true }], + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "error", + "symbol-description": "error", + "unicode-bom": "error", + "use-isnan": ["error", { "enforceForIndexOf": true, "enforceForSwitchCase": true }], + "valid-typeof": ["error", { "requireStringLiterals": true }], + "vars-on-top": "error", + "yoda": "error", + + "typescript/ban-ts-comment": ["error", { "ts-expect-error": "allow-with-description" }], + "typescript/consistent-type-definitions": ["error", "interface"], + "typescript/consistent-type-imports": [ + "error", + { + "prefer": "type-imports", + "fixStyle": "separate-type-imports", + "disallowTypeAnnotations": false, + }, + ], + "typescript/method-signature-style": ["error", "property"], + "typescript/no-duplicate-enum-values": "error", + "typescript/no-empty-object-type": ["error", { "allowInterfaces": "always" }], + "typescript/no-extra-non-null-assertion": "error", + "typescript/no-import-type-side-effects": "error", + "typescript/no-misused-new": "error", + "typescript/no-namespace": "error", + "typescript/no-non-null-asserted-nullish-coalescing": "error", + "typescript/no-non-null-asserted-optional-chain": "error", + "typescript/no-require-imports": "error", + "typescript/no-this-alias": "error", + "typescript/no-unnecessary-type-constraint": "error", + "typescript/no-unsafe-declaration-merging": "error", + "typescript/no-wrapper-object-types": "error", + "typescript/prefer-as-const": "error", + "typescript/prefer-literal-enum-member": "error", + "typescript/prefer-namespace-keyword": "error", + + "unicorn/consistent-empty-array-spread": "error", + "unicorn/error-message": "error", + "unicorn/escape-case": "error", + "unicorn/new-for-builtins": "error", + "unicorn/no-instanceof-builtins": ["error", { "strategy": "loose" }], + "unicorn/no-new-array": "error", + "unicorn/no-new-buffer": "error", + "unicorn/prefer-array-some": "error", + "unicorn/prefer-date-now": "error", + "unicorn/prefer-dom-node-text-content": "error", + "unicorn/prefer-includes": "error", + "unicorn/prefer-node-protocol": "error", + "unicorn/prefer-number-properties": ["error", { "checkInfinity": false, "checkNaN": false }], + "unicorn/prefer-regexp-test": "error", + "unicorn/prefer-string-starts-ends-with": "error", + "unicorn/prefer-type-error": "error", + "unicorn/throw-new-error": "error", + + "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + "import/first": "error", + "import/no-duplicates": "error", + "import/no-mutable-exports": "error", + "import/no-named-default": "error", + + "node/handle-callback-err": ["error", "^(err|error)$"], + "node/no-exports-assign": "error", + "node/no-new-require": "error", + "node/no-path-concat": "error", + "node/no-top-level-await": "error", + + "vue/component-definition-name-casing": "warn", + "vue/no-arrow-functions-in-watch": "error", + "vue/no-async-in-computed-properties": "error", + "vue/no-computed-properties-in-data": "error", + "vue/no-deprecated-data-object-declaration": "error", + "vue/no-deprecated-delete-set": "error", + "vue/no-deprecated-destroyed-lifecycle": "error", + "vue/no-deprecated-events-api": "error", + "vue/no-deprecated-model-definition": "error", + "vue/no-deprecated-props-default-this": "error", + "vue/no-deprecated-vue-config-keycodes": "error", + "vue/no-dupe-keys": "error", + "vue/no-export-in-script-setup": "error", + "vue/no-expose-after-await": "error", + "vue/no-lifecycle-after-await": "error", + "vue/no-multiple-slot-args": "warn", + "vue/no-required-prop-with-default": "warn", + "vue/no-reserved-component-names": "error", + "vue/no-reserved-keys": "error", + "vue/no-reserved-props": "error", + "vue/no-shared-component-data": "error", + "vue/no-side-effects-in-computed-properties": "error", + "vue/no-watch-after-await": "error", + "vue/prefer-import-from-vue": "error", + "vue/prop-name-casing": ["error", "camelCase"], + "vue/require-prop-type-constructor": "error", + "vue/require-render-return": "error", + "vue/require-slots-as-functions": "error", + "vue/return-in-computed-property": "error", + "vue/return-in-emits-validator": "error", + "vue/valid-define-emits": "error", + "vue/valid-define-options": "error", + "vue/valid-define-props": "error", + "vue/valid-next-tick": "error", + + "jsdoc/check-access": "warn", + "jsdoc/check-property-names": "warn", + "jsdoc/empty-tags": "warn", + "jsdoc/implements-on-classes": "warn", + "jsdoc/no-defaults": "warn", + "jsdoc/require-param-name": "warn", + "jsdoc/require-property": "warn", + "jsdoc/require-property-description": "warn", + "jsdoc/require-property-name": "warn", + "jsdoc/require-returns-description": "warn", + + // part of the correctness category, but not wanted here + "no-constant-condition": "off", + "no-empty-pattern": "off", + "import/default": "off", + "import/namespace": "off", + "jsdoc/check-tag-names": "off", + "jsdoc/require-yields": "off", + "typescript/no-useless-empty-export": "off", + "unicorn/no-empty-file": "off", + "unicorn/no-thenable": "off", + "unicorn/no-useless-spread": "off", + // the formatter lowercases hex digits + "unicorn/number-literal-case": "off", + }, + "overrides": [ + { + "files": ["packages/**"], + "rules": { + "no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "vitest", + "message": "Importing from vitest is restricted. Re-check the import and import the source directly instead if it's really needed.", + }, + { "name": "path", "message": "Import from pathe instead" }, + { + "name": "vitest/node", + "message": "Importing from vitest/node is restricted. Re-check the import and import the source directly instead if it's really needed.", + }, + ], + }, + ], + }, + }, + { + // these packages declare vitest as a peer dependency + "files": ["packages/{coverage-*,ui,browser,web-worker,browser-*}/**"], + "rules": { + "no-restricted-imports": ["error", { "paths": ["path"] }], + }, + }, + { + "files": ["packages/browser/src/client/orchestrator.ts"], + "rules": { + "no-restricted-imports": ["error", { "paths": ["vitest/internal/browser", "vitest/node"] }], + }, + }, + { + // ivya must stay in a single rollup chunk, see the browser rollup config + "files": ["packages/browser/**"], + "excludeFiles": [ + "packages/browser/src/vendor-types.ts", + "packages/browser/src/client/tester/aria.ts", + "packages/browser/src/client/tester/locators.ts", + "packages/browser/src/client/tester/expect/**", + "packages/browser/src/client/utils.ts", + ], + "rules": { + "no-restricted-imports": ["error", { "paths": ["ivya", "ivya/utils", "ivya/aria"] }], + }, + }, + { + "files": ["test/unit/**"], + "rules": { + "no-restricted-imports": [ + "error", + { + "paths": ["path"], + "patterns": [ + { + "group": ["**/test-utils", "**/test-utils/**"], + "importNames": ["runVitest", "runInlineTests", "runVitestCli"], + "message": "Tests that start Vitest belong in test/e2e.", + }, + ], + }, + ], + }, + }, + { + "files": ["docs/**", "packages/web-worker/**", "test/unit/**"], + "rules": { + "no-restricted-globals": "off", + }, + }, + { + "files": ["docs/**"], + "rules": { + "prefer-arrow-callback": "off", + "typescript/method-signature-style": "off", + "no-self-compare": "off", + "no-throw-literal": "off", + "no-unused-vars": "off", + "import/no-duplicates": "off", + "import/no-mutable-exports": "off", + }, + }, + { + "files": ["test/**", "scripts/**", "docs/**", "examples/**", "**/*.config.*"], + "rules": { + "node/no-top-level-await": "off", + }, + }, + { + "files": ["**/scripts/**", "**/cli.*", "**/cli/**"], + "rules": { + "no-console": "off", + }, + }, + { + "files": ["**/*.cjs"], + "rules": { + "typescript/no-require-imports": "off", + }, + }, + ], +} diff --git a/.vitepress/blog.data.ts b/.vitepress/blog.data.ts index 8aa4dc4a..d9799a45 100644 --- a/.vitepress/blog.data.ts +++ b/.vitepress/blog.data.ts @@ -17,8 +17,7 @@ export default createContentLoader('blog/*.md', { transform(raw): Post[] { return raw .map(({ url, frontmatter }) => ({ - title: frontmatter.head.find((e: any) => e[1].property === 'og:title')[1] - .content, + title: frontmatter.head.find((e: any) => e[1].property === 'og:title')[1].content, url, date: formatDate(frontmatter.date), })) diff --git a/.vitepress/components/Advanced.vue b/.vitepress/components/Advanced.vue index 58ef886d..6a784df3 100644 --- a/.vitepress/components/Advanced.vue +++ b/.vitepress/components/Advanced.vue @@ -1,5 +1,9 @@ diff --git a/.vitepress/components/BlogIndex.vue b/.vitepress/components/BlogIndex.vue index b702f161..95244ffb 100644 --- a/.vitepress/components/BlogIndex.vue +++ b/.vitepress/components/BlogIndex.vue @@ -10,9 +10,7 @@ function getDateTime(time: number) {