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:
Tyler Butler 2024-08-19 13:30:58 -07:00 коммит произвёл GitHub
Родитель ca9728e76b
Коммит 5b3811e137
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
31 изменённых файлов: 3097 добавлений и 74 удалений

Просмотреть файл

@ -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);
}
}

2105
build-tools/packages/build-tools/src/common/biomeConfigTypes.d.ts поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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==}