Compare commits
60 Commits
@eslit/cor
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e90e3533 | ||
|
|
e28cacfbd6 | ||
|
|
7d663d7ddf | ||
|
|
db8bcda886 | ||
|
|
75f08247b2 | ||
|
|
6fda82d3bc | ||
|
|
64dc504e2a | ||
|
|
a830ec71bd | ||
|
|
13e517964c | ||
|
|
4792b485d6 | ||
|
|
568bdb5d97 | ||
|
|
9a9ffc1a04 | ||
|
|
5eb7eac8ab | ||
|
|
983d4958f2 | ||
|
|
978f06605e | ||
|
|
1b2891b7ee | ||
|
|
4384f6143a | ||
|
|
107be3d4ab | ||
|
|
95ad4abf9f | ||
|
|
d2adda8aeb | ||
|
|
e775d83ccf | ||
|
|
c201a25e6e | ||
|
|
b0e00d6e5c | ||
|
|
73b71033b3 | ||
|
|
f24aba4f8e | ||
|
|
8c1e721346 | ||
|
|
c1f4c262dc | ||
|
|
f7b6faff09 | ||
|
|
48b70de8d9 | ||
|
|
fdad363313 | ||
|
|
1296891431 | ||
|
|
03a9ce3de5 | ||
|
|
5752e76197 | ||
|
|
86c178419c | ||
|
|
41fd41bef6 | ||
|
|
6f1fca2513 | ||
|
|
48f20e94f7 | ||
|
|
e1747ee696 | ||
|
|
5fbc250edd | ||
|
|
2f75775f43 | ||
|
|
9bc8b4fc5b | ||
|
|
c647e6020b | ||
|
|
53e44d5126 | ||
|
|
a6dc972a5c | ||
|
|
5aaaa9ac0e | ||
|
|
ac9bfbd232 | ||
|
|
4533557f4f | ||
|
|
a1e23fa757 | ||
|
|
89f7fc83eb | ||
|
|
b6dc308639 | ||
|
|
e80455e2d4 | ||
|
|
428fdffaf4 | ||
|
|
a474f7277d | ||
|
|
f6c47d14f6 | ||
|
|
3fda9b9156 | ||
|
|
2246103ae6 | ||
|
|
2178a36f9b | ||
|
|
971bbcaa80 | ||
|
|
edb86e3928 | ||
|
|
855b101292 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
|
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
|
||||||
"changelog": "@changesets/cli/changelog",
|
"changelog": ["@svitejs/changesets-changelog-github-compact", { "repo": "loreddev/eslit" }],
|
||||||
"commit": false,
|
"commit": false,
|
||||||
"fixed": [],
|
"fixed": [],
|
||||||
"linked": [],
|
"linked": [],
|
||||||
|
|||||||
2
.github/workflows/release-branch.yml
vendored
2
.github/workflows/release-branch.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Update dev branch
|
- name: Update dev branch
|
||||||
if: steps.changesets.outputs.hasChangesets == 'false'
|
if: ${{ steps.changesets.outputs.hasChangesets == 'false' && github.ref_name == 'main'}}
|
||||||
uses: devmasx/merge-branch@master
|
uses: devmasx/merge-branch@master
|
||||||
with:
|
with:
|
||||||
type: now
|
type: now
|
||||||
|
|||||||
97
.github/workflows/renovate-changesets.yml
vendored
Normal file
97
.github/workflows/renovate-changesets.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Credits to https://github.com/backstage/backstage/blob/master/.github/workflows/sync_renovate-changesets.yml
|
||||||
|
|
||||||
|
name: "📦 Renovate Changesets"
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/renovate-changesets.yml'
|
||||||
|
- '**/pnpm-lock.yaml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
generate-changeset:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.actor == 'renovate[bot]' && github.repository == 'loreddev/eslit'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config --global user.email action@github.io
|
||||||
|
git config --global user.name 'Github changeset workflow'
|
||||||
|
- name: Generate changeset
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { promises: fs } = require("fs");
|
||||||
|
// Parses package.json files and returns the package names
|
||||||
|
async function getPackagesNames(files) {
|
||||||
|
const names = [];
|
||||||
|
for (const file of files) {
|
||||||
|
const data = JSON.parse(await fs.readFile(file, "utf8"));
|
||||||
|
if (!data.private) {
|
||||||
|
names.push(data.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createChangeset(fileName, packageBumps, packages) {
|
||||||
|
let message = "";
|
||||||
|
for (const [pkg, bump] of packageBumps) {
|
||||||
|
message = message + `Updated dependency \`${pkg}\` to \`${bump}\`.\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkgs = packages.map((pkg) => `'${pkg}': patch`).join("\n");
|
||||||
|
const body = `---\n${pkgs}\n---\n\n${message.trim()}\n`;
|
||||||
|
await fs.writeFile(fileName, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBumps(files) {
|
||||||
|
const bumps = new Map();
|
||||||
|
for (const file of files) {
|
||||||
|
const { stdout: changes } = await exec.getExecOutput("git", [
|
||||||
|
"show",
|
||||||
|
file,
|
||||||
|
]);
|
||||||
|
for (const change of changes.split("\n")) {
|
||||||
|
if (!change.startsWith("+ ")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const match = change.match(/"(.*?)"/g);
|
||||||
|
bumps.set(match[0].replace(/"/g, ""), match[1].replace(/"/g, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bumps;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branch = await exec.getExecOutput("git branch --show-current");
|
||||||
|
if (!branch.stdout.startsWith("renovate/")) {
|
||||||
|
console.log("Not a renovate branch, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const diffOutput = await exec.getExecOutput("git diff --name-only HEAD~1");
|
||||||
|
const diffFiles = diffOutput.stdout.split("\n");
|
||||||
|
if (diffFiles.find((f) => f.startsWith(".changeset"))) {
|
||||||
|
console.log("Changeset already exists, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const files = diffFiles
|
||||||
|
.filter((file) => file !== "package.json") // skip root package.json
|
||||||
|
.filter((file) => file.includes("package.json"));
|
||||||
|
const packageNames = await getPackagesNames(files);
|
||||||
|
if (!packageNames.length) {
|
||||||
|
console.log("No package.json changes to published packages, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { stdout: shortHash } = await exec.getExecOutput(
|
||||||
|
"git rev-parse --short HEAD"
|
||||||
|
);
|
||||||
|
const fileName = `.changeset/renovate-${shortHash.trim()}.md`;
|
||||||
|
|
||||||
|
const packageBumps = await getBumps(files);
|
||||||
|
await createChangeset(fileName, packageBumps, packageNames);
|
||||||
|
await exec.exec("git", ["add", fileName]);
|
||||||
|
await exec.exec("git commit -C HEAD --amend --no-edit");
|
||||||
|
await exec.exec("git push --force");
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
ESLINT_USE_FLAT_CONFIG=true
|
|
||||||
|
|
||||||
pnpm dlx commitlint --edit ${1}
|
pnpm dlx commitlint --edit ${1}
|
||||||
pnpm dlx lint-staged
|
pnpm dlx lint-staged
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -21,5 +21,8 @@
|
|||||||
"json",
|
"json",
|
||||||
"jsonc",
|
"jsonc",
|
||||||
"yaml"
|
"yaml"
|
||||||
|
],
|
||||||
|
"cSpell.words": [
|
||||||
|
"ESLIT"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 [fullname]
|
Copyright (c) 2023-present Gustavo "Guz" L. de Mello <contact.guz013@gmail.com> (https://guz.one)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { defineConfig } from '@eslit/core';
|
import { configs, defineConfig, presets } from '@eslit/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig([
|
||||||
environment: {
|
...presets.default,
|
||||||
node: true,
|
configs.environments.node,
|
||||||
},
|
]);
|
||||||
});
|
|
||||||
|
|||||||
8
fixtures/library/CHANGELOG.md
Normal file
8
fixtures/library/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# @eslit-fixtures/library
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`1296891`](https://github.com/LoredDev/ESLit/commit/1296891431117c9a386d36d84c8d402013c3a094)]:
|
||||||
|
- @eslit/cli@0.1.0
|
||||||
16
fixtures/library/package.json
Normal file
16
fixtures/library/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@eslit-fixtures/library",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test": "pnpm cli"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@eslit/cli": "workspace:*"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
36
fixtures/monorepo/.gitignore
vendored
Normal file
36
fixtures/monorepo/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
.next/
|
||||||
|
out/
|
||||||
|
build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# turbo
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
15
fixtures/monorepo/package.json
Normal file
15
fixtures/monorepo/package.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test:cli": "pnpm cli"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslit/cli": "workspace:*"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@8.6.10",
|
||||||
|
"name": "monorepo",
|
||||||
|
"workspaces": [
|
||||||
|
"apps/*",
|
||||||
|
"packages/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -7,9 +7,11 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"lint": "ESLINT_USE_FLAT_CONFIG=true eslint ."
|
"test:lint": "eslint .",
|
||||||
|
"test:cli": "pnpm cli"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslit/cli": "workspace:*",
|
||||||
"@fontsource/fira-mono": "^4.5.10",
|
"@fontsource/fira-mono": "^4.5.10",
|
||||||
"@neoconfetti/svelte": "^1.0.0",
|
"@neoconfetti/svelte": "^1.0.0",
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -3,21 +3,22 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"lint:monorepo": "ESLINT_USE_FLAT_CONFIG=true eslint .",
|
"lint:monorepo": "eslint .",
|
||||||
"test": "",
|
"test": "echo \"NO TESTS CONFIGURED\"",
|
||||||
"release": "changeset publish",
|
"release": "changeset publish",
|
||||||
"prepare": "husky install"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"author": "",
|
"license": "MIT",
|
||||||
"license": "ISC",
|
"type": "module",
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslit/core": "workspace:*"
|
"@eslit/config": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslit/cli": "workspace:*",
|
||||||
"@changesets/cli": "^2.26.2",
|
"@changesets/cli": "^2.26.2",
|
||||||
"@commitlint/config-conventional": "^17.6.6",
|
"@commitlint/config-conventional": "^17.6.6",
|
||||||
"@commitlint/types": "^17.4.4",
|
"@commitlint/types": "^17.4.4",
|
||||||
|
"@svitejs/changesets-changelog-github-compact": "^1.1.0",
|
||||||
"eslint": "^8.44.0",
|
"eslint": "^8.44.0",
|
||||||
"husky": "^8.0.0",
|
"husky": "^8.0.0",
|
||||||
"turbo": "^1.10.9"
|
"turbo": "^1.10.9"
|
||||||
|
|||||||
7
packages/cli/CHANGELOG.md
Normal file
7
packages/cli/CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# @eslit/cli
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- Now the cli can automatically detect the workspace structure on monorepos and single repositories ([#10](https://github.com/LoredDev/ESLit/pull/10))
|
||||||
49
packages/cli/package.json
Normal file
49
packages/cli/package.json
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "@eslit/cli",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"lint": "eslint ."
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": {
|
||||||
|
"email": "contact.guz013@gmail.com",
|
||||||
|
"name": "Gustavo \"Guz\" L. de Mello",
|
||||||
|
"url": "https://guz.one"
|
||||||
|
},
|
||||||
|
"module": "./src/index.js",
|
||||||
|
"source": "./src/index.js",
|
||||||
|
"files": [
|
||||||
|
"src",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/LoredDev/ESLit",
|
||||||
|
"type": "module",
|
||||||
|
"repository": {
|
||||||
|
"directory": "packages/config",
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/LoredDev/ESLit"
|
||||||
|
},
|
||||||
|
"bin": "./src/index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cardinal": "^2.1.1",
|
||||||
|
"commander": "^11.0.0",
|
||||||
|
"nanospinner": "^1.1.0",
|
||||||
|
"picocolors": "^1.0.0",
|
||||||
|
"picomatch": "^2.3.1",
|
||||||
|
"prompts": "^2.4.2",
|
||||||
|
"recast": "^0.23.3",
|
||||||
|
"sisteransi": "^1.0.5",
|
||||||
|
"yaml": "^2.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/estree": "^1.0.1",
|
||||||
|
"@types/node": "^20.4.2",
|
||||||
|
"@types/prompts": "^2.4.4"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
155
packages/cli/src/cli.js
Normal file
155
packages/cli/src/cli.js
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import { Command } from 'commander';
|
||||||
|
import ConfigsProcessor from './configsProcessor.js';
|
||||||
|
import configs from './configs.js';
|
||||||
|
import Workspace from './workspace.js';
|
||||||
|
import c from 'picocolors';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { createSpinner } from 'nanospinner';
|
||||||
|
import count from './lib/count.js';
|
||||||
|
import prompts from 'prompts';
|
||||||
|
import ConfigsFile from './configsFile.js';
|
||||||
|
import * as cardinal from 'cardinal';
|
||||||
|
import ansi from 'sisteransi';
|
||||||
|
import PackageInstaller from './packageInstaller.js';
|
||||||
|
import notNull from './lib/notNull.js';
|
||||||
|
|
||||||
|
const stdout = process.stdout;
|
||||||
|
|
||||||
|
export default class Cli {
|
||||||
|
|
||||||
|
#program = new Command();
|
||||||
|
|
||||||
|
/** @type {import('./types').CliArgs} */
|
||||||
|
args = {
|
||||||
|
dir: process.cwd(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').CliArgs} [args] Cli arguments object
|
||||||
|
*/
|
||||||
|
constructor(args) {
|
||||||
|
this.#program
|
||||||
|
.option('--packages <string...>')
|
||||||
|
.option('--dir <path>', undefined)
|
||||||
|
.option('--merge-to-root')
|
||||||
|
.option('--install-pkgs')
|
||||||
|
.parse();
|
||||||
|
|
||||||
|
this.args = {
|
||||||
|
...this.args,
|
||||||
|
...this.#program.opts(),
|
||||||
|
...args,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.args.dir = !this.args.dir.startsWith('/')
|
||||||
|
? path.join(process.cwd(), this.args.dir)
|
||||||
|
: this.args.dir;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
|
||||||
|
process.chdir(this.args.dir);
|
||||||
|
|
||||||
|
const spinner = createSpinner('Detecting workspace configuration');
|
||||||
|
|
||||||
|
const processor = new ConfigsProcessor({ configs });
|
||||||
|
const workspace = new Workspace(this.args.dir, this.args?.packages);
|
||||||
|
|
||||||
|
let packages = (await workspace.getPackages())
|
||||||
|
.map(pkg => {
|
||||||
|
spinner.update({ text: `Detecting configuration for package ${c.bold(c.blue(pkg.name))}` });
|
||||||
|
|
||||||
|
pkg.config = processor.detectConfig(pkg);
|
||||||
|
|
||||||
|
return pkg;
|
||||||
|
});
|
||||||
|
|
||||||
|
spinner.success({
|
||||||
|
text:
|
||||||
|
'Detecting workspace configuration ' +
|
||||||
|
c.dim(`${count.packagesWithConfigs(packages)} configs founded\n`),
|
||||||
|
});
|
||||||
|
|
||||||
|
const merge = this.args.mergeToRoot ?? packages.length > 1 ?
|
||||||
|
/** @type {{merge: boolean}} */
|
||||||
|
(await prompts({
|
||||||
|
name: 'merge',
|
||||||
|
message:
|
||||||
|
`Would you like to merge all configuration files into one root ${c.blue('eslint.config.js?')}` +
|
||||||
|
c.italic(c.dim('\nAll configurations will be applied to the entire workspace and packages')),
|
||||||
|
initial: true,
|
||||||
|
type: 'confirm',
|
||||||
|
})).merge : true;
|
||||||
|
|
||||||
|
console.log(c.dim('\nPlease select which options you prefer\n'));
|
||||||
|
|
||||||
|
packages = await processor.questionConfig(
|
||||||
|
merge ? workspace.mergePackages(packages) : packages,
|
||||||
|
configs.filter(c => c.manual),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileHandler = new ConfigsFile(configs, packages.find(c => c.root)?.path);
|
||||||
|
|
||||||
|
for (const pkg of packages) {
|
||||||
|
|
||||||
|
pkg.configFile = fileHandler.generateObj(pkg);
|
||||||
|
pkg.configFile.content = await fileHandler.generate(pkg.configFile);
|
||||||
|
|
||||||
|
/** @type {boolean} */
|
||||||
|
const shouldWrite =
|
||||||
|
/** @type {{write: boolean}} */
|
||||||
|
(await prompts({
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'write',
|
||||||
|
message: `Do you want to write this config file for ${pkg.root
|
||||||
|
? c.blue('the root directory')
|
||||||
|
: c.blue(pkg.name)
|
||||||
|
}?\n\n${cardinal.highlight(pkg.configFile.content)}`,
|
||||||
|
initial: true,
|
||||||
|
})).write;
|
||||||
|
|
||||||
|
stdout.write(ansi.erase.lines(pkg.configFile.content.split('\n').length + 2));
|
||||||
|
|
||||||
|
if (shouldWrite) await fileHandler.write(pkg.configFile.path, pkg.configFile.content);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const packagesMap = new Map(packages.map(p => [p.path, [...notNull(p.configFile).imports.keys()]]));
|
||||||
|
const installer = new PackageInstaller(packagesMap, packages.find(p => p.root === true)?.path ?? this.args.dir);
|
||||||
|
|
||||||
|
/** @type {boolean | 'changePackage'} */
|
||||||
|
let installPkgs = this.args.installPkgs !== undefined ? true :
|
||||||
|
/** @type {{install: boolean | 'changePackage'}} */
|
||||||
|
(await prompts({
|
||||||
|
name: 'install',
|
||||||
|
message:
|
||||||
|
`Would you like to ESLit to install the npm packages with ${c.green(installer.packageManager.name)}?`,
|
||||||
|
choices: [
|
||||||
|
{ title: 'Yes, install all packages', value: true, description: installer.packageManager.description },
|
||||||
|
{ title: 'No, I will install them manually', value: false },
|
||||||
|
{ title: 'Change package manager', value: 'changePackage' },
|
||||||
|
],
|
||||||
|
type: 'select',
|
||||||
|
})).install;
|
||||||
|
|
||||||
|
if (installPkgs === 'changePackage') {
|
||||||
|
/** @type {{manager: import('./types').PackageManagerName}} */
|
||||||
|
const prompt = await prompts({
|
||||||
|
name: 'manager',
|
||||||
|
message: 'What package manager do you want ESLit to use?',
|
||||||
|
choices: Object.values(installer.packageManagers).map(m => {
|
||||||
|
return { title: m.name, description: m.description, value: m.name };
|
||||||
|
}),
|
||||||
|
type: 'select',
|
||||||
|
});
|
||||||
|
installer.packageManager = installer.packageManagers[prompt.manager];
|
||||||
|
installPkgs = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installPkgs) await installer.install();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
33
packages/cli/src/configs.js
Normal file
33
packages/cli/src/configs.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
/** @type {import('./types').Config[]} */
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: 'framework',
|
||||||
|
type: 'multiple',
|
||||||
|
description: 'The UI frameworks being used in the project',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'svelte',
|
||||||
|
packages: { 'svelte': 'svelte' },
|
||||||
|
configs: ['svelte.recommended'],
|
||||||
|
detect: ['**/*.svelte', 'svelte.config.{js,ts,cjs,cts}'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vue',
|
||||||
|
packages: { 'vue': ['vue', ['hello', 'world']], 'svelte': ['hello'] },
|
||||||
|
configs: ['vue.recommended'],
|
||||||
|
detect: ['nuxt.config.{js,ts,cjs,cts}', '**/*.vue'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'strict',
|
||||||
|
type: 'confirm',
|
||||||
|
manual: true,
|
||||||
|
options: [{
|
||||||
|
name: 'yes',
|
||||||
|
packages: { 'eslint': 'config', 'svelte': ['test1'] },
|
||||||
|
configs: ['config.strict'],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
];
|
||||||
344
packages/cli/src/configsFile.js
Normal file
344
packages/cli/src/configsFile.js
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import notNull from './lib/notNull.js';
|
||||||
|
import * as recast from 'recast';
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import astUtils from './lib/astUtils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').ConfigFile['imports']} map1 - The map to has it values merged from map2
|
||||||
|
* @param {import('./types').ConfigFile['imports']} map2 - The map to has it values merged to map1
|
||||||
|
* @returns {import('./types').ConfigFile['imports']} The resulting map
|
||||||
|
*/
|
||||||
|
function mergeImportsMaps(map1, map2) {
|
||||||
|
for (const [key, value] of map2) {
|
||||||
|
if (!map1.has(key)) {
|
||||||
|
map1.set(key, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const imports1 = notNull(map1.get(key));
|
||||||
|
const imports2 = notNull(map2.get(key));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because arrays and objects are always different independently from having equal values
|
||||||
|
* ([] === [] -> false). It is converted to a string so the comparison can be made.
|
||||||
|
*/
|
||||||
|
switch ([typeof imports1 === 'string', typeof imports2 === 'string'].join(',')) {
|
||||||
|
case 'true,true':
|
||||||
|
if (imports1.toString() === imports2.toString())
|
||||||
|
map1.set(key, value);
|
||||||
|
else
|
||||||
|
map1.set(key, [['default', imports1.toString()], ['default', imports2.toString()]]);
|
||||||
|
break;
|
||||||
|
case 'true,false':
|
||||||
|
map1.set(key, [['default', imports1.toString()], ...imports2]);
|
||||||
|
break;
|
||||||
|
case 'false,true':
|
||||||
|
map1.set(key, [['default', imports2.toString()], ...imports1]);
|
||||||
|
break;
|
||||||
|
case 'false,false':
|
||||||
|
map1.set(key, [...imports1, ...imports2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (typeof map1.get(key) !== 'string')
|
||||||
|
map1.set(key, [...new Set(map1.get(key))]);
|
||||||
|
}
|
||||||
|
return map1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path1 The path to traverse from
|
||||||
|
* @param {string} root The root path
|
||||||
|
* @returns {string} The path to traverse
|
||||||
|
*/
|
||||||
|
function getPathDepth(path1, root) {
|
||||||
|
const pathDepth = path1.replace(root, '').split('/').slice(1);
|
||||||
|
if (pathDepth.length <= 1) return pathDepth.map(() => '.').join('/');
|
||||||
|
return pathDepth.map(() => '..').join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ConfigsWriter {
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
root = process.cwd();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').Config[]} configs The array of configs to construct from
|
||||||
|
* @param {string} [root] The root directory path
|
||||||
|
*/
|
||||||
|
constructor(configs, root) {
|
||||||
|
this.configs = configs;
|
||||||
|
this.root = root ?? this.root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').Package} pkg The package to generate the config string from
|
||||||
|
* @returns {import('./types').ConfigFile} The config file object
|
||||||
|
*/
|
||||||
|
generateObj(pkg) {
|
||||||
|
/** @type {import('./types').ConfigFile} */
|
||||||
|
const configObj = {
|
||||||
|
path: path.join(pkg.path, 'eslint.config.js'),
|
||||||
|
imports: new Map(),
|
||||||
|
configs: [],
|
||||||
|
presets: [],
|
||||||
|
rules: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!pkg.root) {
|
||||||
|
const rootConfig = path.join(getPathDepth(pkg.path, this.root), 'eslint.config.js');
|
||||||
|
configObj.imports.set(!rootConfig.startsWith('.') ? `./${rootConfig}` : rootConfig, 'root');
|
||||||
|
configObj.presets.push('root');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [configName, optionsNames] of notNull(pkg.config)) {
|
||||||
|
|
||||||
|
const config = this.configs.find(c => c.name === configName);
|
||||||
|
if (!config) continue;
|
||||||
|
|
||||||
|
const options = config.options.filter(o => optionsNames.includes(o.name));
|
||||||
|
if (!options || options.length === 0) continue;
|
||||||
|
|
||||||
|
const imports = options.reduce((acc, opt) => {
|
||||||
|
const map1 = new Map(Object.entries(acc.packages ?? {}));
|
||||||
|
const map2 = new Map(Object.entries(opt.packages ?? {}));
|
||||||
|
acc.packages = Object.fromEntries(mergeImportsMaps(map1, map2));
|
||||||
|
return acc;
|
||||||
|
});
|
||||||
|
|
||||||
|
configObj.imports = mergeImportsMaps(configObj.imports, new Map(Object.entries(imports.packages ?? {})));
|
||||||
|
|
||||||
|
configObj.configs = [...new Set([
|
||||||
|
...configObj.configs,
|
||||||
|
...options.map(o => o.configs ?? []).flat(),
|
||||||
|
])];
|
||||||
|
|
||||||
|
configObj.rules = [...new Set([
|
||||||
|
...configObj.rules,
|
||||||
|
...options.map(o => o.rules ?? []).flat(),
|
||||||
|
])];
|
||||||
|
|
||||||
|
configObj.presets = [...new Set([
|
||||||
|
...configObj.presets,
|
||||||
|
...options.map(o => o.presets ?? []).flat(),
|
||||||
|
])];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return configObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ! NOTE:
|
||||||
|
* These functions declared bellow are notably hard to read and have lots of exceptions and
|
||||||
|
* disabled eslint and typescript checks. Unfortunately this is something that I wasn't able to
|
||||||
|
* prevent because a lot of the AST typescript types are somewhat wrong or simply hard to work
|
||||||
|
* with them.
|
||||||
|
*
|
||||||
|
* But for somewhat help developing and prevent unwanted errors in the future, the types and eslint
|
||||||
|
* errors are explicitly disabled and types are explicitly overridden. This is why there are so
|
||||||
|
* many JSDoc type annotations and comments in general.
|
||||||
|
*
|
||||||
|
* Any help to make this code more readable and robust is appreciated
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('estree').Program} Program
|
||||||
|
* @typedef {(
|
||||||
|
* import('./lib/astUtils.js').ExpressionOrIdentifier |
|
||||||
|
* import('estree').ObjectExpression |
|
||||||
|
* import('estree').SpreadElement
|
||||||
|
* )} ConfigArrayElement
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Program} ast The program ast to be manipulated
|
||||||
|
* @returns {Promise<Program>} The final ast with the recreated default export
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async addDefaultExport(ast) {
|
||||||
|
|
||||||
|
/** @type {{program: Program}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||||
|
const { program: exportTemplateAst } = recast.parse([
|
||||||
|
'/** @type {import(\'eslint\').Linter.FlatConfig[]} */',
|
||||||
|
'export default [',
|
||||||
|
'',
|
||||||
|
'];',
|
||||||
|
].join('\n'), { parser: (await import('recast/parsers/babel.js')) });
|
||||||
|
/** @type {import('estree').ExportDefaultDeclaration} */
|
||||||
|
// @ts-expect-error Node type needs to be ExportDefaultDeclaration to be founded
|
||||||
|
const exportTemplateNode = exportTemplateAst.body.find(n => n.type === 'ExportDefaultDeclaration');
|
||||||
|
|
||||||
|
/** @type {import('estree').ExportDefaultDeclaration | undefined} */
|
||||||
|
// @ts-expect-error Node type needs to be ExportDefaultDeclaration to be founded
|
||||||
|
let astExport = ast.body.find(n => n.type === 'ExportDefaultDeclaration');
|
||||||
|
if (!astExport) { ast.body.push(exportTemplateNode); return ast; }
|
||||||
|
|
||||||
|
/** @type {import('estree').VariableDeclaration | undefined} */
|
||||||
|
const oldExportValue = astExport.declaration.type !== 'ArrayExpression'
|
||||||
|
// @ts-expect-error astExport.declaration is a expression
|
||||||
|
? astUtils.createVariable('oldConfig', 'const', astExport.declaration)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!oldExportValue) return ast;
|
||||||
|
|
||||||
|
// @ts-expect-error declaration is a ArrayExpression
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
||||||
|
exportTemplateNode.declaration.elements.push({
|
||||||
|
type: 'SpreadElement',
|
||||||
|
argument: { type: 'Identifier', name: 'oldConfig' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const astExportIdx = ast.body.indexOf(astExport);
|
||||||
|
ast.body[astExportIdx] = exportTemplateNode;
|
||||||
|
ast.body.splice(astExportIdx - 1, 0, oldExportValue);
|
||||||
|
|
||||||
|
return ast;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').ConfigFile['rules']} rules The rules to be used to create the object
|
||||||
|
* @returns {import('estree').ObjectExpression} The object containing the spread rules
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
createRulesObject(rules) {
|
||||||
|
/** @type {import('estree').SpreadElement[]} */
|
||||||
|
// @ts-expect-error The array is filtered to remove undefined's
|
||||||
|
const expressions = rules
|
||||||
|
.map(r => {
|
||||||
|
const e = astUtils.stringToExpression(r);
|
||||||
|
if (e) return astUtils.toSpreadElement(e);
|
||||||
|
else undefined;
|
||||||
|
}).filter(e => e);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: [{
|
||||||
|
// @ts-expect-error because ObjectProperty doesn't exist in estree types
|
||||||
|
type: 'ObjectProperty',
|
||||||
|
key: { type: 'Identifier', name: 'rules' },
|
||||||
|
value: {
|
||||||
|
type: 'ObjectExpression',
|
||||||
|
properties: expressions,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds elements to the default export node, without adding duplicates
|
||||||
|
* @typedef {import('estree').ArrayExpression} ArrayExpression
|
||||||
|
* @param {Program} ast The program ast to be manipulated
|
||||||
|
* @param {ConfigArrayElement[]} elements The elements to be added to the array
|
||||||
|
* @returns {Program} The final ast with the recreated default export
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
addElementsToExport(ast, elements) {
|
||||||
|
/** @type {import('estree').ExportDefaultDeclaration} */
|
||||||
|
// @ts-expect-error Node type needs to be ExportDefaultDeclaration to be founded
|
||||||
|
const exportNode = ast.body.find(n => n.type === 'ExportDefaultDeclaration');
|
||||||
|
const exportNodeIdx = ast.body.indexOf(exportNode);
|
||||||
|
|
||||||
|
/** @type {ArrayExpression} */
|
||||||
|
// @ts-expect-error declaration is a ArrayExpression
|
||||||
|
const array = exportNode.declaration;
|
||||||
|
|
||||||
|
for (const e of elements) {
|
||||||
|
if (e.type !== 'ObjectExpression' && astUtils.findInArray(array, e)) continue;
|
||||||
|
array.elements.push(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportNode.declaration = array;
|
||||||
|
ast.body[exportNodeIdx] = exportNode;
|
||||||
|
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Program} ast The program ast to be manipulated
|
||||||
|
* @param {import('./types').ConfigFile['imports']} imports The imports map to be used
|
||||||
|
* @returns {Program} The final ast with the recreated default export
|
||||||
|
*/
|
||||||
|
addPackageImports(ast, imports) {
|
||||||
|
|
||||||
|
/** @type {import('estree').ImportDeclaration[]} */
|
||||||
|
const importDeclarations = [];
|
||||||
|
|
||||||
|
for (const [pkgName, specifiers] of imports) {
|
||||||
|
/** @type {import('estree').ImportDeclaration | undefined} */
|
||||||
|
// @ts-expect-error type error, the specifier has to be ImportDeclaration to be founded
|
||||||
|
const existingDeclaration = ast.body.find(s => s.type === 'ImportDeclaration' && s.source.value === pkgName);
|
||||||
|
|
||||||
|
const importDeclaration = astUtils.createImportDeclaration(
|
||||||
|
pkgName, typeof specifiers === 'string' ? specifiers : undefined, existingDeclaration,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof specifiers !== 'string') {
|
||||||
|
specifiers.forEach(s => {
|
||||||
|
if (typeof s === 'string') return importDeclaration.addSpecifier(s);
|
||||||
|
else return importDeclaration.addSpecifier(s[0], s[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingDeclaration) ast.body[ast.body.indexOf(existingDeclaration)] = importDeclaration.body;
|
||||||
|
else importDeclarations.push(importDeclaration.body);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.body.unshift(...importDeclarations);
|
||||||
|
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').ConfigFile} config The config file object to be transformed into a eslint.config.js file
|
||||||
|
* @returns {Promise<string>} The generated config file contents
|
||||||
|
*/
|
||||||
|
async generate(config) {
|
||||||
|
|
||||||
|
const existingConfig = existsSync(config.path) ? await fs.readFile(config.path, 'utf-8') : '';
|
||||||
|
|
||||||
|
/** @type {{program: Program}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const { program: ast } = recast.parse(existingConfig, { parser: (await import('recast/parsers/babel.js')) });
|
||||||
|
|
||||||
|
await this.addDefaultExport(ast);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {ConfigArrayElement[]}
|
||||||
|
*/
|
||||||
|
// @ts-expect-error The array is filtered to remove undefined's
|
||||||
|
const elements = [
|
||||||
|
...config.configs.map(c => astUtils.stringToExpression(c)),
|
||||||
|
...config.presets.map(p => {
|
||||||
|
const e = astUtils.stringToExpression(p);
|
||||||
|
if (e) return astUtils.toSpreadElement(e);
|
||||||
|
else undefined;
|
||||||
|
}),
|
||||||
|
config.rules.length > 0
|
||||||
|
? this.createRulesObject(config.rules)
|
||||||
|
: undefined,
|
||||||
|
].filter(e => e);
|
||||||
|
|
||||||
|
this.addElementsToExport(ast, elements);
|
||||||
|
this.addPackageImports(ast, config.imports);
|
||||||
|
|
||||||
|
const finalCode = recast.prettyPrint(ast, { parser: (await import('recast/parsers/babel.js')) }).code;
|
||||||
|
return finalCode;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path The path to the file to be written
|
||||||
|
* @param {string} content The content of the file
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async write(path, content) {
|
||||||
|
await fs.writeFile(path, content, 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
168
packages/cli/src/configsProcessor.js
Executable file
168
packages/cli/src/configsProcessor.js
Executable file
@@ -0,0 +1,168 @@
|
|||||||
|
#!node
|
||||||
|
import path from 'node:path';
|
||||||
|
import glob from 'picomatch';
|
||||||
|
import prompts from 'prompts';
|
||||||
|
import c from 'picocolors';
|
||||||
|
import str from './lib/str.js';
|
||||||
|
|
||||||
|
export default class ConfigsProcessor {
|
||||||
|
/** @type {string} */
|
||||||
|
dir = process.cwd();
|
||||||
|
|
||||||
|
/** @type {import('./types.js').Config[]} */
|
||||||
|
configs;
|
||||||
|
|
||||||
|
/** @type {string[] | undefined} */
|
||||||
|
#packagesPatterns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {{
|
||||||
|
* configs: import('./types.js').Config[],
|
||||||
|
* packages?: string[],
|
||||||
|
* directory?: string,
|
||||||
|
* }} options - Cli options
|
||||||
|
*/
|
||||||
|
constructor(options) {
|
||||||
|
this.#packagesPatterns = options.packages;
|
||||||
|
this.configs = options?.configs;
|
||||||
|
this.dir = path.normalize(options.directory ?? this.dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types.js').Package} pkg - Package to detect from
|
||||||
|
* @param {import('./types.js').Config['options']} options - Options to be passed
|
||||||
|
* @param {boolean} single - Whether to only detect one option
|
||||||
|
* @returns {string[]} - The detected options
|
||||||
|
*/
|
||||||
|
detectOptions(pkg, options, single) {
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
const detectedOptions = [];
|
||||||
|
|
||||||
|
for (const option of options) {
|
||||||
|
|
||||||
|
if (option.detect === true) {
|
||||||
|
detectedOptions.push(option.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!option.detect) continue;
|
||||||
|
|
||||||
|
const match = glob(option.detect);
|
||||||
|
|
||||||
|
const files = pkg.files.filter(f => match ? match(f) : false);
|
||||||
|
const directories = pkg.directories.filter(f => match ? match(f) : false);
|
||||||
|
|
||||||
|
if (files.length > 0 || directories.length > 0) {
|
||||||
|
detectedOptions.push(option.name);
|
||||||
|
if (single) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return detectedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types.js').Package[] | import('./types.js').Package} pkg - The packages to questions the configs
|
||||||
|
* @param {import('./types').Config[]} configs - The configs to be used
|
||||||
|
* @returns {Promise<import('./types.js').Package[]>} - The selected options by the user
|
||||||
|
*/
|
||||||
|
async questionConfig(pkg, configs) {
|
||||||
|
|
||||||
|
const packages = Array.isArray(pkg) ? [...pkg] : [pkg];
|
||||||
|
|
||||||
|
const instructions = c.dim(`\n${c.bold('A: Toggle all')} - ↑/↓: Highlight option - ←/→/[space]: Toggle selection - enter/return: Complete answer`);
|
||||||
|
|
||||||
|
for (const config of configs) {
|
||||||
|
|
||||||
|
/** @type {import('prompts').Choice[]} */
|
||||||
|
const configChoices = config.options.map(option => {return { title: `${str.capitalize(option.name)}`, value: option.name };});
|
||||||
|
|
||||||
|
/** @type {Record<string, string[]>} */
|
||||||
|
const selectedOptions = await prompts({
|
||||||
|
name: config.name,
|
||||||
|
type: config.type === 'multiple' ? 'multiselect' : 'select',
|
||||||
|
message: str.capitalize(config.name),
|
||||||
|
choices: config.type === 'confirm' ? [
|
||||||
|
{
|
||||||
|
title: 'Yes',
|
||||||
|
value: ['yes'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'No',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
] : configChoices,
|
||||||
|
hint: config.description,
|
||||||
|
instructions: instructions + c.dim(c.italic('\nSelect none if you don\'t want to use this configuration\n')),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedOptions[config.name] === null) continue;
|
||||||
|
|
||||||
|
if (selectedOptions[config.name].length === 0) continue;
|
||||||
|
|
||||||
|
if (packages.length <= 1) {
|
||||||
|
packages[0].config = new Map([
|
||||||
|
...(packages[0].config ?? []),
|
||||||
|
...Object.entries(selectedOptions),
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {{title: string, value: import('./types').Package}[]} */
|
||||||
|
const packagesOptions = packages
|
||||||
|
.map(pkg => {
|
||||||
|
return !pkg.root
|
||||||
|
? {
|
||||||
|
title: `${pkg.name} ${c.dim(pkg.path.replace(this.dir, '.'))}`,
|
||||||
|
value: pkg,
|
||||||
|
}
|
||||||
|
: { title: 'root', value: pkg };
|
||||||
|
})
|
||||||
|
.filter(p => p.title !== 'root');
|
||||||
|
|
||||||
|
/** @type {Record<'packages', import('./types').Package[]>} */
|
||||||
|
const selected = await prompts({
|
||||||
|
name: 'packages',
|
||||||
|
type: 'autocompleteMultiselect',
|
||||||
|
message: `What packages would you like to apply ${config.type === 'single' ? 'this choice' : 'these choices'}?`,
|
||||||
|
choices: packagesOptions,
|
||||||
|
min: 1,
|
||||||
|
instructions: instructions + c.dim(c.italic('\nToggle all to use in the root configuration\n')),
|
||||||
|
});
|
||||||
|
selected.packages = selected.packages ?? [];
|
||||||
|
|
||||||
|
selected.packages.map(pkg => {
|
||||||
|
pkg.config = new Map([
|
||||||
|
...(pkg.config ?? []),
|
||||||
|
...Object.entries(selectedOptions),
|
||||||
|
]); return pkg;
|
||||||
|
});
|
||||||
|
packages.map(pkg => selected.packages.find(s => s.name === pkg.name) ?? pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return packages;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').Package} pkg - The package to detect configs
|
||||||
|
* @returns {import('./types').Package['config']} - Detected configs record
|
||||||
|
*/
|
||||||
|
detectConfig(pkg) {
|
||||||
|
|
||||||
|
/** @type {import('./types.js').Package['config']} */
|
||||||
|
const pkgConfig = new Map();
|
||||||
|
|
||||||
|
for (const config of this.configs.filter(c => !c.manual)) {
|
||||||
|
pkgConfig.set(config.name, this.detectOptions(
|
||||||
|
pkg,
|
||||||
|
config.options,
|
||||||
|
config.type !== 'multiple',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
4
packages/cli/src/index.js
Executable file
4
packages/cli/src/index.js
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
import Cli from './cli.js';
|
||||||
|
|
||||||
|
const cli = new Cli();
|
||||||
|
await cli.run();
|
||||||
170
packages/cli/src/lib/astUtils.js
Normal file
170
packages/cli/src/lib/astUtils.js
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import * as recast from 'recast';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {(
|
||||||
|
* import('estree').MemberExpression |
|
||||||
|
* import('estree').Identifier |
|
||||||
|
* import('estree').CallExpression |
|
||||||
|
* import('estree').NewExpression
|
||||||
|
* )} ExpressionOrIdentifier
|
||||||
|
* This type only includes the expressions used in the cli's config type
|
||||||
|
* @typedef {import('estree').VariableDeclaration} VariableDeclaration
|
||||||
|
* @typedef {import('estree').Identifier['name']} IdentifierName
|
||||||
|
* @typedef {VariableDeclaration['kind']} VariableKind
|
||||||
|
* @typedef {import('estree').VariableDeclarator['init']} VariableInit
|
||||||
|
* @typedef {import('estree').SpreadElement} SpreadElement
|
||||||
|
* @typedef {import('estree').Expression} Expression
|
||||||
|
* @typedef {import('estree').ArrayExpression} ArrayExpression
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {IdentifierName} identifier Nave of the variable identifier
|
||||||
|
* @param {VariableKind} [kind] Type of variable declaration
|
||||||
|
* @param {VariableInit} [init] Initial value of the variable
|
||||||
|
* @returns {VariableDeclaration} The variable declaration ast node object
|
||||||
|
*/
|
||||||
|
export function createVariable(identifier, kind = 'const', init) {
|
||||||
|
return {
|
||||||
|
type: 'VariableDeclaration',
|
||||||
|
kind,
|
||||||
|
declarations: [{
|
||||||
|
type: 'VariableDeclarator',
|
||||||
|
id: { type: 'Identifier', name: identifier },
|
||||||
|
init,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} string The expression in string
|
||||||
|
* @returns {ExpressionOrIdentifier | undefined} The expression or identifier node of that string (undefined if string is not a expression)
|
||||||
|
*/
|
||||||
|
export function stringToExpression(string) {
|
||||||
|
/** @type {ExpressionOrIdentifier} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||||
|
const e = recast.parse(string).program.body[0].expression;
|
||||||
|
if (['MemberExpression', 'Identifier', 'CallExpression', 'NewExpression'].includes(e.type)) return e;
|
||||||
|
else return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ArrayExpression} array The array node to search trough
|
||||||
|
* @param {ExpressionOrIdentifier | SpreadElement} element The element to be search
|
||||||
|
* @returns {ExpressionOrIdentifier | undefined} The element of the array founded, undefined if it isn't found
|
||||||
|
*/
|
||||||
|
export function findInArray(array, element) {
|
||||||
|
|
||||||
|
/** @type {ExpressionOrIdentifier[]} */
|
||||||
|
// @ts-expect-error The array should have just tge type above
|
||||||
|
element = element.type === 'SpreadElement' ? element.argument : element;
|
||||||
|
|
||||||
|
/** @type {ExpressionOrIdentifier[]} */
|
||||||
|
// @ts-expect-error The array is filtered to have the type above
|
||||||
|
const filteredElements = array.elements
|
||||||
|
.map(n => {
|
||||||
|
if (n?.type === 'SpreadElement') return n.argument;
|
||||||
|
return n;
|
||||||
|
}).filter(n => n && n.type === element.type);
|
||||||
|
|
||||||
|
const toStringElements = filteredElements.map(n => recast.print(n).code);
|
||||||
|
const toStringElement = recast.print(element).code;
|
||||||
|
|
||||||
|
const idx = toStringElements.findIndex(e => e === toStringElement);
|
||||||
|
return filteredElements[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ExpressionOrIdentifier} expression The expression to be spread
|
||||||
|
* @returns {SpreadElement} The spread element node
|
||||||
|
*/
|
||||||
|
export function toSpreadElement(expression) {
|
||||||
|
return {
|
||||||
|
type: 'SpreadElement',
|
||||||
|
argument: expression,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* body: import('estree').ImportDeclaration
|
||||||
|
* addSpecifier: (specifier: string, alias?: string) => ThisType<ImportDeclarationHelper>
|
||||||
|
* convertDefaultSpecifier: () => ThisType<ImportDeclarationHelper>
|
||||||
|
* }} ImportDeclarationHelper
|
||||||
|
* @param {string} source The package name or source path to be imported
|
||||||
|
* @param {string} [defaultImported] The default specifier imported
|
||||||
|
* @param {import('estree').ImportDeclaration} [body] The body of the import declaration to start with
|
||||||
|
* @returns {ImportDeclarationHelper} A helper object for manipulating the import declaration
|
||||||
|
*/
|
||||||
|
export function createImportDeclaration(source, defaultImported, body) {
|
||||||
|
const helper = {
|
||||||
|
/** @type {import('estree').ImportDeclaration} */
|
||||||
|
body: body ?? {
|
||||||
|
type: 'ImportDeclaration',
|
||||||
|
specifiers: defaultImported ? [{
|
||||||
|
type: 'ImportDefaultSpecifier',
|
||||||
|
local: { type: 'Identifier', name: defaultImported },
|
||||||
|
}] : [],
|
||||||
|
source: {
|
||||||
|
type: 'Literal',
|
||||||
|
value: source,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Converts a default specifier to a specifier with a alias.
|
||||||
|
* @example
|
||||||
|
* import eslit from 'eslit';
|
||||||
|
* // Is converted to
|
||||||
|
* import { default as eslit } from 'eslit';
|
||||||
|
* @returns {ThisType<ImportDeclarationHelper>} This helper with the converted default specifier
|
||||||
|
*/
|
||||||
|
convertDefaultSpecifier() {
|
||||||
|
const specifier = this.body.specifiers.find(s => s.type === 'ImportDefaultSpecifier');
|
||||||
|
if (!specifier)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
this.body.specifiers.splice(
|
||||||
|
this.body.specifiers.indexOf(specifier),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
return this.addSpecifier('default', specifier.local.name);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* @param {string} specifier The value to be imported from the package
|
||||||
|
* @param {string} [alias] The local alias of the value
|
||||||
|
* @returns {ThisType<ImportDeclarationHelper>} This helper with the added specifiers
|
||||||
|
*/
|
||||||
|
addSpecifier(specifier, alias) {
|
||||||
|
this.convertDefaultSpecifier();
|
||||||
|
if (this.body.specifiers.find(s => s.local.name === alias || s.local.name === specifier))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
this.body.specifiers.push({
|
||||||
|
type: 'ImportSpecifier',
|
||||||
|
imported: {
|
||||||
|
type: 'Identifier',
|
||||||
|
name: specifier,
|
||||||
|
},
|
||||||
|
local: {
|
||||||
|
type: 'Identifier',
|
||||||
|
name: alias ?? specifier,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (defaultImported && body && !body.specifiers.find(s => s.type === 'ImportDefaultSpecifier' && s.local.name === defaultImported)) {
|
||||||
|
helper.addSpecifier('default', defaultImported);
|
||||||
|
}
|
||||||
|
return helper;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createVariable,
|
||||||
|
stringToExpression,
|
||||||
|
toSpreadElement,
|
||||||
|
findInArray,
|
||||||
|
createImportDeclaration,
|
||||||
|
};
|
||||||
12
packages/cli/src/lib/count.js
Normal file
12
packages/cli/src/lib/count.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('../types').Package[]} packages - Package list
|
||||||
|
* @returns {number} Number of packages' configs
|
||||||
|
*/
|
||||||
|
function packagesWithConfigs(packages) {
|
||||||
|
return packages.map(p =>
|
||||||
|
[...p.config?.values() ?? []].filter((options) => options.length > 0).length,
|
||||||
|
).reduce((partial, sum) => partial + sum, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { packagesWithConfigs };
|
||||||
15
packages/cli/src/lib/notNull.js
Normal file
15
packages/cli/src/lib/notNull.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* JSDoc types lack a non-null assertion.
|
||||||
|
* @template T
|
||||||
|
* @param {T} value The value which to assert against null or undefined
|
||||||
|
* @returns {NonNullable<T>} The said value
|
||||||
|
* @throws {TypeError} If the value is unexpectedly null or undefined
|
||||||
|
* @author Jimmy Wärting - https://github.com/jimmywarting
|
||||||
|
* @see https://github.com/Microsoft/TypeScript/issues/23405#issuecomment-873331031
|
||||||
|
* @see https://github.com/Microsoft/TypeScript/issues/23405#issuecomment-1249287966
|
||||||
|
*/
|
||||||
|
export default function notNull(value) {
|
||||||
|
// Use `==` to check for both null and undefined
|
||||||
|
if (value == null) throw new Error('did not expect value to be null or undefined');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
10
packages/cli/src/lib/str.js
Normal file
10
packages/cli/src/lib/str.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} str - The string to capitalize
|
||||||
|
* @returns {string} The capitalized string
|
||||||
|
*/
|
||||||
|
function capitalize(str) {
|
||||||
|
return str[0].toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { capitalize };
|
||||||
210
packages/cli/src/packageInstaller.js
Normal file
210
packages/cli/src/packageInstaller.js
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { exec } from 'node:child_process';
|
||||||
|
import { createSpinner } from 'nanospinner';
|
||||||
|
import c from 'picocolors';
|
||||||
|
import * as recast from 'recast';
|
||||||
|
import { readFile, writeFile } from 'node:fs/promises';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('./types').PackageManagerHandler}
|
||||||
|
*/
|
||||||
|
class CommandHandler {
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
command;
|
||||||
|
|
||||||
|
/** @type {((path: string, packages: string[]) => string | Promise<string>) | undefined} */
|
||||||
|
checker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} command What command to use to install
|
||||||
|
* @param {(path: string, packages: string[]) => string | Promise<string>} [checker] Checks if a argument should be passed
|
||||||
|
*/
|
||||||
|
constructor(command, checker) {
|
||||||
|
this.command = command;
|
||||||
|
this.checker = checker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path The path to run the command
|
||||||
|
* @param {string[]} packages The packages to be added on the command
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async install(path, packages) {
|
||||||
|
|
||||||
|
if (this.checker)
|
||||||
|
this.command += await this.checker(path, packages);
|
||||||
|
|
||||||
|
return new Promise((res) => {
|
||||||
|
const spinner = createSpinner(`Installing packages with ${c.green(this.command)} ${c.dim(packages.join(' '))}`).start();
|
||||||
|
try {
|
||||||
|
const child = exec(`${this.command} ${packages.join(' ')}`, { cwd: path });
|
||||||
|
child.stdout?.on('data', (chunk) => spinner.update({
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||||
|
text: `Installing packages with ${c.green(this.command)} ${c.dim(packages.join(' '))}\n ${c.dim(chunk)}`,
|
||||||
|
}));
|
||||||
|
child.stdout?.on('close', () => {
|
||||||
|
spinner.success({
|
||||||
|
text: `Installed packages with ${c.green(this.command)}`,
|
||||||
|
}); res();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
||||||
|
res(console.error(`Error while installing the packages with ${this.command} ${c.dim(packages.join(' '))} on ${path}: ${error}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('./types').PackageManagerHandler}
|
||||||
|
*/
|
||||||
|
class DenoHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path The path to run the command
|
||||||
|
* @param {string[]} packages The packages to be added on the command
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async install(path, packages) {
|
||||||
|
const configPath = join(path, 'eslint.config.js');
|
||||||
|
|
||||||
|
if (!existsSync(configPath)) return;
|
||||||
|
|
||||||
|
const configFile = await readFile(configPath, 'utf8');
|
||||||
|
/** @type {{program: import('estree').Program}}*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const { program: ast } = recast.parse(configFile, { parser: (await import('recast/parsers/babel.js')) });
|
||||||
|
|
||||||
|
ast.body.map((node) => {
|
||||||
|
if (node.type !== 'ImportDeclaration') return node;
|
||||||
|
|
||||||
|
if (packages.includes(node.source.value?.toString() ?? '')) {
|
||||||
|
node.source.value = `npm:${node.source.value}`;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
|
||||||
|
await writeFile(configPath, recast.prettyPrint(ast).code, 'utf-8');
|
||||||
|
|
||||||
|
console.log(c.green('Added npm: specifier to dependencies'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PackageInstaller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Map<string, string[]>} PackagesMap
|
||||||
|
* @type {PackagesMap}
|
||||||
|
*/
|
||||||
|
packagesMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* name: import('./types').PackageManagerName
|
||||||
|
* description: string
|
||||||
|
* handler: import('./types').PackageManagerHandler
|
||||||
|
* }} PackageManager
|
||||||
|
* @type {PackageManager}
|
||||||
|
*/
|
||||||
|
packageManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Record<import('./types').PackageManagerName, PackageManager>}
|
||||||
|
*/
|
||||||
|
packageManagers = {
|
||||||
|
deno: {
|
||||||
|
name: 'deno',
|
||||||
|
description: 'Adds npm: specifiers to the eslint.config.js file',
|
||||||
|
handler: new DenoHandler(),
|
||||||
|
},
|
||||||
|
bun: {
|
||||||
|
name: 'bun',
|
||||||
|
description: 'Uses bun install',
|
||||||
|
handler: new CommandHandler('bun install'),
|
||||||
|
},
|
||||||
|
pnpm: {
|
||||||
|
name: 'pnpm',
|
||||||
|
description: 'Uses pnpm install',
|
||||||
|
handler: new CommandHandler('pnpm install --save-dev', (path) => {
|
||||||
|
if (existsSync(join(path, 'pnpm-workspace.yaml')) && existsSync(join(path, 'package.json')))
|
||||||
|
return ' -w';
|
||||||
|
else return '';
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
yarn: {
|
||||||
|
name: 'yarn',
|
||||||
|
description: 'Uses yarn add',
|
||||||
|
handler: new CommandHandler('yarn add --dev'),
|
||||||
|
},
|
||||||
|
npm: {
|
||||||
|
name: 'npm',
|
||||||
|
description: 'Uses npm install',
|
||||||
|
handler: new CommandHandler('npm install --save-dev'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {PackagesMap} packagesMap The map of directories and packages to be installed
|
||||||
|
* @param {string} root Root directory path
|
||||||
|
*/
|
||||||
|
constructor(packagesMap, root) {
|
||||||
|
this.packagesMap = packagesMap;
|
||||||
|
this.packageManager = this.detectPackageManager(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} root Root directory path
|
||||||
|
* @returns {PackageManager} The package manager detected;
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
detectPackageManager(root) {
|
||||||
|
/** @type {(...path: string[]) => boolean} */
|
||||||
|
const exists = (...path) => existsSync(join(root, ...path));
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case exists('deno.json'):
|
||||||
|
case exists('deno.jsonc'):
|
||||||
|
return this.packageManagers.deno;
|
||||||
|
|
||||||
|
case exists('bun.lockb'):
|
||||||
|
return this.packageManagers.bun;
|
||||||
|
|
||||||
|
case exists('pnpm-lock.yaml'):
|
||||||
|
return this.packageManagers.pnpm;
|
||||||
|
|
||||||
|
case exists('yarn.lock'):
|
||||||
|
return this.packageManagers.yarn;
|
||||||
|
|
||||||
|
case exists('package-lock.json'):
|
||||||
|
return this.packageManagers.npm;
|
||||||
|
|
||||||
|
case exists('package.json'):
|
||||||
|
/** @type {{packageManager?: string}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, no-case-declarations
|
||||||
|
const { packageManager } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
||||||
|
if (!packageManager) return this.packageManagers.npm;
|
||||||
|
|
||||||
|
if (packageManager.includes('pnpm')) return this.packageManagers.pnpm;
|
||||||
|
if (packageManager.includes('yarn')) return this.packageManagers.yarn;
|
||||||
|
if (packageManager.includes('npm')) return this.packageManagers.npm;
|
||||||
|
|
||||||
|
else return this.packageManagers.npm;
|
||||||
|
|
||||||
|
default: return this.packageManagers.npm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async install() {
|
||||||
|
for (const [path, packages] of this.packagesMap) {
|
||||||
|
await this.packageManager.handler.install(path, packages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
61
packages/cli/src/types.d.ts
vendored
Normal file
61
packages/cli/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import type { OptionValues } from 'commander';
|
||||||
|
|
||||||
|
export type PackageManagerName = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'deno';
|
||||||
|
|
||||||
|
export type CliArgs = {
|
||||||
|
packages?: string[]
|
||||||
|
mergeToRoot?: boolean
|
||||||
|
installPkgs?: boolean | PackageManagerName
|
||||||
|
dir: string
|
||||||
|
} & OptionValues;
|
||||||
|
|
||||||
|
export type Config = {
|
||||||
|
name: string
|
||||||
|
type: 'single' | 'multiple'
|
||||||
|
manual?: boolean
|
||||||
|
description?: string
|
||||||
|
options: {
|
||||||
|
name: string
|
||||||
|
packages?: Record<string, string | (string | [string, string])[]>
|
||||||
|
configs?: string[]
|
||||||
|
rules?: string[]
|
||||||
|
presets?: string[]
|
||||||
|
detect?: string[] | true
|
||||||
|
}[]
|
||||||
|
} | {
|
||||||
|
name: string
|
||||||
|
type: 'confirm'
|
||||||
|
manual: true
|
||||||
|
description?: string
|
||||||
|
options: [{
|
||||||
|
name: 'yes'
|
||||||
|
packages?: Record<string, string | (string | [string, string])[]>
|
||||||
|
configs?: string[]
|
||||||
|
rules?: string[]
|
||||||
|
presets?: string[]
|
||||||
|
detect?: undefined
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Package {
|
||||||
|
root?: boolean
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
files: string[]
|
||||||
|
directories: string[]
|
||||||
|
config?: Map<string, string[]>
|
||||||
|
configFile?: ConfigFile
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigFile {
|
||||||
|
path: string
|
||||||
|
imports: Map<string, string | (string | [string, string])[]>
|
||||||
|
configs: string[]
|
||||||
|
presets: string[]
|
||||||
|
rules: string[]
|
||||||
|
content?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PackageManagerHandler {
|
||||||
|
install(path: string, packages: string[]): Promise<void> | void
|
||||||
|
}
|
||||||
246
packages/cli/src/workspace.js
Normal file
246
packages/cli/src/workspace.js
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
import fs from 'node:fs/promises';
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import YAML from 'yaml';
|
||||||
|
import path, { join } from 'node:path';
|
||||||
|
import glob from 'picomatch';
|
||||||
|
import picomatch from 'picomatch';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {Promise<T>} promise - The async function to try running
|
||||||
|
* @returns {Promise<T | null>} - Returns the result of the async function, or null if it errors
|
||||||
|
*/
|
||||||
|
async function tryRun(promise) {
|
||||||
|
try {
|
||||||
|
return await promise;
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} directory - The directory to find .gitignore and .eslintignore
|
||||||
|
* @returns {Promise<string[]>} - List of ignore glob patterns
|
||||||
|
*/
|
||||||
|
async function getIgnoredFiles(directory) {
|
||||||
|
const gitIgnore = (await tryRun(fs.readFile(join(directory, '.gitignore'), 'utf8')) ?? '')
|
||||||
|
.split('\n')
|
||||||
|
.filter(p => p && !p.startsWith('#'))
|
||||||
|
.map(p => join(directory, '**', p));
|
||||||
|
|
||||||
|
const eslintIgnore = (await tryRun(fs.readFile(join(directory, '.eslintignore'), 'utf8')) ?? '')
|
||||||
|
.split('\n')
|
||||||
|
.filter(p => p && !p.startsWith('#'))
|
||||||
|
.map(p => join(directory, '**', p));
|
||||||
|
|
||||||
|
return [...eslintIgnore, ...gitIgnore];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} directory - The directory to work in.
|
||||||
|
* @returns {Promise<string>} - The package name founded.
|
||||||
|
*/
|
||||||
|
async function getPackageName(directory) {
|
||||||
|
if (existsSync(join(directory, 'package.json'))) {
|
||||||
|
const file = await fs.readFile(join(directory, 'package.json'), 'utf8');
|
||||||
|
/** @type {{name?: string}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const obj = JSON.parse(file);
|
||||||
|
|
||||||
|
if (obj.name) return obj.name;
|
||||||
|
}
|
||||||
|
return path.normalize(directory).split('/').at(-1) ?? directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Workspace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} directory - The directory to get the workspace from
|
||||||
|
* @param {string[] | false} [packagePatterns]
|
||||||
|
* List of package patterns (`false` to explicitly tell that this workspace is not a monorepo)
|
||||||
|
*/
|
||||||
|
constructor(directory, packagePatterns) {
|
||||||
|
this.dir = directory;
|
||||||
|
this.packagePatterns = packagePatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} [directory] - The directory to work on
|
||||||
|
* @param {string[]} [ignores] - Glob patterns to ignore
|
||||||
|
* @returns {Promise<{files: string[], directories: string[]}>} - List of all files in the directory
|
||||||
|
*/
|
||||||
|
async getPaths(directory = this.dir, ignores = []) {
|
||||||
|
|
||||||
|
ignores.push(
|
||||||
|
...[
|
||||||
|
'.git',
|
||||||
|
'.dist',
|
||||||
|
'.DS_Store',
|
||||||
|
'node_modules',
|
||||||
|
].map((f) => join(directory, f)),
|
||||||
|
...await getIgnoredFiles(directory),
|
||||||
|
);
|
||||||
|
|
||||||
|
const paths = (await fs.readdir(directory))
|
||||||
|
.map((f) => path.normalize(join(directory, f)))
|
||||||
|
.filter((p) => !glob.isMatch(p, ignores));
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
const files = [];
|
||||||
|
/** @type {string[]} */
|
||||||
|
const directories = [];
|
||||||
|
|
||||||
|
for (const path of paths) {
|
||||||
|
if ((await fs.lstat(path)).isDirectory()) {
|
||||||
|
const subPaths = await this.getPaths(path, ignores);
|
||||||
|
directories.push(path, ...subPaths.directories);
|
||||||
|
files.push(...subPaths.files);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
files: files.map(p => path.normalize(p.replace(this.dir, './'))),
|
||||||
|
directories: directories.map(p => path.normalize(p.replace(this.dir, './'))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<string[]>} - List of packages on a directory;
|
||||||
|
*/
|
||||||
|
async getPackagePatterns() {
|
||||||
|
|
||||||
|
/** @type {string[]} */
|
||||||
|
let packagePatterns = [];
|
||||||
|
|
||||||
|
const pnpmWorkspace =
|
||||||
|
existsSync(join(this.dir, 'pnpm-workspace.yaml'))
|
||||||
|
? 'pnpm-workspace.yaml'
|
||||||
|
: existsSync(join(this.dir, 'pnpm-workspace.yml'))
|
||||||
|
? 'pnpm-workspace.yml'
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (pnpmWorkspace) {
|
||||||
|
const pnpmWorkspaceYaml = await fs.readFile(join(this.dir, pnpmWorkspace), 'utf8');
|
||||||
|
|
||||||
|
/** @type {{packages?: string[]}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const pnpmWorkspaceObj = YAML.parse(pnpmWorkspaceYaml);
|
||||||
|
|
||||||
|
packagePatterns.push(...(pnpmWorkspaceObj?.packages ?? []));
|
||||||
|
}
|
||||||
|
else if (existsSync(join(this.dir, 'package.json'))) {
|
||||||
|
const packageJson = await fs.readFile(join(this.dir, 'package.json'), 'utf8');
|
||||||
|
|
||||||
|
/** @type {{workspaces?: string[]}} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const packageJsonObj = JSON.parse(packageJson);
|
||||||
|
|
||||||
|
packagePatterns.push(...(packageJsonObj?.workspaces ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
|
return packagePatterns.map(p => {
|
||||||
|
p = path.normalize(p);
|
||||||
|
p = p.startsWith('/') ? p.replace('/', '') : p;
|
||||||
|
p = p.endsWith('/') ? p.slice(0, p.length - 1) : p;
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Promise<import('./types').Package[]>} - The list of packages that exist in the workspace
|
||||||
|
*/
|
||||||
|
async getPackages() {
|
||||||
|
|
||||||
|
const paths = await this.getPaths();
|
||||||
|
|
||||||
|
/** @type {import('./types').Package} */
|
||||||
|
const rootPackage = {
|
||||||
|
root: true,
|
||||||
|
name: await getPackageName(this.dir),
|
||||||
|
path: this.dir,
|
||||||
|
files: paths.files,
|
||||||
|
directories: paths.directories,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.packagePatterns === false) return [rootPackage];
|
||||||
|
|
||||||
|
const packagePatterns = this.packagePatterns ?? await this.getPackagePatterns();
|
||||||
|
const packagePaths = paths.directories.filter(d => picomatch.isMatch(d, packagePatterns));
|
||||||
|
|
||||||
|
/** @type {import('./types').Package[]} */
|
||||||
|
const packages = [];
|
||||||
|
|
||||||
|
for (const packagePath of packagePaths) {
|
||||||
|
packages.push({
|
||||||
|
root: false,
|
||||||
|
path: join(this.dir, packagePath),
|
||||||
|
name: await getPackageName(join(this.dir, packagePath)),
|
||||||
|
files: paths.files
|
||||||
|
.filter(f => picomatch.isMatch(f, `${packagePath}/**/*`))
|
||||||
|
.map(f => f.replace(`${packagePath}/`, '')),
|
||||||
|
directories: paths.directories
|
||||||
|
.filter(d => picomatch.isMatch(d, `${packagePath}/**/*`))
|
||||||
|
.map(d => d.replace(`${packagePath}/`, '')),
|
||||||
|
});
|
||||||
|
|
||||||
|
rootPackage.files = rootPackage.files
|
||||||
|
.filter(f => picomatch.isMatch(f, `!${packagePath}/**/*`));
|
||||||
|
|
||||||
|
rootPackage.directories = rootPackage.directories
|
||||||
|
.filter(d => picomatch.isMatch(d, `!${packagePath}/**/*`));
|
||||||
|
}
|
||||||
|
|
||||||
|
return [rootPackage, ...packages];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').Package[]} packages - Packages to be merged into root
|
||||||
|
* @returns {[import('./types').Package]} A array containing only the root package
|
||||||
|
*/
|
||||||
|
mergePackages(packages) {
|
||||||
|
|
||||||
|
const rootPackage = packages.find(p => p.root) ?? packages[0];
|
||||||
|
|
||||||
|
const merged = packages.reduce((accumulated, pkg) => {
|
||||||
|
|
||||||
|
const files = [...new Set([
|
||||||
|
...accumulated.files,
|
||||||
|
...pkg.files.map(f => join(pkg.path, f)),
|
||||||
|
]
|
||||||
|
.map(p => p.replace(`${rootPackage.path}/`, '')),
|
||||||
|
)];
|
||||||
|
|
||||||
|
const directories = [...new Set([
|
||||||
|
...accumulated.directories,
|
||||||
|
...pkg.directories.map(d => join(pkg.path, d)),
|
||||||
|
]
|
||||||
|
.map(p => p.replace(`${rootPackage.path}/`, ''))),
|
||||||
|
];
|
||||||
|
|
||||||
|
const mergedConfig = new Map();
|
||||||
|
for (const [config, options] of pkg.config ?? []) {
|
||||||
|
const accumulatedOptions = accumulated.config?.get(config) ?? [];
|
||||||
|
mergedConfig.set(config, [...new Set([...options, ...accumulatedOptions])]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
root: true,
|
||||||
|
path: rootPackage.path,
|
||||||
|
name: rootPackage.name,
|
||||||
|
files,
|
||||||
|
directories,
|
||||||
|
config: mergedConfig,
|
||||||
|
};
|
||||||
|
}, rootPackage);
|
||||||
|
|
||||||
|
return [merged];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
packages/config/CHANGELOG.md
Normal file
8
packages/config/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# @eslit/config
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- Rewritten most of the package logic, so now it uses a more standard ESLint configuration object structure. All configurations now are separated in scope and presets are created for better convenience when configuring ESLint. ([#8](https://github.com/LoredDev/ESLit/pull/8))
|
||||||
|
(fixes [#3](https://github.com/loreddev/eslit/issues/3)).
|
||||||
56
packages/config/index.d.ts
vendored
Normal file
56
packages/config/index.d.ts
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type { Config, EnvOptions } from './src/types';
|
||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions for creating/configuring ESLint.
|
||||||
|
*
|
||||||
|
* @param config - Array or function returning an array of ESLint's configuration objects array to be used.
|
||||||
|
* @param environment - An object with environment variables to be declared and used by the configuration.
|
||||||
|
* @returns The array of ESLint's configuration objects.
|
||||||
|
*/
|
||||||
|
export async function defineConfig(config: Config, environment?: EnvOptions): Promise<Linter.FlatConfig[]>;
|
||||||
|
|
||||||
|
export const configs: Readonly<{
|
||||||
|
/**
|
||||||
|
* **This configuration is necessary to be used before any other one**.
|
||||||
|
* Common configuration for using ESLit rules overrides.
|
||||||
|
*/
|
||||||
|
common: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Recommended configuration overrides of ESLit
|
||||||
|
*/
|
||||||
|
recommended: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Formatting rules/configuration overrides for Javascript and Typescript
|
||||||
|
*/
|
||||||
|
formatting: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Typescript specific configuration overrides
|
||||||
|
*/
|
||||||
|
typescript: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Configuration objects for different development environments.
|
||||||
|
*/
|
||||||
|
environments: {
|
||||||
|
/**
|
||||||
|
* Configuration for Node development environment
|
||||||
|
*/
|
||||||
|
node: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Configuration for Deno development environment
|
||||||
|
*/
|
||||||
|
deno: Linter.FlatConfig
|
||||||
|
/**
|
||||||
|
* Configuration for browser development environment
|
||||||
|
*/
|
||||||
|
browser: Linter.FlatConfig
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* JSDoc rules overrides
|
||||||
|
*/
|
||||||
|
jsdoc: Linter.FlatConfig
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const presets: Readonly<{
|
||||||
|
default: Linter.FlatConfig[]
|
||||||
|
}>;
|
||||||
5
packages/config/jsconfig.json
Normal file
5
packages/config/jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"exclude": ["./node_modules/**", "./dist/**"],
|
||||||
|
"include": ["./index.d.ts", "./src/**/*.ts", "./src/**/*.js"],
|
||||||
|
}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "@eslit/core",
|
"name": "@eslit/config",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"module": "./src/index.js",
|
"module": "./src/index.js",
|
||||||
"source": "./src/index.js",
|
"source": "./src/index.js",
|
||||||
|
"files": [
|
||||||
|
"src",
|
||||||
|
"index.d.ts"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/LoredDev/ESLit",
|
||||||
"exports": {
|
"exports": {
|
||||||
"default": "./src/index.js",
|
"default": "./src/index.js",
|
||||||
"import": "./src/index.js",
|
"import": "./src/index.js",
|
||||||
@@ -14,11 +19,19 @@
|
|||||||
"types": "./src/index.js",
|
"types": "./src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"lint": "ESLINT_USE_FLAT_CONFIG=true eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"repository": {
|
||||||
"author": "",
|
"directory": "packages/config",
|
||||||
"license": "ISC",
|
"type": "git",
|
||||||
|
"url": "https://github.com/LoredDev/ESLit"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"email": "contact.guz013@gmail.com",
|
||||||
|
"name": "Gustavo \"Guz\" L. de Mello",
|
||||||
|
"url": "https://guz.one"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/eslint__js": "^8.42.0",
|
"@types/eslint__js": "^8.42.0",
|
||||||
"@types/node": "^20.4.2",
|
"@types/node": "^20.4.2",
|
||||||
@@ -31,14 +44,13 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||||
"@typescript-eslint/parser": "^6.1.0",
|
"@typescript-eslint/parser": "^6.1.0",
|
||||||
"eslint-plugin-jsdoc": "^46.4.4",
|
"eslint-plugin-jsdoc": "^46.4.4",
|
||||||
"globals": "^13.20.0",
|
"globals": "^13.20.0"
|
||||||
"yaml": "^2.3.1"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.45.0",
|
"eslint": "^8.45.0",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.1.6"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
packages/config/src/configs/common.js
Normal file
36
packages/config/src/configs/common.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import tsESLint from '@typescript-eslint/eslint-plugin';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import jsdoc from 'eslint-plugin-jsdoc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **This configuration is necessary to be used before any other one**.
|
||||||
|
* Common configuration for using ESLit rules overrides.
|
||||||
|
*
|
||||||
|
* @type {Readonly<import('eslint').Linter.FlatConfig>}
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsESLint,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
'jsdoc': jsdoc,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
parserOptions: {
|
||||||
|
project: process.env.ESLIT_TSCONFIG ?? [
|
||||||
|
'./{ts,js}config{.eslint,}.json',
|
||||||
|
'./packages/*/{ts,js}config{.eslint,}.json',
|
||||||
|
'./apps/*/{ts,js}config{.eslint,}.json',
|
||||||
|
],
|
||||||
|
tsconfigRootDir: process.env.ESLIT_ROOT ?? process.cwd(),
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
ecmaVersion: (
|
||||||
|
/** @type {import('eslint').Linter.ParserOptions['ecmaVersion']} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
|
() => {return JSON.parse(process.env.ESLIT_ECMASCRIPT ?? '"latest"');}
|
||||||
|
)(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
47
packages/config/src/configs/environments.js
Normal file
47
packages/config/src/configs/environments.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for Node development environment
|
||||||
|
*
|
||||||
|
* @type {import('eslint').Linter.FlatConfig}
|
||||||
|
*/
|
||||||
|
const node = {
|
||||||
|
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.nodeBuiltin,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for Deno development environment
|
||||||
|
*
|
||||||
|
* @type {import('eslint').Linter.FlatConfig}
|
||||||
|
*/
|
||||||
|
const deno = {
|
||||||
|
files: ['**/*.js', '**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
Deno: true,
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for browser development environment
|
||||||
|
*
|
||||||
|
* @type {import('eslint').Linter.FlatConfig}
|
||||||
|
*/
|
||||||
|
const browser = {
|
||||||
|
files: ['**/*.js', '**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
Deno: true,
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { node, deno, browser };
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Formatting rules/configuration for Javascript and Typescript
|
* Formatting rules/configuration overrides for Javascript and Typescript
|
||||||
*
|
*
|
||||||
* @type {import('../types').ESConfig}
|
* @type {Readonly<import('eslint').Linter.FlatConfig>}
|
||||||
*/
|
*/
|
||||||
const config = {
|
const config = {
|
||||||
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
||||||
@@ -15,7 +15,14 @@ const config = {
|
|||||||
'@typescript-eslint/comma-dangle': ['error', 'always-multiline'],
|
'@typescript-eslint/comma-dangle': ['error', 'always-multiline'],
|
||||||
|
|
||||||
'indent': 'off',
|
'indent': 'off',
|
||||||
'@typescript-eslint/indent': ['error', process.env.READABLE_ESLINT_OPTIONS?.indent === 'space' ? 2 : 'tab', {
|
'@typescript-eslint/indent': ['error', (() => {
|
||||||
|
/** @type {import('../types').EnvOptions['ESLIT_INDENT']} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const indent = JSON.parse(process.env.ESLINT_INDENT ?? '"tab"');
|
||||||
|
|
||||||
|
if (indent === 'space') return 2;
|
||||||
|
else return indent;
|
||||||
|
})(), {
|
||||||
SwitchCase: 1,
|
SwitchCase: 1,
|
||||||
VariableDeclarator: 1,
|
VariableDeclarator: 1,
|
||||||
outerIIFEBody: 1,
|
outerIIFEBody: 1,
|
||||||
@@ -67,13 +74,13 @@ const config = {
|
|||||||
'@typescript-eslint/object-curly-spacing': ['error', 'always'],
|
'@typescript-eslint/object-curly-spacing': ['error', 'always'],
|
||||||
|
|
||||||
'quotes': 'off',
|
'quotes': 'off',
|
||||||
'@typescript-eslint/quotes': ['error', process.env.READABLE_ESLINT_OPTIONS?.quotes ?? 'single'],
|
'@typescript-eslint/quotes': ['error', process.env.ESLINT_QUOTES ?? 'single'],
|
||||||
|
|
||||||
'semi': 'off',
|
'semi': 'off',
|
||||||
'@typescript-eslint/semi': ['error', 'always'],
|
'@typescript-eslint/semi': ['error', 'always'],
|
||||||
|
|
||||||
'space-before-blocks': 'off',
|
'space-before-blocks': 'off',
|
||||||
'@typescript-eslint/space-before-blocks': ['error', process.env.READABLE_ESLINT_OPTIONS?.semi ?? 'always'],
|
'@typescript-eslint/space-before-blocks': ['error', process.env.ESLIT_SEMI ?? 'always'],
|
||||||
|
|
||||||
'space-before-function-paren': 'off',
|
'space-before-function-paren': 'off',
|
||||||
'@typescript-eslint/space-before-function-paren': ['error', {
|
'@typescript-eslint/space-before-function-paren': ['error', {
|
||||||
8
packages/config/src/configs/index.js
Normal file
8
packages/config/src/configs/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import formatting from './formatting.js';
|
||||||
|
import jsdoc from './jsdoc.js';
|
||||||
|
import typescript from './typescript.js';
|
||||||
|
import recommended from './recommended.js';
|
||||||
|
import environments from './environments.js';
|
||||||
|
import common from './common.js';
|
||||||
|
|
||||||
|
export default { formatting, jsdoc, typescript, recommended, environments, common };
|
||||||
15
packages/config/src/configs/jsdoc.js
Normal file
15
packages/config/src/configs/jsdoc.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import jsdoc from 'eslint-plugin-jsdoc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSDoc rules overrides
|
||||||
|
* @type {Readonly<import('eslint').Linter.FlatConfig>}
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
rules: {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
...jsdoc.configs['recommended-typescript-flavor-error'].rules,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
|
import tsESlint from '@typescript-eslint/eslint-plugin';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common configuration related to language features of Javascript and Typescript
|
* Recommended configuration overrides of ESLit
|
||||||
*
|
*
|
||||||
* @type {import('../types').ESConfig}
|
* @type {Readonly<import('eslint').Linter.FlatConfig>}
|
||||||
*/
|
*/
|
||||||
const config = {
|
const config = {
|
||||||
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
|
||||||
rules: {
|
rules: {
|
||||||
|
...js.configs.recommended.rules,
|
||||||
|
...tsESlint.configs.recommended.rules,
|
||||||
|
...tsESlint.configs['recommended-requiring-type-checking'].rules,
|
||||||
|
...tsESlint.configs['eslint-recommended'].rules,
|
||||||
|
...tsESlint.configs.strict.rules,
|
||||||
|
|
||||||
'@typescript-eslint/ban-ts-comment': ['error', {
|
'@typescript-eslint/ban-ts-comment': ['error', {
|
||||||
'ts-ignore': 'allow-with-description',
|
'ts-ignore': 'allow-with-description',
|
||||||
}],
|
}],
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import jsdoc from 'eslint-plugin-jsdoc';
|
import jsdoc from 'eslint-plugin-jsdoc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typescript specific configuration
|
* Typescript specific configuration overrides
|
||||||
*
|
*
|
||||||
* @type {import('../types').ESConfig}
|
* @type {Readonly<import('eslint').Linter.FlatConfig>}
|
||||||
*/
|
*/
|
||||||
const config = {
|
const config = {
|
||||||
files: ['**/*.ts', '**/*.cts', '**/*.mts'],
|
files: ['**/*.ts', '**/*.cts', '**/*.mts'],
|
||||||
@@ -35,7 +35,9 @@ const config = {
|
|||||||
...(
|
...(
|
||||||
/** @type {() => import('eslint').Linter.RulesRecord} */
|
/** @type {() => import('eslint').Linter.RulesRecord} */
|
||||||
() => {
|
() => {
|
||||||
const inferrableTypes = process.env.READABLE_ESLINT_OPTIONS?.inferrableTypes ?? 'never';
|
/** @type {import('../types').inferrableTypesOptions} */
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const inferrableTypes = JSON.parse(process.env.ESLIT_INFER_TYPES ?? '"never"');
|
||||||
|
|
||||||
if (typeof inferrableTypes === 'string') {
|
if (typeof inferrableTypes === 'string') {
|
||||||
return {
|
return {
|
||||||
22
packages/config/src/index.js
Normal file
22
packages/config/src/index.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { eslintrc } from './eslintrc-compact.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types').Config} config
|
||||||
|
* Array or function returning an array of ESLint's configuration objects array to be used.
|
||||||
|
*
|
||||||
|
* @param {import('./types').EnvOptions | undefined} environment
|
||||||
|
* An object with environment variables to be declared and used by the configuration.
|
||||||
|
*
|
||||||
|
* @returns {Promise<import('eslint').Linter.FlatConfig[]>}
|
||||||
|
* The array of ESLint's configuration objects.
|
||||||
|
*/
|
||||||
|
export async function defineConfig(config, environment = {}) {
|
||||||
|
for (const [key, value] of Object.entries(environment)) {
|
||||||
|
process.env[key] = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
return typeof config === 'function' ? await config(eslintrc) : config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { default as configs } from './configs/index.js';
|
||||||
|
export { default as presets } from './presets/index.js';
|
||||||
|
|
||||||
23
packages/config/src/presets/default.js
Normal file
23
packages/config/src/presets/default.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import configs from '../configs/index.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Readonly<import('eslint').Linter.FlatConfig[]>}
|
||||||
|
*/
|
||||||
|
const preset = [
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'**/node_modules',
|
||||||
|
'**/dist',
|
||||||
|
'**/fixtures',
|
||||||
|
'**/pnpm-lock.yaml',
|
||||||
|
'**/yarn.lock',
|
||||||
|
'**/package-lock.json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
configs.common,
|
||||||
|
configs.recommended,
|
||||||
|
configs.formatting,
|
||||||
|
configs.jsdoc,
|
||||||
|
configs.typescript,
|
||||||
|
];
|
||||||
|
export default preset;
|
||||||
4
packages/config/src/presets/index.js
Normal file
4
packages/config/src/presets/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
import defaultPreset from './default.js';
|
||||||
|
|
||||||
|
export default { default: defaultPreset };
|
||||||
56
packages/config/src/types.d.ts
vendored
Normal file
56
packages/config/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type { FlatCompat } from '@eslint/eslintrc';
|
||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
type MaybePromise<T> = Promise<T> | T;
|
||||||
|
export type Config = Linter.FlatConfig[] | ((eslintrc: FlatCompat) => MaybePromise<Linter.FlatConfig[]>);
|
||||||
|
|
||||||
|
export interface EnvOptions {
|
||||||
|
ESLIT_TSCONFIG?: string | string[] | true
|
||||||
|
ESLIT_ROOT?: string
|
||||||
|
ESLIT_INDENT?: 'tab' | 'space' | number
|
||||||
|
ESLIT_ECMASCRIPT?: Linter.ParserOptions['ecmaVersion']
|
||||||
|
ESLIT_QUOTES?: 'single' | 'double'
|
||||||
|
ESLIT_SEMI?: 'never' | 'always'
|
||||||
|
/**
|
||||||
|
* Typescript's type-checking is able to infer types from parameters.
|
||||||
|
* So using an explicit `:` type annotation isn't obligatory.
|
||||||
|
*
|
||||||
|
* But, **by default in strict mode**, type annotations are always mandated to make
|
||||||
|
* the code more readable, explicit and robust to changes.
|
||||||
|
*
|
||||||
|
* See {@link https://typescript-eslint.io/rules/no-inferrable-types typescript-eslint documentation }
|
||||||
|
* for more info.
|
||||||
|
* ---
|
||||||
|
* **Option: `never`** (default)
|
||||||
|
* Types are always explicit in Typescript
|
||||||
|
*
|
||||||
|
* @example ```ts
|
||||||
|
// Typescript
|
||||||
|
const id: number = 10;
|
||||||
|
const name: string = 'foo';
|
||||||
|
```
|
||||||
|
* ---
|
||||||
|
* **Option: `always`**
|
||||||
|
* Types are always inferred in Typescript
|
||||||
|
*
|
||||||
|
* @example ```ts
|
||||||
|
// Typescript
|
||||||
|
const id = 10;
|
||||||
|
const name = 'foo';
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
ESLIT_INFER_TYPES?: inferrableTypesOptions
|
||||||
|
[ENV: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export type inferrableTypesOptions = [
|
||||||
|
'never' | 'always',
|
||||||
|
{
|
||||||
|
/** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreparameters} */
|
||||||
|
parameters?: boolean
|
||||||
|
/** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreproperties} */
|
||||||
|
properties?: boolean
|
||||||
|
/** @see {@link https://typescript-eslint.io/rules/explicit-function-return-type} */
|
||||||
|
returnValues?: boolean
|
||||||
|
},
|
||||||
|
] | 'never' | 'always';
|
||||||
3
packages/core/index.d.ts
vendored
3
packages/core/index.d.ts
vendored
@@ -1,3 +0,0 @@
|
|||||||
import type { Config, ESConfig } from './src/types';
|
|
||||||
|
|
||||||
export async function defineConfig(config: Config): Promise<ESConfig[]>;
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
import globals from 'globals';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('../types').Config['environment']} environment
|
|
||||||
* Manual configuration of environments, if undefined,
|
|
||||||
* the function tries to detect the environment automatically
|
|
||||||
* @returns {import('../types').ESConfig[]}
|
|
||||||
* ESLint configuration with global variables and environment
|
|
||||||
*/
|
|
||||||
export function environments(environment) {
|
|
||||||
|
|
||||||
environment ||= {
|
|
||||||
node:
|
|
||||||
typeof window === 'undefined' &&
|
|
||||||
typeof process !== 'undefined' &&
|
|
||||||
typeof require !== 'function',
|
|
||||||
deno:
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
// @ts-expect-error because this package is develop in node
|
|
||||||
typeof Deno !== 'undefined',
|
|
||||||
browser:
|
|
||||||
typeof window !== 'undefined',
|
|
||||||
};
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
|
||||||
languageOptions: {
|
|
||||||
ecmaVersion: environment.ecmaVersion ?? 'latest',
|
|
||||||
globals: {
|
|
||||||
...globals.builtin,
|
|
||||||
...environment.customGlobals,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.cjs', '**/*.cts'],
|
|
||||||
languageOptions: {
|
|
||||||
sourceType: 'commonjs',
|
|
||||||
globals: {
|
|
||||||
...globals.node,
|
|
||||||
...globals.commonjs,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.mts'],
|
|
||||||
languageOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
globals: {
|
|
||||||
...(environment.node ? globals.nodeBuiltin : {}),
|
|
||||||
...(environment.browser || environment.deno ? globals.browser : {}),
|
|
||||||
...(environment.deno ? { Deno: true } : {}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export { default as common } from './common.js';
|
|
||||||
export { default as formatting } from './formatting.js';
|
|
||||||
export { default as jsdoc } from './jsdoc.js';
|
|
||||||
export { default as typescript } from './typescript.js';
|
|
||||||
export * from './environments.js';
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* JSDoc rules overrides
|
|
||||||
*
|
|
||||||
* @type {import('../types').ESConfig}
|
|
||||||
*/
|
|
||||||
const config = {
|
|
||||||
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
|
||||||
rules: {
|
|
||||||
'jsdoc/tag-lines': ['error', 'always', {
|
|
||||||
count: 1,
|
|
||||||
applyToEndTag: false,
|
|
||||||
startLines: 1,
|
|
||||||
endLines: 0,
|
|
||||||
tags: {
|
|
||||||
param: { lines: 'never' },
|
|
||||||
},
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
export default config;
|
|
||||||
12
packages/core/src/env.d.ts
vendored
12
packages/core/src/env.d.ts
vendored
@@ -1,12 +0,0 @@
|
|||||||
import type { Config } from './types';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace NodeJS {
|
|
||||||
interface ProcessEnv {
|
|
||||||
READABLE_ESLINT_STRICT: Config['strict']
|
|
||||||
READABLE_ESLINT_OPTIONS: Config['options']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import { eslintrc } from './eslintrc-compact.js';
|
|
||||||
import tsESlint from '@typescript-eslint/eslint-plugin';
|
|
||||||
import tsParser from '@typescript-eslint/parser';
|
|
||||||
import jsdoc from 'eslint-plugin-jsdoc';
|
|
||||||
import js from '@eslint/js';
|
|
||||||
import * as configs from './configs/index.js';
|
|
||||||
import { getTsConfigs } from './tsconfigs.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {import('./types').Config} userConfig
|
|
||||||
* User configuration
|
|
||||||
* @returns {Promise<import('./types').ESConfig[]>}
|
|
||||||
* The complete list of configs for ESLint
|
|
||||||
*/
|
|
||||||
export async function defineConfig(userConfig) {
|
|
||||||
|
|
||||||
userConfig.strict ??= true;
|
|
||||||
userConfig.rootDir ??= process.cwd();
|
|
||||||
userConfig.tsconfig ??= await getTsConfigs(userConfig.rootDir);
|
|
||||||
|
|
||||||
process.env.READABLE_ESLINT_STRICT = userConfig.strict;
|
|
||||||
process.env.READABLE_ESLINT_OPTIONS = {
|
|
||||||
inferrableTypes: userConfig.strict ? 'always' : 'never',
|
|
||||||
...userConfig.options,
|
|
||||||
};
|
|
||||||
|
|
||||||
const userOverrides = (typeof userConfig.overrides !== 'function'
|
|
||||||
? userConfig.overrides
|
|
||||||
: await userConfig.overrides(eslintrc)) ?? [];
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
'**/node_modules',
|
|
||||||
'**/dist',
|
|
||||||
'**/fixtures',
|
|
||||||
'**/pnpm-lock.yaml',
|
|
||||||
'**/yarn.lock',
|
|
||||||
'**/package-lock.json',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
js.configs.recommended,
|
|
||||||
{
|
|
||||||
files: ['**/*.js', '**/*.cjs', '**/*.mjs', '**/*.ts', '**/*.cts', '**/*.mts'],
|
|
||||||
plugins: {
|
|
||||||
'@typescript-eslint': tsESlint,
|
|
||||||
/**
|
|
||||||
* @todo
|
|
||||||
* Fix eslint-plugin-jsdoc type definitions.
|
|
||||||
* _Typescript should have detected [eslint-plugin-jsdoc.d.ts](./@types/eslint-plugin-jsdoc.d.ts)._
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
'jsdoc': jsdoc,
|
|
||||||
},
|
|
||||||
languageOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
parser: tsParser,
|
|
||||||
parserOptions: {
|
|
||||||
project: userConfig.tsconfig,
|
|
||||||
tsconfigRootDir: userConfig.rootDir,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// See plugins['jsdoc'] for more info on this error
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
rules: {
|
|
||||||
...tsESlint.configs.recommended.rules,
|
|
||||||
...tsESlint.configs['recommended-requiring-type-checking'].rules,
|
|
||||||
...tsESlint.configs['eslint-recommended'].rules,
|
|
||||||
...(userConfig.strict ? tsESlint.configs.strict.rules : null),
|
|
||||||
// See plugins['jsdoc'] for more info on this error
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
||||||
...jsdoc.configs['recommended-typescript-flavor-error'].rules,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
configs.common,
|
|
||||||
configs.formatting,
|
|
||||||
configs.jsdoc,
|
|
||||||
configs.typescript,
|
|
||||||
...configs.environments(userConfig.environment),
|
|
||||||
...userOverrides,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
import { existsSync } from 'node:fs';
|
|
||||||
import { readFile } from 'node:fs/promises';
|
|
||||||
import { join, normalize } from 'node:path';
|
|
||||||
|
|
||||||
/** @type {(...path: string[]) => string} */
|
|
||||||
function toPath(...path) {
|
|
||||||
return normalize(join(...path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {(...path: string[]) => boolean} */
|
|
||||||
function exists(...path) {
|
|
||||||
return existsSync(toPath(...path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} directory what the root directory to detect an workspace/monorepo configuration file
|
|
||||||
* @returns {Promise<string[]>} list of possible paths of packages' tsconfig.json and jsconfig.json files
|
|
||||||
*/
|
|
||||||
async function getMonorepoConfigs(directory) {
|
|
||||||
|
|
||||||
/** @type {string[]} */
|
|
||||||
const paths = [];
|
|
||||||
|
|
||||||
if (exists(directory, 'pnpm-workspace.yaml') || exists(directory, 'pnpm-workspace.yml')) {
|
|
||||||
|
|
||||||
const YAML = await import('yaml');
|
|
||||||
|
|
||||||
const yamlFilePath = exists(directory, 'pnpm-workspace.yaml')
|
|
||||||
? join(directory, 'pnpm-workspace.yaml')
|
|
||||||
: join(directory, 'pnpm-workspace.yml');
|
|
||||||
|
|
||||||
/** @type {{packages?: string[], [properties: string]: unknown}} */
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
const pnpmWorkspaces = YAML.parse(await readFile(yamlFilePath, 'utf-8'));
|
|
||||||
|
|
||||||
const files = pnpmWorkspaces.packages?.map(w => [
|
|
||||||
toPath(directory, w, 'tsconfig.json'),
|
|
||||||
toPath(directory, w, 'jsconfig.json'),
|
|
||||||
]).flat() ?? [];
|
|
||||||
|
|
||||||
paths.push(...files);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (exists(directory, 'package.json')) {
|
|
||||||
/** @type {{workspaces?: string[], [properties: string]: unknown}} */
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
const packageJson = JSON.parse(await readFile(join(directory, 'package.json'), 'utf-8'));
|
|
||||||
|
|
||||||
const files = packageJson.workspaces?.map(w => [
|
|
||||||
toPath(directory, w, 'tsconfig.json'),
|
|
||||||
toPath(directory, w, 'jsconfig.json'),
|
|
||||||
]).flat() ?? [];
|
|
||||||
|
|
||||||
paths.push(...files);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} directory what the root directory to work on
|
|
||||||
* @returns {Promise<string[]>} list of tsconfig.json and jsconfig.json file paths
|
|
||||||
*/
|
|
||||||
export async function getTsConfigs(directory) {
|
|
||||||
|
|
||||||
const rootTSConfig = exists(directory, 'tsconfig.eslint.json')
|
|
||||||
? toPath(directory, 'tsconfig.eslint.json')
|
|
||||||
: exists(directory, 'tsconfig.json')
|
|
||||||
? toPath(directory, 'tsconfig.json')
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const rootJSConfig = exists(directory, 'jsconfig.eslint.json')
|
|
||||||
? toPath(directory, 'jsconfig.eslint.json')
|
|
||||||
: exists(directory, 'jsconfig.json')
|
|
||||||
? toPath(directory, 'jsconfig.json')
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const monorepoConfigs = await getMonorepoConfigs(directory);
|
|
||||||
|
|
||||||
const paths = /** @type {string[]} */
|
|
||||||
([rootTSConfig, rootJSConfig, ...monorepoConfigs]).filter(p => p);
|
|
||||||
|
|
||||||
return paths;
|
|
||||||
|
|
||||||
}
|
|
||||||
112
packages/core/src/types.d.ts
vendored
112
packages/core/src/types.d.ts
vendored
@@ -1,112 +0,0 @@
|
|||||||
import type { FlatCompat } from '@eslint/eslintrc';
|
|
||||||
import type { Linter } from 'eslint';
|
|
||||||
|
|
||||||
export type ESConfig = Readonly<Linter.FlatConfig>;
|
|
||||||
|
|
||||||
export interface Config {
|
|
||||||
tsconfig?: string | string[] | true
|
|
||||||
strict?: boolean
|
|
||||||
rootDir?: string
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* Environment and language settings
|
|
||||||
*
|
|
||||||
* If no globals/environments are defined, the configuration tries to detect the
|
|
||||||
* environment using `typeof`. See each option for more explanation
|
|
||||||
*/
|
|
||||||
environment?: {
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* Enables NodeJS environment globals.
|
|
||||||
*
|
|
||||||
* **Note:** this does not enables CommonJS globals, if you are using
|
|
||||||
* CommonJS, use a file ending in `.cjs` or `.cts`
|
|
||||||
*
|
|
||||||
* @example // Detects if
|
|
||||||
* typeof window === 'undefined' &&
|
|
||||||
* typeof process !== 'undefined' &&
|
|
||||||
* typeof require !== 'undefined'
|
|
||||||
*/
|
|
||||||
node?: boolean
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* Enables the global `Deno` namespace and browser/web standards globals
|
|
||||||
*
|
|
||||||
* @example // Detects if
|
|
||||||
* typeof window !== 'undefined' &&
|
|
||||||
* typeof Deno !== 'undefined'
|
|
||||||
*/
|
|
||||||
deno?: boolean
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* Enables browser/web standards globals
|
|
||||||
*
|
|
||||||
* @example // Detects if
|
|
||||||
* typeof window !== 'undefined'
|
|
||||||
*/
|
|
||||||
browser?: boolean
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* What JavaScript (ECMAScript) that will be evaluated
|
|
||||||
*
|
|
||||||
* **Defaults to `latest`**
|
|
||||||
*/
|
|
||||||
ecmaVersion?: Linter.ParserOptions['ecmaVersion']
|
|
||||||
/**
|
|
||||||
* @summary
|
|
||||||
* User defined globals for edge-cases or if available aren't enough
|
|
||||||
*
|
|
||||||
* **Does not overrides previous enabled ones**
|
|
||||||
*/
|
|
||||||
customGlobals?: Record<string, boolean>
|
|
||||||
}
|
|
||||||
options?: {
|
|
||||||
indent?: 'tab' | 'space'
|
|
||||||
quotes?: 'single' | 'double'
|
|
||||||
semi?: 'never' | 'always'
|
|
||||||
/**
|
|
||||||
* Typescript's type-checking is able to infer types from parameters.
|
|
||||||
* So using an explicit `:` type annotation isn't obligatory.
|
|
||||||
*
|
|
||||||
* But, **by default in strict mode**, type annotations are always mandated to make
|
|
||||||
* the code more readable, explicit and robust to changes.
|
|
||||||
*
|
|
||||||
* See {@link https://typescript-eslint.io/rules/no-inferrable-types typescript-eslint documentation }
|
|
||||||
* for more info.
|
|
||||||
* ---
|
|
||||||
* **Option: `never`** (default)
|
|
||||||
* Types are always explicit in Typescript
|
|
||||||
*
|
|
||||||
* @example ```ts
|
|
||||||
// Typescript
|
|
||||||
const id: number = 10;
|
|
||||||
const name: string = 'foo';
|
|
||||||
```
|
|
||||||
* ---
|
|
||||||
* **Option: `always`**
|
|
||||||
* Types are always inferred in Typescript
|
|
||||||
*
|
|
||||||
* @example ```ts
|
|
||||||
// Typescript
|
|
||||||
const id = 10;
|
|
||||||
const name = 'foo';
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
inferrableTypes?: inferrableTypesOptions
|
|
||||||
}
|
|
||||||
overrides?:
|
|
||||||
| Linter.FlatConfig[]
|
|
||||||
| ((eslintrc: FlatCompat) => Linter.FlatConfig[] | Promise<Linter.FlatConfig[]>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export type inferrableTypesOptions = [
|
|
||||||
'never' | 'always',
|
|
||||||
{
|
|
||||||
/** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreparameters} */
|
|
||||||
parameters?: boolean
|
|
||||||
/** @see {@link https://typescript-eslint.io/rules/no-inferrable-types#ignoreproperties} */
|
|
||||||
properties?: boolean
|
|
||||||
/** @see {@link https://typescript-eslint.io/rules/explicit-function-return-type} */
|
|
||||||
returnValues?: boolean
|
|
||||||
},
|
|
||||||
] | 'never' | 'always';
|
|
||||||
273
pnpm-lock.yaml
generated
273
pnpm-lock.yaml
generated
@@ -8,9 +8,9 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslit/core':
|
'@eslit/config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:packages/core
|
version: link:packages/config
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@changesets/cli':
|
'@changesets/cli':
|
||||||
specifier: ^2.26.2
|
specifier: ^2.26.2
|
||||||
@@ -21,6 +21,12 @@ importers:
|
|||||||
'@commitlint/types':
|
'@commitlint/types':
|
||||||
specifier: ^17.4.4
|
specifier: ^17.4.4
|
||||||
version: 17.4.4
|
version: 17.4.4
|
||||||
|
'@eslit/cli':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:packages/cli
|
||||||
|
'@svitejs/changesets-changelog-github-compact':
|
||||||
|
specifier: ^1.1.0
|
||||||
|
version: 1.1.0
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.44.0
|
specifier: ^8.44.0
|
||||||
version: 8.44.0
|
version: 8.44.0
|
||||||
@@ -31,8 +37,23 @@ importers:
|
|||||||
specifier: ^1.10.9
|
specifier: ^1.10.9
|
||||||
version: 1.10.9
|
version: 1.10.9
|
||||||
|
|
||||||
|
fixtures/library:
|
||||||
|
dependencies:
|
||||||
|
'@eslit/cli':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/cli
|
||||||
|
|
||||||
|
fixtures/monorepo:
|
||||||
|
devDependencies:
|
||||||
|
'@eslit/cli':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/cli
|
||||||
|
|
||||||
fixtures/svelte:
|
fixtures/svelte:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@eslit/cli':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/cli
|
||||||
'@fontsource/fira-mono':
|
'@fontsource/fira-mono':
|
||||||
specifier: ^4.5.10
|
specifier: ^4.5.10
|
||||||
version: 4.5.10
|
version: 4.5.10
|
||||||
@@ -76,7 +97,47 @@ importers:
|
|||||||
specifier: ^4.4.2
|
specifier: ^4.4.2
|
||||||
version: 4.4.2
|
version: 4.4.2
|
||||||
|
|
||||||
packages/core:
|
packages/cli:
|
||||||
|
dependencies:
|
||||||
|
cardinal:
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1
|
||||||
|
commander:
|
||||||
|
specifier: ^11.0.0
|
||||||
|
version: 11.0.0
|
||||||
|
nanospinner:
|
||||||
|
specifier: ^1.1.0
|
||||||
|
version: 1.1.0
|
||||||
|
picocolors:
|
||||||
|
specifier: ^1.0.0
|
||||||
|
version: 1.0.0
|
||||||
|
picomatch:
|
||||||
|
specifier: ^2.3.1
|
||||||
|
version: 2.3.1
|
||||||
|
prompts:
|
||||||
|
specifier: ^2.4.2
|
||||||
|
version: 2.4.2
|
||||||
|
recast:
|
||||||
|
specifier: ^0.23.3
|
||||||
|
version: 0.23.3
|
||||||
|
sisteransi:
|
||||||
|
specifier: ^1.0.5
|
||||||
|
version: 1.0.5
|
||||||
|
yaml:
|
||||||
|
specifier: ^2.3.1
|
||||||
|
version: 2.3.1
|
||||||
|
devDependencies:
|
||||||
|
'@types/estree':
|
||||||
|
specifier: ^1.0.1
|
||||||
|
version: 1.0.1
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^20.4.2
|
||||||
|
version: 20.4.2
|
||||||
|
'@types/prompts':
|
||||||
|
specifier: ^2.4.4
|
||||||
|
version: 2.4.4
|
||||||
|
|
||||||
|
packages/config:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
@@ -96,9 +157,6 @@ importers:
|
|||||||
globals:
|
globals:
|
||||||
specifier: ^13.20.0
|
specifier: ^13.20.0
|
||||||
version: 13.20.0
|
version: 13.20.0
|
||||||
yaml:
|
|
||||||
specifier: ^2.3.1
|
|
||||||
version: 2.3.1
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/eslint__js':
|
'@types/eslint__js':
|
||||||
specifier: ^8.42.0
|
specifier: ^8.42.0
|
||||||
@@ -257,6 +315,15 @@ packages:
|
|||||||
semver: 7.5.4
|
semver: 7.5.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@changesets/get-github-info@0.5.2:
|
||||||
|
resolution: {integrity: sha512-JppheLu7S114aEs157fOZDjFqUDpm7eHdq5E8SSR0gUBTEK0cNSHsrSR5a66xs0z3RWuo46QvA3vawp8BxDHvg==}
|
||||||
|
dependencies:
|
||||||
|
dataloader: 1.4.0
|
||||||
|
node-fetch: 2.6.12
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@changesets/get-release-plan@3.0.17:
|
/@changesets/get-release-plan@3.0.17:
|
||||||
resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==}
|
resolution: {integrity: sha512-6IwKTubNEgoOZwDontYc2x2cWXfr6IKxP3IhKeK+WjyD6y3M4Gl/jdQvBw+m/5zWILSOCAaGLu2ZF6Q+WiPniw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -781,6 +848,16 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@svitejs/changesets-changelog-github-compact@1.1.0:
|
||||||
|
resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==}
|
||||||
|
engines: {node: ^14.13.1 || ^16.0.0 || >=18}
|
||||||
|
dependencies:
|
||||||
|
'@changesets/get-github-info': 0.5.2
|
||||||
|
dotenv: 16.3.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/cookie@0.5.1:
|
/@types/cookie@0.5.1:
|
||||||
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -827,6 +904,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/prompts@2.4.4:
|
||||||
|
resolution: {integrity: sha512-p5N9uoTH76lLvSAaYSZtBCdEXzpOOufsRjnhjVSrZGXikVGHX9+cc9ERtHRV4hvBKHyZb1bg4K+56Bd2TqUn4A==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 20.4.2
|
||||||
|
kleur: 3.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/pug@2.0.6:
|
/@types/pug@2.0.6:
|
||||||
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
|
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1137,6 +1221,10 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
/ansicolors@0.3.2:
|
||||||
|
resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/anymatch@3.1.3:
|
/anymatch@3.1.3:
|
||||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -1195,10 +1283,25 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/assert@2.0.0:
|
||||||
|
resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
|
||||||
|
dependencies:
|
||||||
|
es6-object-assign: 1.1.0
|
||||||
|
is-nan: 1.3.2
|
||||||
|
object-is: 1.1.5
|
||||||
|
util: 0.12.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ast-types@0.16.1:
|
||||||
|
resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.4.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/available-typed-arrays@1.0.5:
|
/available-typed-arrays@1.0.5:
|
||||||
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
|
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/axobject-query@3.2.1:
|
/axobject-query@3.2.1:
|
||||||
resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
|
resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
|
||||||
@@ -1260,7 +1363,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
get-intrinsic: 1.2.1
|
get-intrinsic: 1.2.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/callsites@3.1.0:
|
/callsites@3.1.0:
|
||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
@@ -1280,6 +1382,14 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/cardinal@2.1.1:
|
||||||
|
resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
ansicolors: 0.3.2
|
||||||
|
redeyed: 2.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/chalk@2.4.2:
|
/chalk@2.4.2:
|
||||||
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1371,6 +1481,11 @@ packages:
|
|||||||
/color-name@1.1.4:
|
/color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
|
/commander@11.0.0:
|
||||||
|
resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/comment-parser@1.3.1:
|
/comment-parser@1.3.1:
|
||||||
resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==}
|
resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
@@ -1446,6 +1561,10 @@ packages:
|
|||||||
stream-transform: 2.1.3
|
stream-transform: 2.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dataloader@1.4.0:
|
||||||
|
resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/debug@4.3.4:
|
/debug@4.3.4:
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -1490,7 +1609,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-property-descriptors: 1.0.0
|
has-property-descriptors: 1.0.0
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/dequal@2.0.3:
|
/dequal@2.0.3:
|
||||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||||
@@ -1525,6 +1643,11 @@ packages:
|
|||||||
is-obj: 2.0.0
|
is-obj: 2.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/dotenv@16.3.1:
|
||||||
|
resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/emoji-regex@8.0.0:
|
/emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1606,6 +1729,10 @@ packages:
|
|||||||
is-symbol: 1.0.4
|
is-symbol: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/es6-object-assign@1.1.0:
|
||||||
|
resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/es6-promise@3.3.1:
|
/es6-promise@3.3.1:
|
||||||
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1843,7 +1970,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
|
||||||
|
|
||||||
/esquery@1.5.0:
|
/esquery@1.5.0:
|
||||||
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
|
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
|
||||||
@@ -1961,7 +2087,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
is-callable: 1.2.7
|
is-callable: 1.2.7
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fs-extra@7.0.1:
|
/fs-extra@7.0.1:
|
||||||
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
|
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
|
||||||
@@ -1994,7 +2119,6 @@ packages:
|
|||||||
|
|
||||||
/function-bind@1.1.1:
|
/function-bind@1.1.1:
|
||||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/function.prototype.name@1.1.5:
|
/function.prototype.name@1.1.5:
|
||||||
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
|
resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==}
|
||||||
@@ -2022,7 +2146,6 @@ packages:
|
|||||||
has: 1.0.3
|
has: 1.0.3
|
||||||
has-proto: 1.0.1
|
has-proto: 1.0.1
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/get-symbol-description@1.0.0:
|
/get-symbol-description@1.0.0:
|
||||||
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
|
resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
|
||||||
@@ -2082,7 +2205,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.1
|
get-intrinsic: 1.2.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/graceful-fs@4.2.11:
|
/graceful-fs@4.2.11:
|
||||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||||
@@ -2117,31 +2239,26 @@ packages:
|
|||||||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
get-intrinsic: 1.2.1
|
get-intrinsic: 1.2.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has-proto@1.0.1:
|
/has-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
|
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has-symbols@1.0.3:
|
/has-symbols@1.0.3:
|
||||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has-tostringtag@1.0.0:
|
/has-tostringtag@1.0.0:
|
||||||
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
|
resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
dev: true
|
|
||||||
|
|
||||||
/has@1.0.3:
|
/has@1.0.3:
|
||||||
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
dev: true
|
|
||||||
|
|
||||||
/hosted-git-info@2.8.9:
|
/hosted-git-info@2.8.9:
|
||||||
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||||
@@ -2206,6 +2323,14 @@ packages:
|
|||||||
side-channel: 1.0.4
|
side-channel: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-arguments@1.1.1:
|
||||||
|
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
|
has-tostringtag: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-array-buffer@3.0.2:
|
/is-array-buffer@3.0.2:
|
||||||
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
|
resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2249,7 +2374,6 @@ packages:
|
|||||||
/is-callable@1.2.7:
|
/is-callable@1.2.7:
|
||||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-ci@3.0.1:
|
/is-ci@3.0.1:
|
||||||
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
|
resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==}
|
||||||
@@ -2280,12 +2404,27 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/is-generator-function@1.0.10:
|
||||||
|
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
has-tostringtag: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-glob@4.0.3:
|
/is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
|
|
||||||
|
/is-nan@1.3.2:
|
||||||
|
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
|
define-properties: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/is-negative-zero@2.0.2:
|
/is-negative-zero@2.0.2:
|
||||||
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
|
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2366,7 +2505,6 @@ packages:
|
|||||||
for-each: 0.3.3
|
for-each: 0.3.3
|
||||||
gopd: 1.0.1
|
gopd: 1.0.1
|
||||||
has-tostringtag: 1.0.0
|
has-tostringtag: 1.0.0
|
||||||
dev: true
|
|
||||||
|
|
||||||
/is-weakref@1.0.2:
|
/is-weakref@1.0.2:
|
||||||
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
|
||||||
@@ -2426,6 +2564,10 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/kleur@3.0.3:
|
||||||
|
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
/kleur@4.1.5:
|
/kleur@4.1.5:
|
||||||
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2618,12 +2760,30 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/nanospinner@1.1.0:
|
||||||
|
resolution: {integrity: sha512-yFvNYMig4AthKYfHFl1sLj7B2nkHL4lzdig4osvl9/LdGbXwrdFRoqBS98gsEsOakr0yH+r5NZ/1Y9gdVB8trA==}
|
||||||
|
dependencies:
|
||||||
|
picocolors: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/natural-compare-lite@1.4.0:
|
/natural-compare-lite@1.4.0:
|
||||||
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
|
||||||
|
|
||||||
/natural-compare@1.4.0:
|
/natural-compare@1.4.0:
|
||||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
|
||||||
|
/node-fetch@2.6.12:
|
||||||
|
resolution: {integrity: sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/normalize-package-data@2.5.0:
|
/normalize-package-data@2.5.0:
|
||||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2642,10 +2802,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/object-is@1.1.5:
|
||||||
|
resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.2
|
||||||
|
define-properties: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/object-keys@1.1.1:
|
/object-keys@1.1.1:
|
||||||
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/object.assign@4.1.4:
|
/object.assign@4.1.4:
|
||||||
resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
|
resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
|
||||||
@@ -2771,7 +2938,6 @@ packages:
|
|||||||
|
|
||||||
/picocolors@1.0.0:
|
/picocolors@1.0.0:
|
||||||
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/picomatch@2.3.1:
|
/picomatch@2.3.1:
|
||||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
@@ -2844,6 +3010,14 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/prompts@2.4.2:
|
||||||
|
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
dependencies:
|
||||||
|
kleur: 3.0.3
|
||||||
|
sisteransi: 1.0.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
/pseudomap@1.0.2:
|
/pseudomap@1.0.2:
|
||||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -2901,6 +3075,17 @@ packages:
|
|||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/recast@0.23.3:
|
||||||
|
resolution: {integrity: sha512-HbCVFh2ANP6a09nzD4lx7XthsxMOJWKX5pIcUwtLrmeEIl3I0DwjCoVXDE0Aobk+7k/mS3H50FK4iuYArpcT6Q==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
dependencies:
|
||||||
|
assert: 2.0.0
|
||||||
|
ast-types: 0.16.1
|
||||||
|
esprima: 4.0.1
|
||||||
|
source-map: 0.6.1
|
||||||
|
tslib: 2.4.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/redent@3.0.0:
|
/redent@3.0.0:
|
||||||
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2909,6 +3094,12 @@ packages:
|
|||||||
strip-indent: 3.0.0
|
strip-indent: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/redeyed@2.1.1:
|
||||||
|
resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==}
|
||||||
|
dependencies:
|
||||||
|
esprima: 4.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/regenerator-runtime@0.13.11:
|
/regenerator-runtime@0.13.11:
|
||||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||||
dev: true
|
dev: true
|
||||||
@@ -3075,6 +3266,10 @@ packages:
|
|||||||
totalist: 3.0.1
|
totalist: 3.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/sisteransi@1.0.5:
|
||||||
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/slash@3.0.0:
|
/slash@3.0.0:
|
||||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3107,6 +3302,11 @@ packages:
|
|||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/source-map@0.6.1:
|
||||||
|
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/spawndamnit@2.0.0:
|
/spawndamnit@2.0.0:
|
||||||
resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==}
|
resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3366,6 +3566,10 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tr46@0.0.3:
|
||||||
|
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/trim-newlines@3.0.1:
|
/trim-newlines@3.0.1:
|
||||||
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3386,7 +3590,6 @@ packages:
|
|||||||
|
|
||||||
/tslib@2.4.1:
|
/tslib@2.4.1:
|
||||||
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
|
resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tsutils@3.21.0(typescript@5.0.2):
|
/tsutils@3.21.0(typescript@5.0.2):
|
||||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||||
@@ -3543,6 +3746,16 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.0
|
punycode: 2.3.0
|
||||||
|
|
||||||
|
/util@0.12.5:
|
||||||
|
resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
is-arguments: 1.1.1
|
||||||
|
is-generator-function: 1.0.10
|
||||||
|
is-typed-array: 1.1.10
|
||||||
|
which-typed-array: 1.1.10
|
||||||
|
dev: false
|
||||||
|
|
||||||
/validate-npm-package-license@3.0.4:
|
/validate-npm-package-license@3.0.4:
|
||||||
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3602,6 +3815,17 @@ packages:
|
|||||||
defaults: 1.0.4
|
defaults: 1.0.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/webidl-conversions@3.0.1:
|
||||||
|
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/whatwg-url@5.0.0:
|
||||||
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/which-boxed-primitive@1.0.2:
|
/which-boxed-primitive@1.0.2:
|
||||||
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3634,7 +3858,6 @@ packages:
|
|||||||
gopd: 1.0.1
|
gopd: 1.0.1
|
||||||
has-tostringtag: 1.0.0
|
has-tostringtag: 1.0.0
|
||||||
is-typed-array: 1.1.10
|
is-typed-array: 1.1.10
|
||||||
dev: true
|
|
||||||
|
|
||||||
/which@1.3.1:
|
/which@1.3.1:
|
||||||
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
|
||||||
|
|||||||
10
renovate.json
Normal file
10
renovate.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/fixtures/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user