improvement(fluid-build): Support include/ignore settings in Biome task (#21826)
When the Biome fluid-build task was added it didn't support reading Biome config files. Thadded the capability to read Biome config files, including following `extends` values and merging the resulting include/ignore settings across the files/formatter/linter settings. The BiomeTask now uses this capability to enumerate the files to consider when caching. While we don't use Biome for linting today, I tried to make the code as general-purpose as possible so we could easily adapt the task for linting in the future. I added some test data and test cases. They're not exhaustive but they cover the critical stuff and exercise the recursive loading. [AB#9090](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/9090)
This commit is contained in:
Родитель
ca9728e76b
Коммит
5b3811e137
10
biome.jsonc
10
biome.jsonc
|
@ -39,7 +39,7 @@
|
|||
"*.hbs",
|
||||
|
||||
// Test json
|
||||
"build-tools/packages/build-tools/src/test/data/**",
|
||||
"build-tools/packages/build-tools/src/test/data/biome/empty.jsonc",
|
||||
"experimental/dds/tree/src/test/documents/**",
|
||||
"packages/dds/map/src/test/mocha/snapshots/**/*.json",
|
||||
"packages/dds/matrix/src/test/results/**/*.json",
|
||||
|
@ -99,6 +99,14 @@
|
|||
".memoryTestsOutput/**",
|
||||
".customBenchmarksOutput/**",
|
||||
|
||||
// The paths below are not formatted by Biome. We ignore them explicitly so other tools that read this ignore
|
||||
// list, like fluid-build, know to ignore these files as well.
|
||||
"**/*.md",
|
||||
"**/.gitignore",
|
||||
"**/.npmignore",
|
||||
"**/LICENSE",
|
||||
"**/.changeset/**",
|
||||
|
||||
// Paths below are outside the client release group and aren't configured for biome.
|
||||
"common/build/**",
|
||||
"common/lib/**",
|
||||
|
|
|
@ -6,5 +6,8 @@
|
|||
},
|
||||
"formatter": {
|
||||
"enabled": true
|
||||
},
|
||||
"files": {
|
||||
"ignore": ["packages/build-tools/src/test/data/biome/empty.jsonc"]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"extends": ["../../../biome.jsonc"],
|
||||
"files": {
|
||||
"ignore": ["src/test/data/biome/empty.jsonc"]
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@
|
|||
"eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
|
||||
"format": "npm run format:biome",
|
||||
"format:biome": "biome check --write .",
|
||||
"json-schema-to-typescript:biomeConfigTypes": "json2ts --input node_modules/@biomejs/biome/configuration_schema.json --output src/common/biomeConfigTypes.d.ts",
|
||||
"lint": "npm run eslint",
|
||||
"lint:fix": "npm run eslint:fix",
|
||||
"list-repo-files": "cd ../../.. && git ls-files -co --exclude-standard",
|
||||
|
@ -50,13 +51,16 @@
|
|||
"fs-extra": "^11.2.0",
|
||||
"glob": "^7.2.3",
|
||||
"ignore": "^5.2.4",
|
||||
"json-schema-to-typescript": "^15.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"multimatch": "^5.0.0",
|
||||
"picomatch": "^2.3.1",
|
||||
"rimraf": "^4.4.1",
|
||||
"semver": "^7.5.4",
|
||||
"sort-package-json": "1.57.0",
|
||||
"ts-deepmerge": "^7.0.0",
|
||||
"ts-morph": "^22.0.0",
|
||||
"type-fest": "^2.19.0",
|
||||
"typescript": "~5.4.5",
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile, stat } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import ignore from "ignore";
|
||||
import * as JSON5 from "json5";
|
||||
import multimatch from "multimatch";
|
||||
import { merge } from "ts-deepmerge";
|
||||
// Note: in more recent versions of type-fest, this type has been replaced with "Tagged"
|
||||
// We are using version 2.x because of this issue: https://github.com/sindresorhus/type-fest/issues/547
|
||||
import type { Opaque } from "type-fest";
|
||||
import type { Configuration as BiomeConfigRaw } from "./biomeConfigTypes";
|
||||
import type { GitRepo } from "./gitRepo";
|
||||
|
||||
// switch to regular import once building ESM
|
||||
const findUp = import("find-up");
|
||||
|
||||
/**
|
||||
* Convenience type to represent a Biome config that has been loaded while following and merging the
|
||||
* "extends" values. This helps differentiate between the single loaded configs and the fully resolved config.
|
||||
*/
|
||||
export type BiomeConfigResolved = Opaque<BiomeConfigRaw, "BiomeConfigResolved">;
|
||||
|
||||
/**
|
||||
* Loads a Biome configuration file _without_ following any 'extends' values. You probably want to use
|
||||
* {@link loadBiomeConfigs} instead of this function.
|
||||
*/
|
||||
async function loadRawBiomeConfig(configPath: string): Promise<BiomeConfigRaw> {
|
||||
const contents = await readFile(configPath, "utf8");
|
||||
const config: BiomeConfigRaw = JSON5.parse(contents);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of absolute paths to Biome config files. The paths are in the order in which they are merged by
|
||||
* Biome. That is, the last item in the array will be the absolute path to `configPath`.
|
||||
*/
|
||||
export async function getAllBiomeConfigPaths(configPath: string): Promise<string[]> {
|
||||
const config = await loadRawBiomeConfig(configPath);
|
||||
let extendedConfigPaths: string[] = [];
|
||||
|
||||
if (config.extends) {
|
||||
const pathsNested = await Promise.all(
|
||||
config.extends.map((configToExtend) =>
|
||||
getAllBiomeConfigPaths(path.join(path.dirname(configPath), configToExtend)),
|
||||
),
|
||||
);
|
||||
extendedConfigPaths = pathsNested.flat();
|
||||
}
|
||||
|
||||
// Add the current config as the last one to be applied when they're merged
|
||||
extendedConfigPaths.push(configPath);
|
||||
return extendedConfigPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a Biome configuration file. If the config extends others, then those are loaded recursively and the results are
|
||||
* merged. Array-type values are not merged, in accordance with how Biome applies configs.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The intent is to merge the configs in the same way that Biome itself does, but the implementation is based on the
|
||||
* Biome documentation, so there may be subtle differences unaccounted for. Where this implementation diverges from
|
||||
* Biome's behavior, this function should be considered incorrect.
|
||||
*
|
||||
* Relevant Biome documentation: {@link https://biomejs.dev/guides/configure-biome/#share-a-configuration-file}
|
||||
*/
|
||||
export async function loadBiomeConfig(configPath: string): Promise<BiomeConfigResolved> {
|
||||
const allConfigPaths = await getAllBiomeConfigPaths(configPath);
|
||||
return loadBiomeConfigs(allConfigPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a set of Biome configs, such as that returned by {@link getAllBiomeConfigPaths}. The configs are loaded
|
||||
* recursively and the results are merged. Array-type values are not merged, in accordance with how Biome applies
|
||||
* configs.
|
||||
*/
|
||||
async function loadBiomeConfigs(allConfigPaths: string[]): Promise<BiomeConfigResolved> {
|
||||
const allConfigs = await Promise.all(
|
||||
allConfigPaths.map((pathToConfig) => loadRawBiomeConfig(pathToConfig)),
|
||||
);
|
||||
|
||||
const mergedConfig = merge.withOptions(
|
||||
{
|
||||
// Biome does not merge arrays
|
||||
mergeArrays: false,
|
||||
},
|
||||
...allConfigs,
|
||||
);
|
||||
|
||||
return mergedConfig as BiomeConfigResolved;
|
||||
}
|
||||
|
||||
export type BiomeIncludeIgnore = "include" | "ignore";
|
||||
export type BiomeConfigSection = "formatter" | "linter";
|
||||
|
||||
/**
|
||||
* Given a Biome config object, returns the combined settings for 'ignore' and 'include' across the 'files', 'formatter'
|
||||
* and 'linter' sections in the config.
|
||||
*/
|
||||
export function getSettingValuesFromBiomeConfig(
|
||||
config: BiomeConfigResolved,
|
||||
section: BiomeConfigSection,
|
||||
kind: BiomeIncludeIgnore,
|
||||
): Set<string> {
|
||||
const generalFiles = config.files?.[kind] ?? [];
|
||||
const sectionFiles = config?.[section]?.[kind] ?? [];
|
||||
return new Set([...generalFiles, ...sectionFiles]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute path to the closest Biome config file found from the current working directory up to the root
|
||||
* of the repo.
|
||||
*
|
||||
* @throws If a Biome config file cannot be found.
|
||||
*/
|
||||
export async function getClosestBiomeConfigPath(
|
||||
cwd: string,
|
||||
stopAt?: string,
|
||||
): Promise<string> {
|
||||
return (await findUp)
|
||||
.findUp(["biome.json", "biome.jsonc"], { cwd, stopAt })
|
||||
.then((config) => {
|
||||
if (config === undefined) {
|
||||
throw new Error(`Can't find biome config file`);
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of absolute paths to files that Biome would format under the provided path. Note that .gitignored
|
||||
* paths are always excluded, regardless of the "vcs" setting in the Biome configuration.
|
||||
*
|
||||
* @param directoryOrConfigFile - A path to a directory or a Biome config file. If a directory is provided, then the
|
||||
* closest Biome configuration will be loaded and used. If a path to a file is provided, it is assumed to be a Biome
|
||||
* config file and will be loaded as such. The directory containing the config file will be used as the working
|
||||
* directory when applying the Biome include/ignore settings.
|
||||
* @param gitRepo - A GitRepo instance that is used to enumerate files.
|
||||
*/
|
||||
export async function getBiomeFormattedFilesFromDirectory(
|
||||
directoryOrConfigFile: string,
|
||||
gitRepo: GitRepo,
|
||||
): Promise<string[]> {
|
||||
/**
|
||||
* The repo root-relative path to the directory being used as the Biome working directory.
|
||||
*/
|
||||
let directory: string;
|
||||
let configFile: string;
|
||||
if ((await stat(directoryOrConfigFile)).isFile()) {
|
||||
configFile = directoryOrConfigFile;
|
||||
directory = path.relative(gitRepo.resolvedRoot, path.dirname(directoryOrConfigFile));
|
||||
} else {
|
||||
configFile = await getClosestBiomeConfigPath(directoryOrConfigFile);
|
||||
directory = path.relative(gitRepo.resolvedRoot, directoryOrConfigFile);
|
||||
}
|
||||
const config = await loadBiomeConfig(configFile);
|
||||
return getBiomeFormattedFiles(config, directory, gitRepo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of absolute paths to files that Biome would format under the provided path. Note that .gitignored
|
||||
* paths are always excluded, regardless of the "vcs" setting in the Biome configuration.
|
||||
*
|
||||
* @param config - A resolved/merged Biome config.
|
||||
* @param directory - The directory containing files to be formatted.
|
||||
* @param gitRepo - A GitRepo instance that is used to enumerate files.
|
||||
*/
|
||||
export async function getBiomeFormattedFiles(
|
||||
config: BiomeConfigResolved,
|
||||
directory: string,
|
||||
gitRepo: GitRepo,
|
||||
): Promise<string[]> {
|
||||
const [includeEntries, ignoreEntries] = await Promise.all([
|
||||
getSettingValuesFromBiomeConfig(config, "formatter", "include"),
|
||||
getSettingValuesFromBiomeConfig(config, "formatter", "ignore"),
|
||||
]);
|
||||
|
||||
// From the Biome docs (https://biomejs.dev/guides/how-biome-works/#include-and-ignore-explained):
|
||||
//
|
||||
// "At the moment, Biome uses a glob library that treats all globs as having a **/ prefix.
|
||||
// This means that src/**/*.js and **/src/**/*.js are treated as identical. They match both src/file.js and
|
||||
// test/src/file.js. This is something we plan to fix in Biome v2.0.0."
|
||||
const prefixedIncludes = [...includeEntries].map((glob) => `**/${glob}`);
|
||||
const prefixedIgnores = [...ignoreEntries].map((glob) => `**/${glob}`);
|
||||
|
||||
/**
|
||||
* All files that could possibly be formatted before Biome include and ignore entries are applied. Paths are relative
|
||||
* to the root of the repo.
|
||||
*/
|
||||
const gitLsFiles = new Set(await gitRepo.getFiles(directory));
|
||||
|
||||
/**
|
||||
* An array of repo-relative paths to files included via the 'include' settings in the Biome config.
|
||||
*/
|
||||
const includedPaths =
|
||||
prefixedIncludes.length > 0
|
||||
? // If there are includes, then we filter the possible files using the include globs
|
||||
multimatch([...gitLsFiles], prefixedIncludes)
|
||||
: // No Biome includes were provided, so we include everything git enumerated
|
||||
[...gitLsFiles];
|
||||
|
||||
const ignoreObject = ignore().add(prefixedIgnores);
|
||||
// Note that ignoreObject.filter expects the paths to be relative to the repo root.
|
||||
const filtered = ignoreObject.filter(includedPaths);
|
||||
|
||||
// Convert repo root-relative paths to absolute paths
|
||||
const repoRoot = gitRepo.resolvedRoot;
|
||||
return filtered.map((filePath) => path.resolve(repoRoot, filePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* A class used to simplify access to a Biome config when you want to just load a config and get the file list and
|
||||
* config details. Given a directory and a GitRepo instance, the class calculates and caches the configs and formatted
|
||||
* files. Using this class can be more convenient than using the free functions, especially when you need access to all
|
||||
* the configs and formatted files.
|
||||
*/
|
||||
export class BiomeConfigReader {
|
||||
public get closestConfig(): string {
|
||||
assert(
|
||||
this.allConfigs.length > 0,
|
||||
"BiomeConfigLoader.allConfigs must be initialized before getting the closestConfig.",
|
||||
);
|
||||
// The closest config is the last one in the list of configs, because they're sorted in the order they're applied.
|
||||
// We previously asserted that there is at least one element in the array
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.allConfigs.at(-1)!;
|
||||
}
|
||||
|
||||
public readonly directory: string;
|
||||
|
||||
private constructor(
|
||||
configFile: string,
|
||||
public readonly allConfigs: string[],
|
||||
public readonly mergedConfig: BiomeConfigResolved,
|
||||
public readonly formattedFiles: string[],
|
||||
) {
|
||||
this.directory = path.dirname(configFile);
|
||||
}
|
||||
/**
|
||||
* Create a BiomeConfig instance rooted in the provided directory.
|
||||
*/
|
||||
public static async create(
|
||||
directoryOrConfigFile: string,
|
||||
gitRepo: GitRepo,
|
||||
): Promise<BiomeConfigReader> {
|
||||
/**
|
||||
* The repo root-relative path to the directory being used as the Biome working directory.
|
||||
*/
|
||||
let directory: string;
|
||||
let configFile: string;
|
||||
if ((await stat(directoryOrConfigFile)).isFile()) {
|
||||
configFile = directoryOrConfigFile;
|
||||
directory = path.relative(gitRepo.resolvedRoot, path.dirname(directoryOrConfigFile));
|
||||
} else {
|
||||
configFile = await getClosestBiomeConfigPath(directoryOrConfigFile);
|
||||
directory = path.relative(gitRepo.resolvedRoot, directoryOrConfigFile);
|
||||
}
|
||||
|
||||
const allConfigs = await getAllBiomeConfigPaths(configFile);
|
||||
const mergedConfig = await loadBiomeConfigs(allConfigs);
|
||||
const files = await getBiomeFormattedFiles(mergedConfig, directory, gitRepo);
|
||||
return new BiomeConfigReader(configFile, allConfigs, mergedConfig, files);
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,26 +3,20 @@
|
|||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import { BiomeConfigReader } from "../../../common/biomeConfig";
|
||||
import { getResolvedFluidRoot } from "../../../common/fluidUtils";
|
||||
import { GitRepo } from "../../../common/gitRepo";
|
||||
import { LeafWithFileStatDoneFileTask } from "./leafTask";
|
||||
|
||||
// switch to regular import once building ESM
|
||||
const findUp = import("find-up");
|
||||
|
||||
/**
|
||||
* This task enables incremental build support for Biome formatting tasks. It has important limitations.
|
||||
* This task enables incremental build support for Biome formatting tasks. It reads Biome configuration files to load
|
||||
* the 'include' and 'ignore' settings, and will not consider other files when checking if the task needs to run.
|
||||
*
|
||||
* @remarks
|
||||
* The task will consider the 'extends' value and load nested Biome configs. The configs will be merged, but array-type
|
||||
* settings like 'includes' and 'ignores' are not merged - the top-most config wins for such keys.
|
||||
*
|
||||
* - The task does not read Biome configuration files to determine what files would be formatted. Instead it naively
|
||||
* assumes all files would be formatted.
|
||||
* - All Biome configuration files found when looking up from the package directory to the root of the repo are
|
||||
* considered used, whether the file is used.
|
||||
*
|
||||
* As of version 0.41.0, The task uses a content-based caching strategy, so it is less susceptible to invalidation than
|
||||
* earlier versions which were based on file modification times. However, the limitations above still apply.
|
||||
* Note that .gitignored paths are always excluded, regardless of the "vcs" setting in the Biome configuration.
|
||||
* Internally the task uses git itself to enumerate files, and files that aren't enumerated are not considered.
|
||||
*/
|
||||
export class BiomeTask extends LeafWithFileStatDoneFileTask {
|
||||
// performance note: having individual tasks each acquire repo root and GitRepo
|
||||
|
@ -30,6 +24,9 @@ export class BiomeTask extends LeafWithFileStatDoneFileTask {
|
|||
// to task constructors.
|
||||
private readonly repoRoot = getResolvedFluidRoot(true);
|
||||
private readonly gitRepo = this.repoRoot.then((repoRoot) => new GitRepo(repoRoot));
|
||||
private readonly biomeConfig = this.gitRepo.then((gitRepo) =>
|
||||
BiomeConfigReader.create(this.node.pkg.directory, gitRepo),
|
||||
);
|
||||
|
||||
/**
|
||||
* Use hashes instead of modified times in donefile.
|
||||
|
@ -39,41 +36,17 @@ export class BiomeTask extends LeafWithFileStatDoneFileTask {
|
|||
}
|
||||
|
||||
/**
|
||||
* Includes all files in the task's package directory and any biome config files in the directory tree. Files ignored
|
||||
* by git are excluded.
|
||||
* Includes all files in the the task's package directory that Biome would format and any Biome config files that
|
||||
* apply to the directory. Files ignored by git are excluded.
|
||||
*/
|
||||
protected async getInputFiles(): Promise<string[]> {
|
||||
const repoRoot = await this.repoRoot;
|
||||
const gitRepo = await this.gitRepo;
|
||||
|
||||
const configFiles = await this.getBiomeConfigPaths(this.node.pkg.directory);
|
||||
const files = (await gitRepo.getFiles(this.node.pkg.directory)).map((file) =>
|
||||
path.join(repoRoot, file),
|
||||
);
|
||||
|
||||
return [...new Set([...configFiles, ...files])];
|
||||
const biomeConfig = await this.biomeConfig;
|
||||
// Absolute paths to files that would be formatted by biome.
|
||||
const { formattedFiles, allConfigs } = biomeConfig;
|
||||
return [...new Set([...allConfigs, ...formattedFiles])];
|
||||
}
|
||||
|
||||
protected async getOutputFiles(): Promise<string[]> {
|
||||
// Input and output files are the same.
|
||||
return this.getInputFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all the biome config files found from the current working directory up to the root of the repo.
|
||||
*
|
||||
* Rather than parse and read the config files, this implementation naively searches for all config files from the
|
||||
* task's package directory up to the root of the repo and assumes they're all used. In the future we might want to
|
||||
* parse the config files anyway to extract ignore paths, at which point this implementation can change.
|
||||
*/
|
||||
private async getBiomeConfigPaths(cwd: string): Promise<string[]> {
|
||||
return (await findUp)
|
||||
.findUpMultiple(["biome.json", "biome.jsonc"], { cwd, stopAt: await this.repoRoot })
|
||||
.then((configs) => {
|
||||
if (configs.length === 0) {
|
||||
this.traceError(`Can't find biome config file`);
|
||||
}
|
||||
return configs;
|
||||
});
|
||||
return (await this.biomeConfig).formattedFiles;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -558,11 +558,12 @@ export class UnknownLeafTask extends LeafTask {
|
|||
|
||||
export abstract class LeafWithFileStatDoneFileTask extends LeafWithDoneFileTask {
|
||||
/**
|
||||
* @returns the list of files that this task depends on. The files are relative to the package directory.
|
||||
* @returns the list of absolute paths to files that this task depends on.
|
||||
*/
|
||||
protected abstract getInputFiles(): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* @returns the list of files that this task generates. The files are relative to the package directory.
|
||||
* @returns the list of absolute paths to files that this task generates.
|
||||
*/
|
||||
protected abstract getOutputFiles(): Promise<string[]>;
|
||||
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
// There are no cases in this file where the values being checked should be undefined, so `!.` is more correct with
|
||||
// respect to intent than `?.`.
|
||||
|
||||
import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
import {
|
||||
BiomeConfigReader,
|
||||
type BiomeConfigResolved,
|
||||
getBiomeFormattedFilesFromDirectory,
|
||||
getSettingValuesFromBiomeConfig,
|
||||
loadBiomeConfig,
|
||||
} from "../common/biomeConfig";
|
||||
import type { Configuration as BiomeConfigOnDisk } from "../common/biomeConfigTypes";
|
||||
import { getResolvedFluidRoot } from "../common/fluidUtils";
|
||||
import { GitRepo } from "../common/gitRepo";
|
||||
import { testDataPath } from "./init";
|
||||
|
||||
describe("Biome config loading", () => {
|
||||
describe("BiomeConfigReader class", () => {
|
||||
// These variables need to be initialized once for all the tests in this describe block. Defining them outside
|
||||
// of the before block causes the tests to be skipped.
|
||||
const testDir = path.resolve(testDataPath, "biome/pkg-b");
|
||||
let gitRepo: GitRepo;
|
||||
before(async () => {
|
||||
const repoRoot = await getResolvedFluidRoot(true);
|
||||
gitRepo = new GitRepo(repoRoot);
|
||||
});
|
||||
|
||||
it("loads", async () => {
|
||||
const config = await BiomeConfigReader.create(testDir, gitRepo);
|
||||
assert(config !== undefined);
|
||||
});
|
||||
|
||||
it("has correct formatted files list", async () => {
|
||||
const config = await BiomeConfigReader.create(testDir, gitRepo);
|
||||
const expected = [
|
||||
path.resolve(
|
||||
testDataPath,
|
||||
"biome/pkg-b/include-formatter-added-1/subdirectory/sourceFile2.ts",
|
||||
),
|
||||
path.resolve(
|
||||
testDataPath,
|
||||
"biome/pkg-b/include-formatter-added-1/subdirectory/markdownFile2.md",
|
||||
),
|
||||
path.resolve(testDataPath, "biome/pkg-b/include-formatter-added-1/sourceFile.ts"),
|
||||
path.resolve(testDataPath, "biome/pkg-b/include-formatter-added-1/markdownFile1.md"),
|
||||
];
|
||||
const { formattedFiles } = config;
|
||||
assert(
|
||||
formattedFiles.length === 4,
|
||||
`expected 4 elements in the array, got ${formattedFiles.length}`,
|
||||
);
|
||||
for (const actual of formattedFiles) {
|
||||
assert(expected.includes(actual));
|
||||
}
|
||||
});
|
||||
|
||||
it("returns only files matching files.includes", async () => {
|
||||
const config = await BiomeConfigReader.create(
|
||||
path.resolve(testDataPath, "biome/pkg-b/include-md-only.jsonc"),
|
||||
gitRepo,
|
||||
);
|
||||
const expected = [
|
||||
path.resolve(
|
||||
testDataPath,
|
||||
"biome/pkg-b/include-formatter-added-1/subdirectory/markdownFile2.md",
|
||||
),
|
||||
path.resolve(testDataPath, "biome/pkg-b/include-formatter-added-1/markdownFile1.md"),
|
||||
];
|
||||
const { formattedFiles } = config;
|
||||
assert(
|
||||
formattedFiles.length === 2,
|
||||
`expected 2 elements in the array, got ${formattedFiles.length}`,
|
||||
);
|
||||
for (const actual of formattedFiles) {
|
||||
assert(expected.includes(actual));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadConfig", () => {
|
||||
it("throws on missing config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/missing.jsonc");
|
||||
assert.rejects(async () => await loadBiomeConfig(testFile), Error);
|
||||
});
|
||||
|
||||
it("throws on empty config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/empty.jsonc");
|
||||
assert.rejects(async () => await loadBiomeConfig(testFile), Error);
|
||||
});
|
||||
|
||||
it("loads single config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/base.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
assert.notEqual(actual, undefined);
|
||||
assert.equal(actual.files!.ignoreUnknown, true);
|
||||
});
|
||||
|
||||
it("loads config with multiple extends", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-b/biome.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
|
||||
assert(actual !== undefined);
|
||||
|
||||
assert(actual.files!.ignoreUnknown === false);
|
||||
assert(actual.files!.include!.includes("pkg-a-include/**"));
|
||||
assert(actual.files!.ignore!.includes("pkg-a-ignore/**"));
|
||||
assert(actual.formatter!.include!.includes("include-formatter-added-1/**"));
|
||||
assert(actual.formatter!.ignore!.includes("ignore-formatter-added-1/**"));
|
||||
assert(actual.linter!.include!.includes("include-linter-added-1/**"));
|
||||
assert(actual.linter!.ignore!.includes("ignore-linter-added-1/**"));
|
||||
});
|
||||
|
||||
describe("extends from a single config", () => {
|
||||
// These variables need to be initialized once for all the tests in this describe block. Defining them outside
|
||||
// of the before block causes the tests to be skipped.
|
||||
let testConfig: BiomeConfigOnDisk;
|
||||
before(async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
testConfig = await loadBiomeConfig(testFile);
|
||||
});
|
||||
|
||||
it("top-level property is inherited", async () => {
|
||||
assert(testConfig !== undefined);
|
||||
assert(testConfig.files!.ignoreUnknown === true);
|
||||
});
|
||||
|
||||
it("files.include is overridden by loaded config", async () => {
|
||||
assert(testConfig.files!.include!.includes("pkg-a-include/**"));
|
||||
assert(
|
||||
testConfig.files!.include!.length === 1,
|
||||
`expected 1 elements in the array, got ${testConfig.files!.include!.length}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("files.ignore is overridden by loaded config", async () => {
|
||||
assert(testConfig.files!.ignore!.includes("pkg-a-ignore/**"));
|
||||
assert(
|
||||
testConfig.files!.ignore!.length === 1,
|
||||
`expected 1 elements in the array, got ${testConfig.files!.ignore!.length}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("formatter.include is overridden by loaded config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
|
||||
assert(actual.formatter!.include!.includes("include-formatter/**"));
|
||||
assert(
|
||||
actual.formatter!.include!.length === 1,
|
||||
`expected 1 elements in the array, got ${actual.formatter!.include!.length}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("formatter.ignore is overridden by loaded config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
|
||||
assert(actual.formatter!.ignore!.includes("ignore-formatter/**"));
|
||||
assert(
|
||||
actual.formatter!.ignore!.length === 1,
|
||||
`expected 1 elements in the array, got ${actual.formatter!.ignore!.length}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("linter.include is overridden by loaded config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
|
||||
assert(actual.linter!.include!.includes("include-linter/**"));
|
||||
assert(
|
||||
actual.linter!.include!.length === 1,
|
||||
`expected 1 elements in the array, got ${actual.linter!.include!.length}`,
|
||||
);
|
||||
});
|
||||
|
||||
it("linter.ignore is overridden by loaded config", async () => {
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
const actual = await loadBiomeConfig(testFile);
|
||||
|
||||
assert(actual.linter!.ignore!.includes("ignore-linter/**"));
|
||||
assert(
|
||||
actual.linter!.ignore!.length === 1,
|
||||
`expected 1 elements in the array, got ${actual.linter!.ignore!.length}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSettingValuesFromBiomeConfig", () => {
|
||||
describe("extends from a single config", () => {
|
||||
// These variables need to be initialized once for all the tests in this describe block. Defining them outside
|
||||
// of the before block causes the tests to be skipped.
|
||||
const testFile = path.resolve(testDataPath, "biome/pkg-a/biome.jsonc");
|
||||
let testConfig: BiomeConfigResolved;
|
||||
before(async () => {
|
||||
testConfig = await loadBiomeConfig(testFile);
|
||||
});
|
||||
|
||||
it("formatter ignore settings are merged with root", async () => {
|
||||
const ignores = await getSettingValuesFromBiomeConfig(
|
||||
testConfig,
|
||||
"formatter",
|
||||
"ignore",
|
||||
);
|
||||
assert(ignores.has("pkg-a-ignore/**"));
|
||||
assert(ignores.has("ignore-formatter/**"));
|
||||
assert(ignores.size === 2, `expected 2 items in the set, got ${ignores.size}`);
|
||||
});
|
||||
|
||||
it("linter ignore settings are merged with root", async () => {
|
||||
const ignores = await getSettingValuesFromBiomeConfig(testConfig, "linter", "ignore");
|
||||
assert(ignores.has("pkg-a-ignore/**"));
|
||||
assert(ignores.has("ignore-linter/**"));
|
||||
assert(ignores.size === 2);
|
||||
});
|
||||
|
||||
it("formatter include settings are merged with root", async () => {
|
||||
const includes = await getSettingValuesFromBiomeConfig(
|
||||
testConfig,
|
||||
"formatter",
|
||||
"include",
|
||||
);
|
||||
assert(includes.has("pkg-a-include/**"));
|
||||
assert(includes.has("include-formatter/**"));
|
||||
assert(includes.size === 2);
|
||||
});
|
||||
|
||||
it("linter include settings are merged with root", async () => {
|
||||
const includes = await getSettingValuesFromBiomeConfig(
|
||||
testConfig,
|
||||
"linter",
|
||||
"include",
|
||||
);
|
||||
assert(includes.has("pkg-a-include/**"));
|
||||
assert(includes.has("include-linter/**"));
|
||||
assert(includes.size === 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getBiomeFormattedFilesFromDirectory", () => {
|
||||
describe("extends from a single config", () => {
|
||||
// These variables need to be initialized once for all the tests in this describe block. Defining them outside
|
||||
// of the before block causes the tests to be skipped.
|
||||
const testPath = path.resolve(testDataPath, "biome/pkg-a/");
|
||||
let gitRepo: GitRepo;
|
||||
before(async () => {
|
||||
const repoRoot = await getResolvedFluidRoot(true);
|
||||
gitRepo = new GitRepo(repoRoot);
|
||||
});
|
||||
|
||||
it("returns correct file set", async () => {
|
||||
const expected = [
|
||||
path.resolve(testDataPath, "biome/pkg-a/pkg-a-include/sourceFile.ts"),
|
||||
path.resolve(
|
||||
testDataPath,
|
||||
"biome/pkg-a/pkg-a-include/include-formatter/formatter.ts",
|
||||
),
|
||||
path.resolve(testDataPath, "biome/pkg-a/pkg-a-include/include-linter/linter.ts"),
|
||||
path.resolve(testDataPath, "biome/pkg-a/include-formatter/formatter.ts"),
|
||||
];
|
||||
const formattedFiles = await getBiomeFormattedFilesFromDirectory(testPath, gitRepo);
|
||||
for (const actual of formattedFiles) {
|
||||
assert(expected.includes(actual));
|
||||
}
|
||||
assert(
|
||||
formattedFiles.length === 4,
|
||||
`expected 4 elements in the array, got ${formattedFiles.length}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extends from multiple configs", () => {
|
||||
// These variables need to be initialized once for all the tests in this describe block. Defining them outside
|
||||
// of the before block causes the tests to be skipped.
|
||||
const testPath = path.resolve(testDataPath, "biome/pkg-a/extended.jsonc");
|
||||
let gitRepo: GitRepo;
|
||||
before(async () => {
|
||||
const repoRoot = await getResolvedFluidRoot(true);
|
||||
gitRepo = new GitRepo(repoRoot);
|
||||
});
|
||||
|
||||
it("returns correct file set", async () => {
|
||||
const expected = [
|
||||
path.resolve(testDataPath, "biome/pkg-a/pkg-a-include/sourceFile.ts"),
|
||||
path.resolve(testDataPath, "biome/pkg-a/pkg-a-include/include-linter/linter.ts"),
|
||||
path.resolve(
|
||||
testDataPath,
|
||||
"biome/pkg-a/pkg-a-include/include-formatter/formatter.ts",
|
||||
),
|
||||
path.resolve(testDataPath, "biome/pkg-a/pkg-a-include/pkg-a-ignore/ignoredFile.ts"),
|
||||
path.resolve(testDataPath, "biome/pkg-a/include-formatter/formatter.ts"),
|
||||
];
|
||||
const formattedFiles = await getBiomeFormattedFilesFromDirectory(testPath, gitRepo);
|
||||
for (const actual of formattedFiles) {
|
||||
assert(expected.includes(actual));
|
||||
}
|
||||
assert(
|
||||
formattedFiles.length === 5,
|
||||
`expected 5 elements in the array, got ${formattedFiles.length}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"formatter": {
|
||||
"include": ["include-formatter-added-1/**"],
|
||||
"ignore": ["ignore-formatter-added-1/**"]
|
||||
},
|
||||
"linter": {
|
||||
"include": ["include-linter-added-1/**"],
|
||||
"ignore": ["ignore-linter-added-1/**"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"root": ".",
|
||||
"useIgnoreFile": true,
|
||||
"defaultBranch": "main"
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": true,
|
||||
|
||||
"ignore": [
|
||||
// Build output
|
||||
"**/base-1/*",
|
||||
"**/base-2/*",
|
||||
"**/base/*.done.build.log"
|
||||
],
|
||||
|
||||
"maxSize": 2097152
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": false
|
||||
},
|
||||
"linter": {
|
||||
"enabled": false,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"formatWithErrors": true,
|
||||
"indentStyle": "tab",
|
||||
"lineWidth": 95,
|
||||
"lineEnding": "lf"
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"arrowParentheses": "always",
|
||||
"jsxQuoteStyle": "double",
|
||||
"semicolons": "always",
|
||||
"trailingCommas": "all",
|
||||
"quoteProperties": "preserve",
|
||||
"quoteStyle": "double",
|
||||
"bracketSpacing": true
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"formatter": {
|
||||
"indentStyle": "tab"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
// @fluid-experimental/tree FORMATTING
|
||||
// This configuration is used to format the @fluid-experimental/tree package, which uses different settings than
|
||||
// most projects. This override is needed to ensure that the formatter is applied correctly when run from the root
|
||||
// of the repo.
|
||||
//
|
||||
// This configuration should be kept up-to-date with the settings in `experimental/dds/tree/biome.jsonc`.
|
||||
"include": ["experimental/dds/tree/**"],
|
||||
"formatter": {
|
||||
"lineWidth": 120
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"jsxQuoteStyle": "single",
|
||||
"trailingCommas": "es5",
|
||||
"quoteStyle": "single"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// JSONC WITHOUT TRAILING COMMAS JSONC is not a standard, and support for trailing commas is not universal. For
|
||||
// simplicity and safety, we parse most JSONC files in a liberal way -- allowing comments and trailing commas, but
|
||||
// format them conservatively without trailing commas.
|
||||
//
|
||||
// See also: https://github.com/microsoft/vscode/issues/102061
|
||||
"include": [
|
||||
"**/*.jsonc",
|
||||
|
||||
// Tools reading api-extractor config files do not consistently support trailing commas.
|
||||
"**/api-extractor*.json",
|
||||
|
||||
// Tools reading tsdoc config files do not consistently support trailing commas.
|
||||
"**/tsdoc*.json"
|
||||
],
|
||||
"json": {
|
||||
"parser": {
|
||||
"allowComments": true,
|
||||
"allowTrailingCommas": true
|
||||
},
|
||||
"formatter": {
|
||||
"trailingCommas": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// JSONC WITH TRAILING COMMAS
|
||||
// These JSONC files are known to support trailing commas.
|
||||
"include": [
|
||||
// vscode config files all support trailing commas.
|
||||
"**/.vscode/*.json",
|
||||
|
||||
// tsconfig files support trailing commas.
|
||||
"**/tsconfig*.json"
|
||||
],
|
||||
"json": {
|
||||
"parser": {
|
||||
"allowComments": true,
|
||||
"allowTrailingCommas": true
|
||||
},
|
||||
"formatter": {
|
||||
"trailingCommas": "all"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
// PACKAGE.JSON
|
||||
// These settings are used to format package.json files in the way npm itself does, with the exception of using
|
||||
// tabs instead of spaces.
|
||||
"include": ["**/package.json"],
|
||||
"json": {
|
||||
"formatter": {
|
||||
"lineWidth": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"$schema": "../../../../../node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"extends": ["../base.jsonc"],
|
||||
"files": {
|
||||
"include": ["pkg-a-include/**"],
|
||||
"ignore": ["pkg-a-ignore/**"]
|
||||
},
|
||||
"formatter": {
|
||||
"include": ["include-formatter/**"],
|
||||
"ignore": ["ignore-formatter/**"]
|
||||
},
|
||||
"linter": {
|
||||
"include": ["include-linter/**"],
|
||||
"ignore": ["ignore-linter/**"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": ["biome.jsonc"],
|
||||
"files": {
|
||||
"ignore": []
|
||||
},
|
||||
"formatter": {
|
||||
"ignore": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "../../../../../node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"extends": ["../pkg-a/biome.jsonc", "../add-includes-ignores.jsonc"],
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1 @@
|
|||
Test file
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1 @@
|
|||
Test file
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "../../../../node_modules/@biomejs/biome/configuration_schema.json",
|
||||
"extends": ["../pkg-a/biome.jsonc"],
|
||||
"files": {
|
||||
"include": ["*.md"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export const testValue = 1;
|
|
@ -0,0 +1,11 @@
|
|||
/*!
|
||||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* Path to the test data. It's rooted two directories up because the tests get executed from dist/.
|
||||
*/
|
||||
export const testDataPath = path.resolve(__dirname, "../../src/test/data");
|
|
@ -11,11 +11,7 @@ import {
|
|||
readPackageJsonAndIndent,
|
||||
updatePackageJsonFile,
|
||||
} from "../common/npmPackage";
|
||||
|
||||
/**
|
||||
* Path to the test data. It's rooted two directories up because the tests get executed from dist/.
|
||||
*/
|
||||
const testDataPath = path.resolve(__dirname, "../../src/test/data");
|
||||
import { testDataPath } from "./init";
|
||||
|
||||
/**
|
||||
* A transformer function that does nothing.
|
||||
|
|
|
@ -400,6 +400,9 @@ importers:
|
|||
ignore:
|
||||
specifier: ^5.2.4
|
||||
version: 5.2.4
|
||||
json-schema-to-typescript:
|
||||
specifier: ^15.0.0
|
||||
version: 15.0.0
|
||||
json5:
|
||||
specifier: ^2.2.3
|
||||
version: 2.2.3
|
||||
|
@ -409,6 +412,9 @@ importers:
|
|||
lodash.isequal:
|
||||
specifier: ^4.5.0
|
||||
version: 4.5.0
|
||||
multimatch:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
picomatch:
|
||||
specifier: ^2.3.1
|
||||
version: 2.3.1
|
||||
|
@ -421,6 +427,9 @@ importers:
|
|||
sort-package-json:
|
||||
specifier: 1.57.0
|
||||
version: 1.57.0
|
||||
ts-deepmerge:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.1
|
||||
ts-morph:
|
||||
specifier: ^22.0.0
|
||||
version: 22.0.0
|
||||
|
@ -660,6 +669,15 @@ packages:
|
|||
/@andrewbranch/untar.js@1.0.3:
|
||||
resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==}
|
||||
|
||||
/@apidevtools/json-schema-ref-parser@11.7.0:
|
||||
resolution: {integrity: sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog==}
|
||||
engines: {node: '>= 16'}
|
||||
dependencies:
|
||||
'@jsdevtools/ono': 7.1.3
|
||||
'@types/json-schema': 7.0.15
|
||||
js-yaml: 4.1.0
|
||||
dev: false
|
||||
|
||||
/@babel/code-frame@7.18.6:
|
||||
resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
@ -1628,6 +1646,10 @@ packages:
|
|||
'@jridgewell/sourcemap-codec': 1.4.14
|
||||
dev: true
|
||||
|
||||
/@jsdevtools/ono@7.1.3:
|
||||
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
|
||||
dev: false
|
||||
|
||||
/@kwsites/file-exists@1.1.1:
|
||||
resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==}
|
||||
dependencies:
|
||||
|
@ -2444,6 +2466,10 @@ packages:
|
|||
/@types/json-schema@7.0.14:
|
||||
resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==}
|
||||
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
dev: false
|
||||
|
||||
/@types/json5@0.0.29:
|
||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||
dev: true
|
||||
|
@ -2469,6 +2495,10 @@ packages:
|
|||
resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==}
|
||||
dev: true
|
||||
|
||||
/@types/lodash@4.17.7:
|
||||
resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==}
|
||||
dev: false
|
||||
|
||||
/@types/mdast@4.0.4:
|
||||
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
|
||||
dependencies:
|
||||
|
@ -2801,7 +2831,7 @@ packages:
|
|||
dependencies:
|
||||
'@typescript-eslint/types': 5.59.11
|
||||
'@typescript-eslint/visitor-keys': 5.59.11
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
globby: 11.1.0
|
||||
is-glob: 4.0.3
|
||||
semver: 7.6.2
|
||||
|
@ -3284,6 +3314,11 @@ packages:
|
|||
call-bind: 1.0.2
|
||||
is-array-buffer: 3.0.2
|
||||
|
||||
/array-differ@3.0.0:
|
||||
resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/array-ify@1.0.0:
|
||||
resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
|
||||
dev: true
|
||||
|
@ -3360,6 +3395,11 @@ packages:
|
|||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/arrify@2.0.1:
|
||||
resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/assertion-error@1.1.0:
|
||||
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
|
||||
dev: true
|
||||
|
@ -3853,6 +3893,17 @@ packages:
|
|||
timers-ext: 0.1.7
|
||||
dev: true
|
||||
|
||||
/cli-color@2.0.4:
|
||||
resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==}
|
||||
engines: {node: '>=0.10'}
|
||||
dependencies:
|
||||
d: 1.0.1
|
||||
es5-ext: 0.10.64
|
||||
es6-iterator: 2.0.3
|
||||
memoizee: 0.4.15
|
||||
timers-ext: 0.1.7
|
||||
dev: false
|
||||
|
||||
/cli-cursor@2.1.0:
|
||||
resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -4430,9 +4481,8 @@ packages:
|
|||
/d@1.0.1:
|
||||
resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
|
||||
dependencies:
|
||||
es5-ext: 0.10.62
|
||||
es5-ext: 0.10.64
|
||||
type: 1.2.0
|
||||
dev: true
|
||||
|
||||
/danger@11.3.0:
|
||||
resolution: {integrity: sha512-h4zkvmEfRVZp2EIKlQSky0IotxrDbJZtXgMTvyN1nwPCfg0JgvQVmVbvOZXrOgNVlgL+42ZDjNL2qAwVmJypNw==}
|
||||
|
@ -4903,22 +4953,29 @@ packages:
|
|||
es6-iterator: 2.0.3
|
||||
es6-symbol: 3.1.3
|
||||
next-tick: 1.1.0
|
||||
dev: true
|
||||
|
||||
/es5-ext@0.10.64:
|
||||
resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==}
|
||||
engines: {node: '>=0.10'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
es6-iterator: 2.0.3
|
||||
es6-symbol: 3.1.3
|
||||
esniff: 2.0.1
|
||||
next-tick: 1.1.0
|
||||
|
||||
/es6-iterator@2.0.3:
|
||||
resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
|
||||
dependencies:
|
||||
d: 1.0.1
|
||||
es5-ext: 0.10.62
|
||||
es5-ext: 0.10.64
|
||||
es6-symbol: 3.1.3
|
||||
dev: true
|
||||
|
||||
/es6-symbol@3.1.3:
|
||||
resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
|
||||
dependencies:
|
||||
d: 1.0.1
|
||||
ext: 1.7.0
|
||||
dev: true
|
||||
|
||||
/es6-weak-map@2.0.3:
|
||||
resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
|
||||
|
@ -4927,7 +4984,6 @@ packages:
|
|||
es5-ext: 0.10.62
|
||||
es6-iterator: 2.0.3
|
||||
es6-symbol: 3.1.3
|
||||
dev: true
|
||||
|
||||
/escalade@3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
|
@ -5484,6 +5540,15 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/esniff@2.0.1:
|
||||
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
|
||||
engines: {node: '>=0.10'}
|
||||
dependencies:
|
||||
d: 1.0.1
|
||||
es5-ext: 0.10.64
|
||||
event-emitter: 0.3.5
|
||||
type: 2.7.2
|
||||
|
||||
/espree@9.6.1:
|
||||
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
@ -5529,7 +5594,6 @@ packages:
|
|||
dependencies:
|
||||
d: 1.0.1
|
||||
es5-ext: 0.10.62
|
||||
dev: true
|
||||
|
||||
/event-lite@0.1.2:
|
||||
resolution: {integrity: sha512-HnSYx1BsJ87/p6swwzv+2v6B4X+uxUteoDfRxsAb1S1BePzQqOLevVmkdA15GHJVd9A9Ok6wygUR18Hu0YeV9g==}
|
||||
|
@ -5562,7 +5626,6 @@ packages:
|
|||
resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
|
||||
dependencies:
|
||||
type: 2.7.2
|
||||
dev: true
|
||||
|
||||
/extend-shallow@2.0.1:
|
||||
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
|
||||
|
@ -6087,7 +6150,7 @@ packages:
|
|||
dependencies:
|
||||
foreground-child: 3.1.1
|
||||
jackspeak: 2.3.6
|
||||
minimatch: 9.0.4
|
||||
minimatch: 9.0.5
|
||||
minipass: 7.0.4
|
||||
path-scurry: 1.10.2
|
||||
|
||||
|
@ -6517,7 +6580,7 @@ packages:
|
|||
resolution: {integrity: sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
dependencies:
|
||||
minimatch: 9.0.4
|
||||
minimatch: 9.0.5
|
||||
|
||||
/ignore@5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
|
@ -6803,7 +6866,6 @@ packages:
|
|||
|
||||
/is-promise@2.2.2:
|
||||
resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
|
||||
dev: true
|
||||
|
||||
/is-regex@1.1.4:
|
||||
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
|
||||
|
@ -7057,6 +7119,25 @@ packages:
|
|||
dependencies:
|
||||
jju: 1.4.0
|
||||
|
||||
/json-schema-to-typescript@15.0.0:
|
||||
resolution: {integrity: sha512-gOX3cJB4eL1ztMc3WUh569ubRcKnr8MnYk++6+/WaaN4bufGHSR6EcbUbvLZgirPQOfvni5SSGkRx0pYloYU8A==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@apidevtools/json-schema-ref-parser': 11.7.0
|
||||
'@types/json-schema': 7.0.15
|
||||
'@types/lodash': 4.17.7
|
||||
cli-color: 2.0.4
|
||||
glob: 10.3.12
|
||||
is-glob: 4.0.3
|
||||
js-yaml: 4.1.0
|
||||
lodash: 4.17.21
|
||||
minimist: 1.2.8
|
||||
mkdirp: 3.0.1
|
||||
node-fetch: 3.3.2
|
||||
prettier: 3.2.5
|
||||
dev: false
|
||||
|
||||
/json-schema-traverse@0.4.1:
|
||||
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||
|
||||
|
@ -7496,7 +7577,6 @@ packages:
|
|||
resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
|
||||
dependencies:
|
||||
es5-ext: 0.10.62
|
||||
dev: true
|
||||
|
||||
/make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
|
@ -7715,14 +7795,13 @@ packages:
|
|||
resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==}
|
||||
dependencies:
|
||||
d: 1.0.1
|
||||
es5-ext: 0.10.62
|
||||
es5-ext: 0.10.64
|
||||
es6-weak-map: 2.0.3
|
||||
event-emitter: 0.3.5
|
||||
is-promise: 2.2.2
|
||||
lru-queue: 0.1.0
|
||||
next-tick: 1.1.0
|
||||
timers-ext: 0.1.7
|
||||
dev: true
|
||||
|
||||
/meow@8.1.2:
|
||||
resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==}
|
||||
|
@ -8303,6 +8382,17 @@ packages:
|
|||
int64-buffer: 0.1.10
|
||||
isarray: 1.0.0
|
||||
|
||||
/multimatch@5.0.0:
|
||||
resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
'@types/minimatch': 3.0.5
|
||||
array-differ: 3.0.0
|
||||
array-union: 2.1.0
|
||||
arrify: 2.0.1
|
||||
minimatch: 3.1.2
|
||||
dev: false
|
||||
|
||||
/mute-stream@0.0.7:
|
||||
resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==}
|
||||
dev: true
|
||||
|
@ -8341,7 +8431,6 @@ packages:
|
|||
|
||||
/next-tick@1.1.0:
|
||||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
dev: true
|
||||
|
||||
/nise@5.1.9:
|
||||
resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==}
|
||||
|
@ -10435,9 +10524,8 @@ packages:
|
|||
/timers-ext@0.1.7:
|
||||
resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==}
|
||||
dependencies:
|
||||
es5-ext: 0.10.62
|
||||
es5-ext: 0.10.64
|
||||
next-tick: 1.1.0
|
||||
dev: true
|
||||
|
||||
/tiny-jsonc@1.0.1:
|
||||
resolution: {integrity: sha512-ik6BCxzva9DoiEfDX/li0L2cWKPPENYvixUprFdl3YPi4bZZUhDnNI9YUkacrv+uIG90dnxR5mNqaoD6UhD6Bw==}
|
||||
|
@ -10496,6 +10584,11 @@ packages:
|
|||
typescript: 5.4.5
|
||||
dev: true
|
||||
|
||||
/ts-deepmerge@7.0.1:
|
||||
resolution: {integrity: sha512-JBFCmNenZdUCc+TRNCtXVM6N8y/nDQHAcpj5BlwXG/gnogjam1NunulB9ia68mnqYI446giMfpqeBFFkOleh+g==}
|
||||
engines: {node: '>=14.13.1'}
|
||||
dev: false
|
||||
|
||||
/ts-morph@20.0.0:
|
||||
resolution: {integrity: sha512-JVmEJy2Wow5n/84I3igthL9sudQ8qzjh/6i4tmYCm6IqYyKFlNbJZi7oBdjyqcWSWYRu3CtL0xbT6fS03ESZIg==}
|
||||
dependencies:
|
||||
|
@ -10624,7 +10717,7 @@ packages:
|
|||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@tufjs/models': 1.0.4
|
||||
debug: 4.3.4(supports-color@8.1.1)
|
||||
debug: 4.3.5(supports-color@8.1.1)
|
||||
make-fetch-happen: 11.1.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -10686,11 +10779,9 @@ packages:
|
|||
|
||||
/type@1.2.0:
|
||||
resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
|
||||
dev: true
|
||||
|
||||
/type@2.7.2:
|
||||
resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
|
||||
dev: true
|
||||
|
||||
/typed-array-buffer@1.0.0:
|
||||
resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
|
||||
|
|
Загрузка…
Ссылка в новой задаче