Skip to content

Commit

Permalink
Merge branch 'main' into prerelease
Browse files Browse the repository at this point in the history
  • Loading branch information
zanminkian committed Dec 4, 2024
2 parents cc0754f + db82f69 commit 73a4151
Show file tree
Hide file tree
Showing 21 changed files with 139 additions and 82 deletions.
6 changes: 6 additions & 0 deletions .changeset/cool-chicken-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fenge/eslint-config": minor
"fenge": minor
---

refactor(eslint-config): replace `override` and `extend` properties with `append` property for overriding or extending the built-in rules
5 changes: 5 additions & 0 deletions .changeset/eleven-books-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fenge/eslint-config": minor
---

feat(eslint-config): allow configuring global linter options, and disable `reportUnusedDisableDirectives` by default
5 changes: 5 additions & 0 deletions .changeset/forty-dryers-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fenge/types": minor
---

refactor(types): separate `promise-catch.d.ts` into `promise-then.d.ts` and `promise-then.d.ts`
5 changes: 5 additions & 0 deletions .changeset/lucky-months-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fenge/eslint-config": patch
---

feat(eslint-config): remove rule `no-template-curly-in-string` for flexibility
5 changes: 5 additions & 0 deletions .changeset/many-hotels-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fenge": minor
---

feat(fenge): use local ESLint and Prettier first
4 changes: 2 additions & 2 deletions packages/eslint-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ export default new Builder()
omit: ["no-var"], // these rules will not work for js files
})
.enableTypescript({
extend: {
// apply additional rules for ts files
// apply additional rules or override the built-in rules for ts files
append: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/consistent-type-assertions": [
"error",
Expand Down
14 changes: 11 additions & 3 deletions packages/eslint-config/src/config/base.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import childProcess from "node:child_process";

export interface LinterOptions {
reportUnusedDisableDirectives?: "error" | "warn" | "off";
noInlineConfig?: boolean;
}

function gitignore() {
let stdout = "";
try {
Expand All @@ -22,7 +27,10 @@ function gitignore() {
] as const;
}

export function base() {
export function base({
reportUnusedDisableDirectives = "off",
noInlineConfig = false,
}: LinterOptions = {}) {
return [
...gitignore(), // global ignore
{
Expand All @@ -33,8 +41,8 @@ export function base() {
"**/package.json",
],
linterOptions: {
// noInlineConfig: true, // too strict
reportUnusedDisableDirectives: true,
reportUnusedDisableDirectives,
noInlineConfig,
},
},
// Ignore unsupported files.
Expand Down
5 changes: 4 additions & 1 deletion packages/eslint-config/src/config/js/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const standardConfigRules = {
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
// 'no-tabs': 'error',
'no-template-curly-in-string': 'error',
// 'no-template-curly-in-string': 'error', // User who using VSCode or WebStorm can easily notice the problem. Enabling this will decrease flexibility.
'no-this-before-super': 'error',
'no-throw-literal': 'error',
// 'no-trailing-spaces': 'error',
Expand Down Expand Up @@ -493,6 +493,9 @@ export const jsBase = {
"ts-nocheck": true,
},
],
"@typescript-eslint/class-literal-property-style": "error",
"@typescript-eslint/no-this-alias": "error",
"@typescript-eslint/no-useless-empty-export": "error",
"@typescript-eslint/prefer-for-of": "error",
},
} as const;
7 changes: 2 additions & 5 deletions packages/eslint-config/src/config/ts/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export const tsBase = {
"@typescript-eslint/adjacent-overload-signatures": "error",
// "@typescript-eslint/array-type": ["error", 'array-simple'], // The default option is 'array'. Not very sure if we need to change the option. So disabled it.
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/class-literal-property-style": "error",
"@typescript-eslint/consistent-generic-constructors": "error",
"@typescript-eslint/consistent-indexed-object-style": "error",
"@typescript-eslint/consistent-type-assertions": [
Expand Down Expand Up @@ -152,7 +151,7 @@ export const tsBase = {
"@typescript-eslint/no-duplicate-type-constituents": "error",
"@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-extra-non-null-assertion": "error",
"@typescript-eslint/no-extraneous-class": "error",
// "@typescript-eslint/no-extraneous-class": "error", // Classes have only static member is reasonable sometimes.
"@typescript-eslint/no-floating-promises": [
"error",
{
Expand All @@ -172,7 +171,6 @@ export const tsBase = {
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-redundant-type-constituents": "error",
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-this-alias": "error",
// "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", // unsafe when `strictNullChecks` in tsconfig.json is disabled
"@typescript-eslint/no-unnecessary-condition": "error",
"@typescript-eslint/no-unnecessary-parameter-property-assignment": "error",
Expand All @@ -183,12 +181,11 @@ export const tsBase = {
"@typescript-eslint/no-unsafe-declaration-merging": "error",
"@typescript-eslint/no-unsafe-enum-comparison": "error",
"@typescript-eslint/no-unsafe-function-type": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/no-unsafe-return": "error", // This rule is not very perfect. See https://github.com/typescript-eslint/typescript-eslint/issues/10439
"@typescript-eslint/no-unsafe-unary-minus": "error",
"@typescript-eslint/no-wrapper-object-types": "error",
"@typescript-eslint/non-nullable-type-assertion-style": "error",
"@typescript-eslint/prefer-as-const": "error",
"@typescript-eslint/prefer-for-of": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/prefer-literal-enum-member": "error",
"@typescript-eslint/prefer-optional-chain": "error",
Expand Down
59 changes: 20 additions & 39 deletions packages/eslint-config/src/eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { base } from "./config/base.js";
import { base, type LinterOptions } from "./config/base.js";
import { javascript } from "./config/javascript.js";
import { packagejson } from "./config/packagejson.js";
import { typescript } from "./config/typescript.js";
Expand All @@ -18,20 +18,26 @@ type PkgRuleKey = keyof ReturnType<typeof packagejson>[0]["rules"];
interface Options<T extends string[]> {
pick?: NoDuplicate<T>;
omit?: NoDuplicate<T>;
extend?: Record<
string,
"error" | "warn" | "off" | ["error" | "warn", ...unknown[]]
>;
override?: Partial<
Record<
T[number],
"error" | "warn" | "off" | ["error" | "warn", ...unknown[]]
>
>;
append?:
| Partial<
Record<
T[number],
"error" | "warn" | "off" | ["error" | "warn", ...unknown[]]
>
>
| Record<
string,
"error" | "warn" | "off" | ["error" | "warn", ...unknown[]]
>;
}

export type BuilderOptions = LinterOptions;
export class Builder {
private readonly configs: object[] = [...base()];
private readonly configs: object[] = [];

constructor(options: BuilderOptions = {}) {
this.configs.push(...base(options));
}

toConfig() {
return this.configs;
Expand All @@ -42,7 +48,7 @@ export class Builder {
{ plugins: object; rules: object },
...object[],
],
{ pick, omit, extend = {}, override = {} }: Options<string[]>,
{ pick, omit, append = {} }: Options<string[]>,
) {
const select = (ruleKey: string) => {
if (!pick && !omit) {
Expand All @@ -58,33 +64,8 @@ export class Builder {
const rules = Object.fromEntries(
Object.entries(mainConfig.rules).filter(([ruleKey]) => select(ruleKey)),
);
// check `override` field
Object.keys(override).forEach((key) => {
if (!(key in rules)) {
throw new Error(`The overriding rule key ${key} is not existing.`);
}
});
// check `extend` field
Object.keys(extend).forEach((key) => {
if (key in rules) {
throw new Error(`The extending rule key ${key} is already existing.`);
}
if (key.includes("/")) {
const pluginName = key.split("/")[0];
if (!pluginName)
throw new Error(`The extending rule key '${key}' is invalid`);
if (!(pluginName in mainConfig.plugins)) {
const supportedPlugins = Object.keys(mainConfig.plugins)
.map((k) => `'${k}'`)
.join(",");
throw new Error(
`The plugin name '${pluginName}' of extending rule key '${key}' is not supported. Only ${supportedPlugins} plugins are supported.`,
);
}
}
});
this.configs.push(
{ ...mainConfig, rules: { ...rules, ...override, ...extend } },
{ ...mainConfig, rules: { ...rules, ...append } },
...otherConfigs,
);
return this;
Expand Down
5 changes: 3 additions & 2 deletions packages/eslint-plugin-publint/src/get-publint-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ import type { Pkg } from "publint/utils";
* Sync function of `(await import('publint')).publint()`.
* If publint provides sync function, this function should be deleted.
*/
function publint(pkgDir: string): Result {
function publint(pkgDir: string) {
// publint doesn't provide sync function
const publintPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
"publint.cli.js",
);
return JSON.parse(
const result: Result = JSON.parse(
childProcess.execSync(`node ${publintPath} ${pkgDir}`, {
encoding: "utf8",
}),
);
return result;
}

export interface PublintInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { noUnnecessaryTemplateString } from "./no-unnecessary-template-string.js
const valid = [
"'abc'",
'"def"',
"`ab${cd}ef`", // eslint-disable-line no-template-curly-in-string
"`ab${cd}ef`",
"`\n`",
"`abc\n`",
"`\nabc`",
Expand Down
4 changes: 2 additions & 2 deletions packages/fenge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ export default {
omit: ["no-var"], // these rules will not work for js files
})
.enableTypescript({
extend: {
// apply additional rules for ts files
// apply additional rules or override the built-in rules for ts files
append: {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/consistent-type-assertions": [
"error",
Expand Down
2 changes: 1 addition & 1 deletion packages/fenge/src/bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ program
.option("-c, --config <path>", "path to configuration file")
.option(
"--default",
"force to use built-in default config, ignore specified config and local config",
"force to use built-in default config, built-in prettier and built-in eslint. ignore specified config, local config, local prettier and local eslint",
)
.option(
"-d, --dry-run",
Expand Down
17 changes: 15 additions & 2 deletions packages/fenge/src/command/format.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function format(paths = [], options = {}) {
write = false,
dryRun = false,
config,
default: useDefaultConfig,
default: useDefaultConfig = false,
} = options;

const ignores = [".gitignore", ".prettierignore", prettierignore]
Expand All @@ -23,7 +23,7 @@ export async function format(paths = [], options = {}) {
return execAsync(
[
// "node",
await getBinPath("prettier"),
await getPrettierPath(useDefaultConfig),
...ignores,
"--log-level",
"warn",
Expand All @@ -46,3 +46,16 @@ export async function format(paths = [], options = {}) {
},
);
}

/**
* @param {boolean} useDefaultConfig
*/
async function getPrettierPath(useDefaultConfig) {
const builtinBinPath = await getBinPath("prettier");
if (useDefaultConfig) {
return builtinBinPath;
}
return await getBinPath("prettier", process.cwd()).catch(
() => builtinBinPath,
);
}
15 changes: 13 additions & 2 deletions packages/fenge/src/command/lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ export async function lint(paths = [], options = {}) {
fix = false,
dryRun = false,
config,
default: useDefaultConfig,
default: useDefaultConfig = false,
} = options;

return execAsync(
[
// "node",
await getBinPath("eslint"),
await getEslintPath(useDefaultConfig),
"--config",
path.join(dir(import.meta.url), "..", "config", "eslint.config.js"),
...(update || fix ? ["--fix"] : []),
Expand All @@ -38,3 +38,14 @@ export async function lint(paths = [], options = {}) {
},
);
}

/**
* @param {boolean} useDefaultConfig
*/
async function getEslintPath(useDefaultConfig) {
const builtinBinPath = await getBinPath("eslint");
if (useDefaultConfig) {
return builtinBinPath;
}
return await getBinPath("eslint", process.cwd()).catch(() => builtinBinPath);
}
14 changes: 11 additions & 3 deletions packages/fenge/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,18 @@ function getExitCode(error) {

/**
* @param {string} moduleName `eslint` or `prettier` or `@commitlint/cli` or `lint-staged`
* @param {string} cliName
* @param {string} from directory path or file path
*/
export async function getBinPath(moduleName, cliName = moduleName) {
const packageJsonPath = createRequire(import.meta.url).resolve(
export async function getBinPath(
moduleName,
from = fileURLToPath(import.meta.url),
) {
const fromPath =
!from.endsWith(path.sep) && (await fs.stat(from)).isDirectory()
? from + path.sep
: from;
const cliName = moduleName;
const packageJsonPath = createRequire(fromPath).resolve(
`${moduleName}/package.json`,
);
/** @type {any} */
Expand Down
6 changes: 4 additions & 2 deletions packages/types/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ A type patch for TypeScript, enhancing type-safe for built-in JavaScript apis.
- 🚨 `JSON.parse` returns `any`.
- 🚨 `new Map()` generates `Map<any, any>`.
- 🚨 `new Promise()` can reject a non `Error` variable.
- 🚨 `<promise object>.catch` accepts `(reason: any) => void | PromiseLike<void>`
- 🚨 `<promise object>.catch` accepts `(reason: any) => void | PromiseLike<void>`.
- 🚨 `<promise object>.then` accepts `(reason: any) => void | PromiseLike<void>` for the second parameter.

**With this library:**

- 👍 `Array.isArray` returns `unknown[]`.
- 👍 `JSON.parse` returns `unknown`.
- 👍 `new Map()` generates `Map<unknown, unknown>`.
- 👍 `new Promise()` must reject an `Error` variable.
- 👍 `<promise object>.catch` accepts `(reason: unknown) => void | PromiseLike<void>`
- 👍 `<promise object>.catch` accepts `(reason: unknown) => void | PromiseLike<void>`.
- 👍 `<promise object>.then` accepts `(reason: unknown) => void | PromiseLike<void>` for the second parameter.

## Usage

Expand Down
1 change: 1 addition & 0 deletions packages/types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ import "./json-parse.d.ts";
import "./new-map.d.ts";
import "./new-promise-reject.d.ts";
import "./promise-catch.d.ts";
import "./promise-then.d.ts";
Loading

0 comments on commit 73a4151

Please sign in to comment.