Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 0 additions & 53 deletions .eslintrc.cjs

This file was deleted.

6 changes: 3 additions & 3 deletions babel.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module.exports = {
env: {
test: {
presets: ['@babel/preset-env', '@babel/preset-typescript'],
plugins: ['@babel/plugin-transform-modules-commonjs']
}
}
plugins: ['@babel/plugin-transform-modules-commonjs'],
},
},
};
9 changes: 7 additions & 2 deletions bin/create-release-branch.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#!/usr/bin/env node
/* eslint-disable import/extensions */

// eslint-disable-next-line import/no-unassigned-import, import/no-unresolved
// Three things:
// - This file doesn't export anything, as it's a script.
// - We are using a `.js` extension because that's what appears in `dist/`.
// - This file will only exist after running `yarn build`. We don't want
// developers or CI to receive a lint error if the script has not been run.
// (A warning will appear if the script *has* been run, but that is okay.)
// eslint-disable-next-line import-x/no-unassigned-import, import-x/extensions, import-x/no-unresolved
import '../dist/cli.js';
131 changes: 131 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import base, { createConfig } from '@metamask/eslint-config';
import jest from '@metamask/eslint-config-jest';
import nodejs from '@metamask/eslint-config-nodejs';
import typescript from '@metamask/eslint-config-typescript';

const config = createConfig([
{
ignores: ['dist/', 'docs/', '.yarn/'],
},

{
extends: base,

languageOptions: {
sourceType: 'module',
parserOptions: {
tsconfigRootDir: import.meta.dirname,
},
},

rules: {
// Consider copying this to @metamask/eslint-config
'jsdoc/require-jsdoc': [
Copy link
Contributor Author

@mcmire mcmire Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated in the PR description, I found the new changes to this rule in MetaMask/eslint-config#394 rather strict. I removed TSPropertySignature as it began asking that properties in adhoc object types were documented. For instance:

function foo(): {
  // This is now required to be documented
  bar: string;
} {
  // ...
}

I also found a similar thing for arrow functions:

// The arrow function here is now required to be documented
foo(() => {
  // ...
})


foo({
  // The arrow function here is now required to be documented
  bar: () => {
    // ...
  }
})

So, I amended what this rule is looking for, which is explained in the comments below. I feel like this makes more sense, but happy to know y'all's thoughts.

Copy link
Contributor Author

@mcmire mcmire Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, I used https://astexplorer.net/ to test out the selectors (make sure to select @typescript-eslint/parser and Transform > ESLint v8).

'error',
{
require: {
// Classes
ClassDeclaration: true,
// Function declarations
FunctionDeclaration: true,
// Methods
MethodDefinition: true,
},
contexts: [
// Type interfaces that are not defined within `declare` blocks
':not(TSModuleBlock) > TSInterfaceDeclaration',
// Type aliases
'TSTypeAliasDeclaration',
// Enums
'TSEnumDeclaration',
// Arrow functions that are not contained within plain objects or
// are not arguments to functions or methods
':not(Property, NewExpression, CallExpression) > ArrowFunctionExpression',
// Function expressions that are not contained within plain objects
// or are not arguments to functions or methods
':not(Property, NewExpression, CallExpression) > FunctionExpression',
// Exported variables at the root
'ExportNamedDeclaration:has(> VariableDeclaration)',
],
},
],
// Consider copying this to @metamask/eslint-config
'jsdoc/no-blank-blocks': 'error',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that this existed and I found this to be a helpful rule. It seemed that with the new JSDoc rules, running yarn eslint --fix added a bunch of empty JSDoc blocks to various symbols. With this enabled, I was able to quickly find those and fill them in.

},

settings: {
'import-x/extensions': ['.js', '.mjs'],
},
},

{
files: ['**/*.ts'],
extends: typescript,
rules: {
// Consider copying this to @metamask/eslint-config
'@typescript-eslint/explicit-function-return-type': [
'error',
{
allowExpressions: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I found that this new rule was too strict. For instance:

return {
  // A return type is now required for this function
  message: () => 'foo',
  pass: false,
}

},
],
// Consider copying this to @metamask/eslint-config
'jsdoc/require-jsdoc': [
'error',
{
require: {
// Classes
ClassDeclaration: true,
// Function declarations
FunctionDeclaration: true,
// Methods
MethodDefinition: true,
},
contexts: [
// Type interfaces that are not defined within `declare` blocks
':not(TSModuleBlock) > TSInterfaceDeclaration',
// Type aliases
'TSTypeAliasDeclaration',
// Enums
'TSEnumDeclaration',
// Arrow functions that are not contained within plain objects or
// are not arguments to functions or methods
':not(Property, NewExpression, CallExpression) > ArrowFunctionExpression',
// Function expressions that are not contained within plain objects
// or are not arguments to functions or methods
':not(Property, NewExpression, CallExpression) > FunctionExpression',
// Exported variables at the root
'ExportNamedDeclaration:has(> VariableDeclaration)',
],
},
],
// Consider copying this to @metamask/eslint-config
'jsdoc/no-blank-blocks': 'error',
},
},

{
files: ['**/*.js', '**/*.cjs', '**/*.ts', '**/*.test.ts', '**/*.test.js'],
ignores: ['src/ui/**'],
extends: nodejs,
},

{
files: ['**/*.test.ts'],
extends: jest,
},

// List this last to override any settings inherited from plugins,
// especially `eslint-config-n`, which mistakenly assumes that all `.cjs`
// files are modules (since we specified `type: module` in `package.json`)
{
files: ['**/*.js', '**/*.cjs'],
// This *is* a script, but is written using ESM.
ignores: ['bin/create-release-branch.js'],
languageOptions: {
sourceType: 'script',
},
},
]);

export default config;
29 changes: 17 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"build:ui": "vite build",
"build:clean": "rimraf dist && yarn build",
"lint": "yarn lint:eslint && yarn lint:misc --check",
"lint:eslint": "eslint . --cache --ext js,ts",
"lint:eslint": "eslint .",
"lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write",
"lint:misc": "prettier '**/*.json' '**/*.md' '!CHANGELOG.md' '**/*.yml' '!.yarnrc.yml' --ignore-path .gitignore --no-error-on-unmatched-pattern",
"prepack": "./scripts/prepack.sh",
Expand Down Expand Up @@ -46,10 +46,10 @@
"@babel/preset-env": "^7.23.5",
"@babel/preset-typescript": "^7.23.3",
"@lavamoat/allow-scripts": "^3.1.0",
"@metamask/eslint-config": "^10.0.0",
"@metamask/eslint-config-jest": "^10.0.0",
"@metamask/eslint-config-nodejs": "^10.0.0",
"@metamask/eslint-config-typescript": "^10.0.0",
"@metamask/eslint-config": "^15.0.0",
"@metamask/eslint-config-jest": "^15.0.0",
"@metamask/eslint-config-nodejs": "^15.0.0",
"@metamask/eslint-config-typescript": "^15.0.0",
"@tailwindcss/vite": "^4.0.9",
"@types/debug": "^4.1.7",
"@types/express": "^5.0.0",
Expand All @@ -63,18 +63,21 @@
"@types/validate-npm-package-name": "^4.0.2",
"@types/which": "^3.0.0",
"@types/yargs": "^17.0.10",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vitejs/plugin-react": "^4.3.4",
"babel-jest": "^29.7.0",
"deepmerge": "^4.2.2",
"eslint": "^8.27.0",
"eslint": "^9.21.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.9.0",
"eslint-plugin-jsdoc": "^39.6.2",
"eslint-import-resolver-typescript": "^3.8.3",
"eslint-plugin-import-x": "^4.6.1",
"eslint-plugin-jest": "^28.11.0",
"eslint-plugin-jsdoc": "^50.6.3",
"eslint-plugin-n": "^17.15.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^7.2.1",
"jest": "^29.7.0",
"jest-it-up": "^3.0.0",
"jest-when": "^3.5.2",
Expand All @@ -89,6 +92,7 @@
"tailwindcss": "^4.0.9",
"tsx": "^4.6.1",
"typescript": "~5.1.6",
"typescript-eslint": "^8.49.0",
"vite": "^6.2.0"
},
"peerDependencies": {
Expand All @@ -106,7 +110,8 @@
"allowScripts": {
"@lavamoat/preinstall-always-fail": false,
"tsx>esbuild": false,
"vite>esbuild": false
"vite>esbuild": false,
"eslint-plugin-import-x>unrs-resolver": false
}
}
}
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { main } from './main.js';
/**
* The entrypoint to this tool.
*/
async function cli() {
async function cli(): Promise<void> {
await main({
argv: process.argv,
cwd: process.cwd(),
Expand Down
5 changes: 4 additions & 1 deletion src/command-line-arguments.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
import yargs from 'yargs/yargs';

/**
* The set of positional and named arguments that can be passed to this tool.
*/
export type CommandLineArguments = {
projectDirectory: string;
tempDirectory: string | undefined;
Expand Down
9 changes: 3 additions & 6 deletions src/dirname.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
import { fileURLToPath } from 'url';

/**
* Get the current directory path.
*
* @returns The current directory path.
*/
export function getCurrentDirectoryPath() {
return __dirname;
export function getCurrentDirectoryPath(): string {
return dirname(fileURLToPath(import.meta.url));
}
1 change: 1 addition & 0 deletions src/editor.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { when } from 'jest-when';

import { determineEditor } from './editor.js';
import * as envModule from './env.js';
import * as miscUtils from './misc-utils.js';
Expand Down
13 changes: 8 additions & 5 deletions src/editor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { getErrorMessage } from '@metamask/utils';

import { getEnvironmentVariables } from './env.js';
import { debug, resolveExecutable } from './misc-utils.js';

/**
* Information about the editor present on the user's computer.
*
* @property path - The path to the executable representing the editor.
* @property args - Command-line arguments to pass to the executable when
* calling it.
* Properties:
Copy link
Contributor Author

@mcmire mcmire Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@property is now no longer allowed as a tag. I wanted a consistent way that we could document properties going forward, accounting for intersections or unions, so I made up this format. Let me know what you think.

*
* - `path` - The path to the executable representing the editor.
* - `args` - Command-line arguments to pass to the executable when calling it.
*/
export type Editor = {
path: string;
Expand All @@ -31,7 +34,7 @@ export async function determineEditor(): Promise<Editor | null> {
executablePath = await resolveExecutable(EDITOR);
} catch (error) {
debug(
`Could not resolve executable ${EDITOR} (${error}), falling back to VSCode`,
`Could not resolve executable ${EDITOR} (${getErrorMessage(error)}), falling back to VSCode`,
);
}
}
Expand All @@ -43,7 +46,7 @@ export async function determineEditor(): Promise<Editor | null> {
executableArgs.push('--wait');
} catch (error) {
debug(
`Could not resolve path to VSCode: ${error}, continuing regardless`,
`Could not resolve path to VSCode: ${getErrorMessage(error)}, continuing regardless`,
);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/env.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// This file tests a file that is concerned with accessing environment
// variables.
/* eslint-disable n/no-process-env */

import { getEnvironmentVariables } from './env.js';

describe('env', () => {
Expand Down
9 changes: 9 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
// This file tests a file that is concerned with accessing environment
// variables.
/* eslint-disable n/no-process-env */

/**
* Environment variables that this tool uses.
*/
type Env = {
// Environment variables are uppercase by convention.
// eslint-disable-next-line @typescript-eslint/naming-convention
EDITOR: string | undefined;
};

Expand Down
Loading
Loading