@alint-js/config 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -5
- package/dist/index.d.mts +124 -3
- package/dist/index.mjs +776 -34
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -11,34 +11,77 @@ This package owns the file-system side of configuration:
|
|
|
11
11
|
- parses and stringifies setup TOML
|
|
12
12
|
- merges setup layers
|
|
13
13
|
- writes provider setup files
|
|
14
|
-
-
|
|
14
|
+
- installs remote plugin packages or local plugin directories and writes `.alint/plugins/lock.json`
|
|
15
|
+
- exports built-in ignore pattern groups for lower-level tooling
|
|
15
16
|
|
|
16
17
|
It is used by `@alint-js/cli` and is useful for tools that need to inspect or prepare an `alint` project without running the linter.
|
|
17
18
|
|
|
19
|
+
Ordinary `alint.config.*` files should import ignore presets through the `@alint-js/cli` facade so projects only need the CLI package.
|
|
20
|
+
|
|
18
21
|
## How to use
|
|
19
22
|
|
|
20
23
|
```ts
|
|
21
24
|
import {
|
|
22
25
|
getGlobalSetupConfigPath,
|
|
26
|
+
installStaticPlugins,
|
|
23
27
|
loadAlintConfig,
|
|
24
28
|
loadSetupConfig,
|
|
25
29
|
writeSetupConfig,
|
|
26
30
|
} from '@alint-js/config'
|
|
27
31
|
|
|
28
32
|
const setupPath = getGlobalSetupConfigPath()
|
|
29
|
-
const
|
|
30
|
-
const
|
|
33
|
+
const cwd = process.cwd()
|
|
34
|
+
const setup = await loadSetupConfig(setupPath)
|
|
35
|
+
|
|
36
|
+
await installStaticPlugins({ cwd })
|
|
37
|
+
|
|
38
|
+
const config = await loadAlintConfig(cwd)
|
|
31
39
|
|
|
32
40
|
await writeSetupConfig(setupPath, setup)
|
|
33
41
|
```
|
|
34
42
|
|
|
43
|
+
Static configs can use TOML, YAML, JSON, JSONC, or JSON5. They are data-only alternatives to executable JavaScript and TypeScript flat configs, and identify plugin sources with strings.
|
|
44
|
+
|
|
45
|
+
For example, TOML supports exact remote packages, config-relative directories, and native absolute paths:
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
[[config.group]]
|
|
49
|
+
|
|
50
|
+
[config.group.plugins]
|
|
51
|
+
plugin_1 = "@scope/alint-plugin-example1@1.2.3" # package
|
|
52
|
+
plugin_2 = "./plugins/relative-plugin-examplex" # relative directory
|
|
53
|
+
plugin_3 = "/opt/alint/plugins/absolute-plugin" # absolute directory
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Relative paths use the config file's directory as their base. Windows native paths should use TOML literal strings so backslashes remain literal:
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
[[config.group]]
|
|
60
|
+
|
|
61
|
+
[config.group.plugins]
|
|
62
|
+
native = 'C:\alint\plugins\native-plugin'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use `installStaticPlugins` after adding or changing plugin sources.
|
|
66
|
+
|
|
67
|
+
- **Package:** downloads the exact package version into `.alint/plugins/store`, verifies its integrity, and locks the installed snapshot.
|
|
68
|
+
- **Local:** installs the directory in place, validates its package root export, and locks its physical directory identity. It does not build the plugin or install its dependencies.
|
|
69
|
+
|
|
70
|
+
Run installation again after changing a source string, moving a local directory, or changing its symlink target. Changes inside the same local directory are loaded by the next process without reinstalling.
|
|
71
|
+
|
|
72
|
+
## Plugin lockfile
|
|
73
|
+
|
|
74
|
+
`.alint/plugins/lock.json` stores package snapshots with integrity metadata and local directory source identities. Local plugin code executes as trusted Node.js; containment checks are not a sandbox.
|
|
75
|
+
|
|
35
76
|
## When to use
|
|
36
77
|
|
|
37
78
|
- You are building CLI commands, editors, or automation around `alint` config.
|
|
38
79
|
- You need to read or write provider setup TOML.
|
|
39
|
-
- You need
|
|
80
|
+
- You need to load `alint.config.*` outside the official CLI.
|
|
81
|
+
- You need to install remote packages or local plugin directories before loading static config.
|
|
82
|
+
- You need the same ignore defaults as the official CLI in lower-level tooling.
|
|
40
83
|
|
|
41
84
|
## When not to use
|
|
42
85
|
|
|
43
|
-
- Use `@alint-js/cli` when you only need the `alint` command.
|
|
86
|
+
- Use `@alint-js/cli` when you only need the `alint` command or an ordinary `alint.config.*` file.
|
|
44
87
|
- Use `@alint-js/core` when you need the rule DSL, source runtime, model resolution, or run engine.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,56 @@
|
|
|
1
|
-
import { AlintConfig, AlintConfig as AlintConfig$1, ModelSize, ProviderDefinition, ProviderType, RunnerConfig, RunnerStatsConfig, SetupConfig, SetupConfig as SetupConfig$1, SetupModelDefinition } from "@alint-js/core";
|
|
1
|
+
import { AlintConfig, AlintConfig as AlintConfig$1, AlintConfigItem, ModelSize, PluginDefinition, ProviderDefinition, ProviderType, RunnerConfig, RunnerStatsConfig, SetupConfig, SetupConfig as SetupConfig$1, SetupModelDefinition } from "@alint-js/core";
|
|
2
2
|
|
|
3
|
+
//#region src/plugins/spec.d.ts
|
|
4
|
+
interface DirectoryPluginSpecifier {
|
|
5
|
+
directory: string;
|
|
6
|
+
raw: string;
|
|
7
|
+
type: 'directory';
|
|
8
|
+
}
|
|
9
|
+
type ParsedPluginSpecifier = DirectoryPluginSpecifier | RegistryPluginSpecifier;
|
|
10
|
+
interface ParsePluginSpecifierOptions {
|
|
11
|
+
configFile?: string;
|
|
12
|
+
}
|
|
13
|
+
interface RegistryPluginSpecifier {
|
|
14
|
+
name: string;
|
|
15
|
+
raw: string;
|
|
16
|
+
registryPath: string;
|
|
17
|
+
segments: string[];
|
|
18
|
+
type: 'registry';
|
|
19
|
+
version: string;
|
|
20
|
+
}
|
|
21
|
+
declare function parsePluginSpecifier(value: string, options?: ParsePluginSpecifierOptions): ParsedPluginSpecifier;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/config/static.d.ts
|
|
24
|
+
interface ParsedStaticConfig {
|
|
25
|
+
groups: ParsedStaticConfigGroup[];
|
|
26
|
+
}
|
|
27
|
+
interface ParsedStaticConfigGroup {
|
|
28
|
+
item: StaticConfigItem;
|
|
29
|
+
plugins: StaticPluginReference[];
|
|
30
|
+
}
|
|
31
|
+
interface ParseStaticConfigOptions {
|
|
32
|
+
configFile?: string;
|
|
33
|
+
}
|
|
34
|
+
type StaticConfigInput = readonly StaticConfigInput[] | StaticConfigItem;
|
|
35
|
+
interface StaticConfigItem extends Omit<AlintConfigItem, 'plugins'> {
|
|
36
|
+
plugins?: Record<string, PluginDefinition | string>;
|
|
37
|
+
}
|
|
38
|
+
interface StaticPluginReference {
|
|
39
|
+
alias: string;
|
|
40
|
+
specifier: ParsedPluginSpecifier;
|
|
41
|
+
}
|
|
42
|
+
type StaticPluginResolver = (reference: StaticPluginReference) => Promise<PluginDefinition>;
|
|
43
|
+
interface ToAlintConfigOptions {
|
|
44
|
+
pluginResolver: StaticPluginResolver;
|
|
45
|
+
}
|
|
46
|
+
declare function parseStaticConfig(value: unknown, options?: ParseStaticConfigOptions): ParsedStaticConfig;
|
|
47
|
+
declare function toAlintConfig(config: ParsedStaticConfig, options: ToAlintConfigOptions): Promise<AlintConfig$1>;
|
|
48
|
+
//#endregion
|
|
3
49
|
//#region src/config/load.d.ts
|
|
4
50
|
declare function loadAlintConfig(cwd: string, configFile?: string): Promise<AlintConfig$1>;
|
|
51
|
+
declare function loadStaticConfig(cwd: string, configFile?: string): Promise<ParsedStaticConfig>;
|
|
5
52
|
//#endregion
|
|
6
|
-
//#region src/ignore
|
|
53
|
+
//#region src/constants/patterns/ignore.d.ts
|
|
7
54
|
declare const ignorePatternsEslintDefaults: readonly ["**/node_modules/**", ".git/**", "**/.git/**"];
|
|
8
55
|
declare const ignorePatternsBuildOutputs: readonly ["**/dist/**", "**/coverage/**", "**/output/**", "**/.output/**", "**/.next/**", "**/.nuxt/**", "**/.svelte-kit/**", "**/.vercel/**"];
|
|
9
56
|
declare const ignorePatternsCaches: readonly ["**/.cache/**", "**/.temp/**", "**/.tmp/**", "**/temp/**", "**/tmp/**", "**/.vitepress/cache/**", "**/.vite-inspect/**"];
|
|
@@ -17,9 +64,83 @@ interface GlobalSetupConfigPathOptions {
|
|
|
17
64
|
xdgConfig?: string;
|
|
18
65
|
}
|
|
19
66
|
declare function getGlobalSetupConfigPath(env?: NodeJS.ProcessEnv, options?: GlobalSetupConfigPathOptions): string;
|
|
67
|
+
declare function getProjectPluginLockPath(cwd: string): string;
|
|
68
|
+
declare function getProjectPluginStorePath(cwd: string): string;
|
|
20
69
|
declare function getProjectSetupConfigPath(cwd: string): string;
|
|
21
70
|
declare function getStatsDir(env?: NodeJS.ProcessEnv, options?: GlobalSetupConfigPathOptions): string;
|
|
22
71
|
//#endregion
|
|
72
|
+
//#region src/plugins/types.d.ts
|
|
73
|
+
interface DirectoryPluginLockEntry {
|
|
74
|
+
alias: string;
|
|
75
|
+
path: string;
|
|
76
|
+
specifier: string;
|
|
77
|
+
type: 'directory';
|
|
78
|
+
}
|
|
79
|
+
interface ParsedDirectoryPluginLockEntry {
|
|
80
|
+
alias: string;
|
|
81
|
+
cwd: string;
|
|
82
|
+
lockEntry: DirectoryPluginLockEntry;
|
|
83
|
+
resolutionError?: unknown;
|
|
84
|
+
specifier: DirectoryPluginSpecifier;
|
|
85
|
+
type: 'directory';
|
|
86
|
+
}
|
|
87
|
+
type ParsedPluginLockEntry = ParsedDirectoryPluginLockEntry | ParsedRegistryPluginLockEntry;
|
|
88
|
+
interface ParsedPluginLockFile {
|
|
89
|
+
cwd: string;
|
|
90
|
+
entries: ParsedPluginLockEntry[];
|
|
91
|
+
file: PluginLockFile;
|
|
92
|
+
find: (reference: StaticPluginReference) => ParsedPluginLockEntry | undefined;
|
|
93
|
+
get: (reference: StaticPluginReference) => ParsedPluginLockEntry;
|
|
94
|
+
}
|
|
95
|
+
interface ParsedRegistryPluginLockEntry {
|
|
96
|
+
alias: string;
|
|
97
|
+
cwd: string;
|
|
98
|
+
lockEntry: RegistryPluginLockEntry;
|
|
99
|
+
resolutionError?: unknown;
|
|
100
|
+
specifier: RegistryPluginSpecifier;
|
|
101
|
+
type: 'registry';
|
|
102
|
+
}
|
|
103
|
+
type PluginLockEntry = DirectoryPluginLockEntry | RegistryPluginLockEntry;
|
|
104
|
+
interface PluginLockFile {
|
|
105
|
+
plugins: Record<string, PluginLockEntry>;
|
|
106
|
+
version: 2;
|
|
107
|
+
}
|
|
108
|
+
interface RegistryPluginLockEntry {
|
|
109
|
+
alias: string;
|
|
110
|
+
entry: string;
|
|
111
|
+
integrity: string;
|
|
112
|
+
name: string;
|
|
113
|
+
registry: string;
|
|
114
|
+
specifier: string;
|
|
115
|
+
tarball: string;
|
|
116
|
+
type: 'registry';
|
|
117
|
+
version: string;
|
|
118
|
+
}
|
|
119
|
+
interface StaticPluginInstallOptions {
|
|
120
|
+
configFile?: string;
|
|
121
|
+
cwd: string;
|
|
122
|
+
registry?: string;
|
|
123
|
+
}
|
|
124
|
+
interface StaticPluginInstallResult {
|
|
125
|
+
configuredPluginCount: number;
|
|
126
|
+
installedLocalDirectoryCount: number;
|
|
127
|
+
installedPackageCount: number;
|
|
128
|
+
lock: PluginLockFile;
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/plugins/install.d.ts
|
|
132
|
+
declare function installStaticPlugins(options: StaticPluginInstallOptions): Promise<StaticPluginInstallResult>;
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/plugins/lock.d.ts
|
|
135
|
+
declare function createEmptyPluginLockFile(): PluginLockFile;
|
|
136
|
+
declare function listMissing(config: ParsedStaticConfig, lock: ParsedPluginLockFile): StaticPluginReference[];
|
|
137
|
+
declare function listUnresolved(config: ParsedStaticConfig, lock: ParsedPluginLockFile): Promise<ParsedPluginLockEntry[]>;
|
|
138
|
+
declare function loadPluginLockFile(cwd: string): Promise<PluginLockFile>;
|
|
139
|
+
declare function parsePluginLockFile(value: unknown, options: {
|
|
140
|
+
cwd: string;
|
|
141
|
+
}): ParsedPluginLockFile;
|
|
142
|
+
declare function writePluginLockFile(cwd: string, lockFile: PluginLockFile): Promise<void>;
|
|
143
|
+
//#endregion
|
|
23
144
|
//#region src/setup/load.d.ts
|
|
24
145
|
declare const emptySetupConfig: SetupConfig$1;
|
|
25
146
|
declare function loadSetupConfig(filePath: string): Promise<SetupConfig$1>;
|
|
@@ -32,4 +153,4 @@ declare function stringifySetupConfigToml(config: SetupConfig$1): string;
|
|
|
32
153
|
//#region src/setup/write.d.ts
|
|
33
154
|
declare function writeSetupConfig(filePath: string, config: SetupConfig$1): Promise<void>;
|
|
34
155
|
//#endregion
|
|
35
|
-
export { type AlintConfig, type GlobalSetupConfigPathOptions, type ModelSize, type ProviderDefinition, type ProviderType, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, emptySetupConfig, getGlobalSetupConfigPath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, parseSetupConfigToml, stringifySetupConfigToml, writeSetupConfig };
|
|
156
|
+
export { type AlintConfig, type DirectoryPluginLockEntry, type GlobalSetupConfigPathOptions, type ModelSize, type ParseStaticConfigOptions, type ParsedDirectoryPluginLockEntry, type ParsedPluginLockEntry, type ParsedPluginLockFile, type ParsedPluginSpecifier, type ParsedRegistryPluginLockEntry, type ParsedStaticConfig, type ParsedStaticConfigGroup, type PluginLockEntry, type PluginLockFile, type ProviderDefinition, type ProviderType, type RegistryPluginLockEntry, type RunnerConfig, type RunnerStatsConfig, type SetupConfig, type SetupModelDefinition, type StaticConfigInput, type StaticConfigItem, type StaticPluginInstallOptions, type StaticPluginInstallResult, type StaticPluginReference, type StaticPluginResolver, type ToAlintConfigOptions, createEmptyPluginLockFile, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, listMissing, listUnresolved, loadAlintConfig, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,714 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { dirname, isAbsolute, join, normalize, posix, relative, resolve, sep, win32 } from "node:path";
|
|
2
2
|
import { loadConfig } from "c12";
|
|
3
3
|
import { createJiti } from "jiti/static";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import { mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { array, boolean, literal, looseObject, optional, parse, picklist, record, strictObject, string, tuple, union, unknown, variant } from "valibot";
|
|
4
7
|
import process from "node:process";
|
|
5
8
|
import { homedir } from "node:os";
|
|
6
|
-
import { dirname, join } from "pathe";
|
|
9
|
+
import { dirname as dirname$1, extname, join as join$1 } from "pathe";
|
|
7
10
|
import { xdgConfig } from "xdg-basedir";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
11
|
+
import { isError } from "@moeru/std/error";
|
|
12
|
+
import { exports } from "resolve.exports";
|
|
13
|
+
import { Buffer } from "node:buffer";
|
|
14
|
+
import { createWriteStream } from "node:fs";
|
|
15
|
+
import { Readable } from "node:stream";
|
|
16
|
+
import { pipeline } from "node:stream/promises";
|
|
17
|
+
import { createGunzip } from "node:zlib";
|
|
18
|
+
import tar from "tar-stream";
|
|
19
|
+
import { ofetch } from "ofetch";
|
|
20
|
+
import { pathToFileURL } from "node:url";
|
|
21
|
+
import { isPlainObject } from "es-toolkit/compat";
|
|
22
|
+
import { parse as parse$1, stringify } from "smol-toml";
|
|
23
|
+
//#region src/paths.ts
|
|
24
|
+
function getGlobalSetupConfigPath(env = process.env, options = {}) {
|
|
25
|
+
return join$1(resolveAlintHome(env, options), "config.toml");
|
|
26
|
+
}
|
|
27
|
+
function getProjectPluginLockPath(cwd) {
|
|
28
|
+
return join$1(cwd, ".alint", "plugins", "lock.json");
|
|
29
|
+
}
|
|
30
|
+
function getProjectPluginStorePath(cwd) {
|
|
31
|
+
return join$1(cwd, ".alint", "plugins", "store");
|
|
32
|
+
}
|
|
33
|
+
function getProjectSetupConfigPath(cwd) {
|
|
34
|
+
return join$1(cwd, ".alint", "config.toml");
|
|
35
|
+
}
|
|
36
|
+
function getStatsDir(env = process.env, options = {}) {
|
|
37
|
+
return join$1(resolveAlintHome(env, options), "stats");
|
|
38
|
+
}
|
|
39
|
+
function resolveAlintHome(env, options) {
|
|
40
|
+
return join$1(env.XDG_CONFIG_HOME ?? resolveDefaultConfigHome(options), "alint");
|
|
41
|
+
}
|
|
42
|
+
function resolveDefaultConfigHome(options) {
|
|
43
|
+
if (options.isMacOS ?? process.platform === "darwin") return join$1(homedir(), ".config");
|
|
44
|
+
return options.xdgConfig ?? xdgConfig ?? join$1(homedir(), ".config");
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/utils/fs.ts
|
|
48
|
+
async function exists(path) {
|
|
49
|
+
try {
|
|
50
|
+
await stat(path);
|
|
51
|
+
return true;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (isENOENTError(error)) return false;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isENOENTError(error) {
|
|
58
|
+
return isNodeErrorCode(error, "ENOENT");
|
|
59
|
+
}
|
|
60
|
+
function isNodeErrorCode(error, code) {
|
|
61
|
+
return isError(error) && "code" in error && error.code === code;
|
|
62
|
+
}
|
|
63
|
+
function isPathInside(path, parent) {
|
|
64
|
+
const childRelativePath = relative(parent, path);
|
|
65
|
+
return childRelativePath === "" || childRelativePath !== ".." && !childRelativePath.startsWith(`..${sep}`) && !isAbsolute(childRelativePath);
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/plugins/integrity.ts
|
|
69
|
+
const SUPPORTED_INTEGRITY_ALGORITHMS = ["sha512", "sha256"];
|
|
70
|
+
const SUPPORTED_INTEGRITY_TOKEN_PATTERN = /^(sha512|sha256)-([A-Za-z0-9+/]+={0,2})$/u;
|
|
71
|
+
function checkIntegrity(data, integrity, specifier) {
|
|
72
|
+
const result = parseIntegrity(integrity, specifier);
|
|
73
|
+
if (!result.tokens.some(({ algorithm, expected }) => {
|
|
74
|
+
if (algorithm !== result.strongestAlgorithm) return false;
|
|
75
|
+
return createHash(algorithm).update(data).digest("base64") === expected;
|
|
76
|
+
})) throw new Error(`Integrity mismatch for "${specifier}".`);
|
|
77
|
+
}
|
|
78
|
+
function parseIntegrity(integrity, specifier) {
|
|
79
|
+
const tokens = integrity.trim().split(/\s+/u).map((token) => SUPPORTED_INTEGRITY_TOKEN_PATTERN.exec(token)).filter((match) => match !== null).map(([, algorithm, expected]) => ({
|
|
80
|
+
algorithm,
|
|
81
|
+
expected
|
|
82
|
+
}));
|
|
83
|
+
const strongestAlgorithm = SUPPORTED_INTEGRITY_ALGORITHMS.find((algorithm) => tokens.some((token) => token.algorithm === algorithm));
|
|
84
|
+
if (strongestAlgorithm === void 0) throw new Error(`Unsupported npm integrity format for "${specifier}": "${integrity}".`);
|
|
85
|
+
return {
|
|
86
|
+
strongestAlgorithm,
|
|
87
|
+
tokens
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/plugins/sources/manifest.ts
|
|
92
|
+
function resolveRelativeRootEntry(packageJson) {
|
|
93
|
+
const [entry] = exports(packageJson, ".", {
|
|
94
|
+
browser: false,
|
|
95
|
+
require: false
|
|
96
|
+
}) ?? [];
|
|
97
|
+
if (entry === void 0) {
|
|
98
|
+
const name = typeof packageJson.name === "string" ? packageJson.name : "<unknown>";
|
|
99
|
+
throw new Error(`Package "${name}" does not define a resolvable "." export.`);
|
|
100
|
+
}
|
|
101
|
+
return entry.startsWith("./") ? entry.slice(2) : entry;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/plugins/sources/local/index.ts
|
|
105
|
+
async function createLockEntry$1(installed, options) {
|
|
106
|
+
const relativePath = relative(await realpath(options.cwd), installed.path);
|
|
107
|
+
const path = isAbsolute(relativePath) || win32.isAbsolute(relativePath) ? installed.path : relativePath === "" ? "." : relativePath;
|
|
108
|
+
return {
|
|
109
|
+
alias: options.alias,
|
|
110
|
+
path,
|
|
111
|
+
specifier: options.specifier.raw,
|
|
112
|
+
type: "directory"
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async function install$1(options) {
|
|
116
|
+
const packageDir = await canonicalRoot(options.alias, options.specifier.directory);
|
|
117
|
+
await validateRoot(options.alias, packageDir);
|
|
118
|
+
return {
|
|
119
|
+
path: packageDir,
|
|
120
|
+
type: "directory"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async function resolve$2(entry) {
|
|
124
|
+
const lockedPath = isAbsolute(entry.lockEntry.path) || win32.isAbsolute(entry.lockEntry.path) ? entry.lockEntry.path : resolve(entry.cwd, entry.lockEntry.path);
|
|
125
|
+
const [packageDir, lockedPackageDir] = await Promise.all([lockedRoot(entry.alias, entry.specifier.directory, "configured"), lockedRoot(entry.alias, lockedPath, "locked")]);
|
|
126
|
+
if (packageDir !== lockedPackageDir) throw new Error(`Directory plugin "${entry.alias}" has moved or its symlink target changed. Run: alint plugin install`);
|
|
127
|
+
return {
|
|
128
|
+
cache: "content",
|
|
129
|
+
entry: await validateRoot(entry.alias, packageDir)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function canonicalRoot(alias, directory) {
|
|
133
|
+
let packageDir;
|
|
134
|
+
try {
|
|
135
|
+
packageDir = await realpath(directory);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (isENOENTError(error)) throw new Error(`Directory plugin "${alias}" does not exist at "${directory}".`, { cause: error });
|
|
138
|
+
throw new Error(`Could not resolve directory plugin "${alias}" at "${directory}".`, { cause: error });
|
|
139
|
+
}
|
|
140
|
+
let directoryStat;
|
|
141
|
+
try {
|
|
142
|
+
directoryStat = await stat(packageDir);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new Error(`Could not inspect directory plugin "${alias}" at "${directory}".`, { cause: error });
|
|
145
|
+
}
|
|
146
|
+
if (!directoryStat.isDirectory()) throw new Error(`Directory plugin "${alias}" path "${directory}" is not a directory.`);
|
|
147
|
+
return packageDir;
|
|
148
|
+
}
|
|
149
|
+
async function lockedRoot(alias, directory, source) {
|
|
150
|
+
try {
|
|
151
|
+
return await realpath(directory);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (isENOENTError(error)) throw new Error(`Directory plugin "${alias}" has moved or its symlink target changed. Run: alint plugin install`, { cause: error });
|
|
154
|
+
throw new Error(`Could not resolve ${source} directory plugin "${alias}" at "${directory}".`, { cause: error });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function validateRoot(alias, packageDir) {
|
|
158
|
+
const manifestPath = join(packageDir, "package.json");
|
|
159
|
+
let packageJson;
|
|
160
|
+
try {
|
|
161
|
+
packageJson = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new Error(`Directory plugin "${alias}" has an unreadable or invalid package.json at "${manifestPath}".`, { cause: error });
|
|
164
|
+
}
|
|
165
|
+
const relativeEntry = resolveRelativeRootEntry(packageJson);
|
|
166
|
+
const entry = resolve(packageDir, relativeEntry);
|
|
167
|
+
if (!isPathInside(entry, packageDir)) throw new Error(`Directory plugin "${alias}" root export escapes the package directory.`);
|
|
168
|
+
let physicalEntry;
|
|
169
|
+
try {
|
|
170
|
+
physicalEntry = await realpath(entry);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (isENOENTError(error)) throw new Error(`Directory plugin "${alias}" entry "${relativeEntry}" does not exist. Build the package and try again.`, { cause: error });
|
|
173
|
+
throw new Error(`Could not resolve directory plugin "${alias}" entry "${relativeEntry}".`, { cause: error });
|
|
174
|
+
}
|
|
175
|
+
if (!isPathInside(physicalEntry, packageDir)) throw new Error(`Directory plugin "${alias}" entry physically escapes the package directory.`);
|
|
176
|
+
let entryStat;
|
|
177
|
+
try {
|
|
178
|
+
entryStat = await stat(physicalEntry);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (isENOENTError(error)) throw new Error(`Directory plugin "${alias}" entry "${relativeEntry}" does not exist. Build the package and try again.`, { cause: error });
|
|
181
|
+
throw new Error(`Could not inspect directory plugin "${alias}" entry "${relativeEntry}".`, { cause: error });
|
|
182
|
+
}
|
|
183
|
+
if (!entryStat.isFile()) throw new Error(`Directory plugin "${alias}" entry "${relativeEntry}" is not a regular file.`);
|
|
184
|
+
return physicalEntry;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/plugins/sources/package/index.ts
|
|
188
|
+
function createLockEntry(installed, options) {
|
|
189
|
+
return {
|
|
190
|
+
alias: options.alias,
|
|
191
|
+
entry: installed.entry,
|
|
192
|
+
integrity: installed.integrity,
|
|
193
|
+
name: installed.name,
|
|
194
|
+
registry: installed.registry,
|
|
195
|
+
specifier: options.specifier.raw,
|
|
196
|
+
tarball: installed.tarball,
|
|
197
|
+
type: "registry",
|
|
198
|
+
version: installed.version
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function install(options) {
|
|
202
|
+
const { name, segments, version } = options.specifier;
|
|
203
|
+
const npmRegistry = options.npmRegistry.endsWith("/") ? options.npmRegistry : `${options.npmRegistry}/`;
|
|
204
|
+
const dist = (await ofetch(`${npmRegistry.replace(/\/$/u, "")}/${options.specifier.registryPath}`)).versions?.[version]?.dist;
|
|
205
|
+
if (dist?.tarball === void 0) throw new Error(`Npm metadata for "${name}" does not include a tarball for version ${version}.`);
|
|
206
|
+
if (dist.integrity === void 0 || dist.integrity.trim() === "") throw new Error(`Npm metadata for "${name}" does not include integrity for version ${version}.`);
|
|
207
|
+
const storePath = getProjectPluginStorePath(options.cwd);
|
|
208
|
+
const packageDir = join(storePath, ...segments, version, "package");
|
|
209
|
+
if (!isPathInside(resolve(packageDir), resolve(storePath))) throw new Error(`Static plugin package "${name}" resolves outside the project plugin store.`);
|
|
210
|
+
const stagingPackageDir = join(dirname(packageDir), `package-staging-${randomUUID()}`);
|
|
211
|
+
let relativeEntry;
|
|
212
|
+
try {
|
|
213
|
+
const tarball = Buffer.from(await ofetch(dist.tarball, { responseType: "arrayBuffer" }));
|
|
214
|
+
checkIntegrity(tarball, dist.integrity, `${name}@${version}`);
|
|
215
|
+
await mkdir(stagingPackageDir, { recursive: true });
|
|
216
|
+
await extractTarball(tarball, stagingPackageDir);
|
|
217
|
+
relativeEntry = resolveRelativeRootEntry(JSON.parse(await readFile(join(stagingPackageDir, "package.json"), "utf8")));
|
|
218
|
+
await replaceDirectory(packageDir, stagingPackageDir);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
await rm(stagingPackageDir, {
|
|
221
|
+
force: true,
|
|
222
|
+
recursive: true
|
|
223
|
+
});
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
entry: posix.join(".alint/plugins/store", ...segments, version, "package", relativeEntry.split(/[\\/]/u).join("/")),
|
|
228
|
+
integrity: dist.integrity,
|
|
229
|
+
name,
|
|
230
|
+
registry: npmRegistry,
|
|
231
|
+
tarball: dist.tarball,
|
|
232
|
+
type: "registry",
|
|
233
|
+
version
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
async function resolve$1(entry) {
|
|
237
|
+
const projectRoot = resolve(entry.cwd);
|
|
238
|
+
const pluginRoot = join(projectRoot, ".alint", "plugins", "store");
|
|
239
|
+
const rawEntryPath = entry.lockEntry.entry;
|
|
240
|
+
const resolvedEntry = isAbsolute(rawEntryPath) ? resolve(rawEntryPath) : resolve(projectRoot, rawEntryPath);
|
|
241
|
+
if (!isPathInside(resolvedEntry, projectRoot)) throw new Error(`Plugin lock entry "${entry.alias}" resolves outside the project root.`);
|
|
242
|
+
const [physicalEntry, physicalPluginRoot] = await Promise.all([resolvePhysicalPath(resolvedEntry), realpath(pluginRoot)]);
|
|
243
|
+
if (!isPathInside(physicalEntry, physicalPluginRoot)) throw new Error(`Plugin lock entry "${entry.alias}" resolves outside the plugin store.`);
|
|
244
|
+
const packageDir = getLockedPackageDir(projectRoot, pluginRoot, entry);
|
|
245
|
+
if (await realpath(packageDir) !== join(physicalPluginRoot, ...getPackagePathSegments(entry.lockEntry.name), entry.lockEntry.version, "package")) throw new Error(`Plugin lock entry "${entry.alias}" resolves outside the locked package directory.`);
|
|
246
|
+
if (!isPathInside(resolvedEntry, packageDir)) throw new Error(`Plugin lock entry "${entry.alias}" resolves outside the locked package directory.`);
|
|
247
|
+
JSON.parse(await readFile(join(packageDir, "package.json"), "utf8"));
|
|
248
|
+
return {
|
|
249
|
+
cache: "default",
|
|
250
|
+
entry: resolvedEntry
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async function extractEntry(packageDir, header, stream) {
|
|
254
|
+
const entryName = header.name;
|
|
255
|
+
if (!entryName.startsWith("package/")) {
|
|
256
|
+
stream.resume();
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const relativeEntry = entryName.slice(8);
|
|
260
|
+
const normalized = posix.normalize(relativeEntry);
|
|
261
|
+
if (relativeEntry === "" || normalized === ".") {
|
|
262
|
+
stream.resume();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (normalized === ".." || normalized.startsWith("../") || posix.isAbsolute(normalized)) {
|
|
266
|
+
stream.resume();
|
|
267
|
+
throw new Error(`Plugin tarball entry "${entryName}" escapes the package directory.`);
|
|
268
|
+
}
|
|
269
|
+
const outputPath = resolve(packageDir, normalized);
|
|
270
|
+
if (!isPathInside(outputPath, packageDir)) {
|
|
271
|
+
stream.resume();
|
|
272
|
+
throw new Error(`Plugin tarball entry "${entryName}" escapes the package directory.`);
|
|
273
|
+
}
|
|
274
|
+
if (header.type === "directory") {
|
|
275
|
+
stream.resume();
|
|
276
|
+
await mkdir(outputPath, { recursive: true });
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (header.type !== "file") {
|
|
280
|
+
stream.resume();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
284
|
+
await pipeline(stream, createWriteStream(outputPath));
|
|
285
|
+
}
|
|
286
|
+
async function extractTarball(tarball, packageDir) {
|
|
287
|
+
const extract = tar.extract();
|
|
288
|
+
extract.on("entry", (header, stream, next) => {
|
|
289
|
+
extractEntry(packageDir, header, stream).then(() => next()).catch((error) => extract.destroy(error));
|
|
290
|
+
});
|
|
291
|
+
await pipeline(Readable.from(tarball), createGunzip(), extract);
|
|
292
|
+
}
|
|
293
|
+
function getLockedPackageDir(projectRoot, pluginRoot, entry) {
|
|
294
|
+
const name = entry.lockEntry.name;
|
|
295
|
+
const version = entry.lockEntry.version;
|
|
296
|
+
if (entry.specifier.name !== name) throw new Error(`Plugin lock entry "${entry.alias}" package name does not match its specifier.`);
|
|
297
|
+
if (entry.specifier.version !== version) throw new Error(`Plugin lock entry "${entry.alias}" package version does not match its specifier.`);
|
|
298
|
+
const packageDir = join(pluginRoot, ...getPackagePathSegments(name), version, "package");
|
|
299
|
+
if (!isPathInside(packageDir, projectRoot) || !isPathInside(packageDir, pluginRoot)) throw new Error(`Plugin lock entry "${entry.alias}" package metadata resolves outside the plugin store.`);
|
|
300
|
+
return packageDir;
|
|
301
|
+
}
|
|
302
|
+
function getPackagePathSegments(name) {
|
|
303
|
+
const segments = name.split("/");
|
|
304
|
+
const segmentPattern = /^[\w.-]+$/u;
|
|
305
|
+
if (segments.length === 2) {
|
|
306
|
+
const [scope, packageName] = segments;
|
|
307
|
+
if (scope === void 0 || packageName === void 0 || !scope.startsWith("@") || !segmentPattern.test(scope.slice(1)) || !segmentPattern.test(packageName)) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
308
|
+
return segments;
|
|
309
|
+
}
|
|
310
|
+
if (segments.length !== 1 || !segmentPattern.test(segments[0])) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
311
|
+
return segments;
|
|
312
|
+
}
|
|
313
|
+
async function nearestExistingParent(path) {
|
|
314
|
+
let current = path;
|
|
315
|
+
while (current !== dirname(current)) {
|
|
316
|
+
if (await exists(current)) return current;
|
|
317
|
+
current = dirname(current);
|
|
318
|
+
}
|
|
319
|
+
return current;
|
|
320
|
+
}
|
|
321
|
+
async function replaceDirectory(packageDir, stagingPackageDir) {
|
|
322
|
+
const backupPackageDir = join(dirname(packageDir), `package-backup-${randomUUID()}`);
|
|
323
|
+
let hasBackup = false;
|
|
324
|
+
let replaced = false;
|
|
325
|
+
try {
|
|
326
|
+
await rename(packageDir, backupPackageDir);
|
|
327
|
+
hasBackup = true;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (!isENOENTError(error)) throw error;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
await rename(stagingPackageDir, packageDir);
|
|
333
|
+
replaced = true;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
if (hasBackup) {
|
|
336
|
+
await rename(backupPackageDir, packageDir);
|
|
337
|
+
hasBackup = false;
|
|
338
|
+
}
|
|
339
|
+
throw error;
|
|
340
|
+
} finally {
|
|
341
|
+
if (replaced || !hasBackup) await rm(backupPackageDir, {
|
|
342
|
+
force: true,
|
|
343
|
+
recursive: true
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async function resolvePhysicalPath(path) {
|
|
348
|
+
try {
|
|
349
|
+
return await realpath(path);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
if (!isENOENTError(error)) throw error;
|
|
352
|
+
}
|
|
353
|
+
const parent = await nearestExistingParent(dirname(path));
|
|
354
|
+
return join(await realpath(parent), relative(parent, path));
|
|
355
|
+
}
|
|
356
|
+
//#endregion
|
|
357
|
+
//#region src/plugins/resolve.ts
|
|
358
|
+
function resolvePluginImportTarget(entry) {
|
|
359
|
+
return entry.type === "directory" ? resolve$2(entry) : resolve$1(entry);
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
//#region src/plugins/spec.ts
|
|
363
|
+
const explicitRelativePathPattern = /^\.{1,2}(?:[\\/]|$)/u;
|
|
364
|
+
function getPluginSpecifierKey(specifier) {
|
|
365
|
+
return specifier.type === "registry" ? `registry:${specifier.raw}` : `directory:${normalizeDirectoryIdentity(specifier.directory)}`;
|
|
366
|
+
}
|
|
367
|
+
function isDirectoryPluginSpecifier(value) {
|
|
368
|
+
return isAbsolute(value) || win32.isAbsolute(value) || explicitRelativePathPattern.test(value);
|
|
369
|
+
}
|
|
370
|
+
function parsePluginSpecifier(value, options = {}) {
|
|
371
|
+
if (/^file:/iu.test(value)) throw new Error("Static plugin file URLs are not supported. Use a relative or absolute directory path instead.");
|
|
372
|
+
if (isAbsolute(value) || win32.isAbsolute(value)) return {
|
|
373
|
+
directory: isAbsolute(value) ? normalize(value) : win32.normalize(value),
|
|
374
|
+
raw: value,
|
|
375
|
+
type: "directory"
|
|
376
|
+
};
|
|
377
|
+
if (explicitRelativePathPattern.test(value)) {
|
|
378
|
+
if (options.configFile === void 0) throw new Error(`Relative static plugin directory "${value}" requires a config file.`);
|
|
379
|
+
const relativePath = value.split(/[\\/]/u).join(sep);
|
|
380
|
+
return {
|
|
381
|
+
directory: resolve(dirname(options.configFile), relativePath),
|
|
382
|
+
raw: value,
|
|
383
|
+
type: "directory"
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
const versionSeparator = value.lastIndexOf("@");
|
|
387
|
+
if (versionSeparator <= 0 || versionSeparator === value.length - 1) throw new Error(`Static plugin specifier "${value}" must include an exact package version.`);
|
|
388
|
+
const name = value.slice(0, versionSeparator);
|
|
389
|
+
const segments = parsePackageNameSegments(name);
|
|
390
|
+
return {
|
|
391
|
+
name,
|
|
392
|
+
raw: value,
|
|
393
|
+
registryPath: name.startsWith("@") ? name.replace("/", "%2f") : encodeURIComponent(name),
|
|
394
|
+
segments,
|
|
395
|
+
type: "registry",
|
|
396
|
+
version: value.slice(versionSeparator + 1)
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function normalizeDirectoryIdentity(directory) {
|
|
400
|
+
return isAbsolute(directory) ? normalize(directory) : win32.normalize(directory);
|
|
401
|
+
}
|
|
402
|
+
function parsePackageNameSegments(name) {
|
|
403
|
+
if (name === "" || name.includes("\\") || isAbsolute(name) || posix.isAbsolute(name)) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
404
|
+
const segments = name.split("/");
|
|
405
|
+
if (segments.some((segment) => segment === "" || segment === "." || segment === "..")) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
406
|
+
const segmentPattern = /^[a-z0-9][a-z0-9._~-]*$/u;
|
|
407
|
+
if (name.startsWith("@")) {
|
|
408
|
+
const [scope, packageName, extraSegment] = segments;
|
|
409
|
+
if (segments.length !== 2 || scope === void 0 || packageName === void 0 || extraSegment !== void 0 || !scope.startsWith("@") || !segmentPattern.test(scope.slice(1)) || !segmentPattern.test(packageName)) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
410
|
+
return segments;
|
|
411
|
+
}
|
|
412
|
+
if (segments.length !== 1 || !segmentPattern.test(segments[0])) throw new Error(`Invalid static plugin package name "${name}".`);
|
|
413
|
+
return segments;
|
|
414
|
+
}
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/plugins/lock.ts
|
|
417
|
+
const PluginLockEntrySchema = variant("type", [strictObject({
|
|
418
|
+
alias: string(),
|
|
419
|
+
path: string(),
|
|
420
|
+
specifier: string(),
|
|
421
|
+
type: literal("directory")
|
|
422
|
+
}), strictObject({
|
|
423
|
+
alias: string(),
|
|
424
|
+
entry: string(),
|
|
425
|
+
integrity: string(),
|
|
426
|
+
name: string(),
|
|
427
|
+
registry: string(),
|
|
428
|
+
specifier: string(),
|
|
429
|
+
tarball: string(),
|
|
430
|
+
type: literal("registry"),
|
|
431
|
+
version: string()
|
|
432
|
+
})]);
|
|
433
|
+
const PluginLockFileSchema = strictObject({
|
|
434
|
+
plugins: record(string(), PluginLockEntrySchema),
|
|
435
|
+
version: literal(2)
|
|
436
|
+
});
|
|
437
|
+
function createEmptyPluginLockFile() {
|
|
438
|
+
return {
|
|
439
|
+
plugins: {},
|
|
440
|
+
version: 2
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function listMissing(config, lock) {
|
|
444
|
+
const missing = [];
|
|
445
|
+
for (const group of config.groups) for (const reference of group.plugins) if (lock.find(reference) === void 0) missing.push(reference);
|
|
446
|
+
return missing;
|
|
447
|
+
}
|
|
448
|
+
async function listUnresolved(config, lock) {
|
|
449
|
+
const unresolved = [];
|
|
450
|
+
const checkedAliases = /* @__PURE__ */ new Set();
|
|
451
|
+
for (const group of config.groups) for (const reference of group.plugins) {
|
|
452
|
+
const entry = lock.find(reference);
|
|
453
|
+
if (entry === void 0 || checkedAliases.has(entry.alias)) continue;
|
|
454
|
+
checkedAliases.add(entry.alias);
|
|
455
|
+
try {
|
|
456
|
+
await resolvePluginImportTarget(entry);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
unresolved.push({
|
|
459
|
+
...entry,
|
|
460
|
+
resolutionError: error
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return unresolved;
|
|
465
|
+
}
|
|
466
|
+
async function loadPluginLockFile(cwd) {
|
|
467
|
+
try {
|
|
468
|
+
return parsePluginLockFileValue(await readFile(getProjectPluginLockPath(cwd), "utf8"));
|
|
469
|
+
} catch (error) {
|
|
470
|
+
if (isENOENTError(error)) return createEmptyPluginLockFile();
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function parsePluginLockFile(value, options) {
|
|
475
|
+
const file = parsePluginLockFileValue(value);
|
|
476
|
+
const entries = Object.values(file.plugins).map((entry) => {
|
|
477
|
+
if (entry.type === "registry") {
|
|
478
|
+
const specifier = parsePluginSpecifier(entry.specifier);
|
|
479
|
+
if (specifier.type !== "registry") throw new Error(`Registry plugin lock entry "${entry.alias}" must use a registry specifier.`);
|
|
480
|
+
return {
|
|
481
|
+
alias: entry.alias,
|
|
482
|
+
cwd: options.cwd,
|
|
483
|
+
lockEntry: entry,
|
|
484
|
+
specifier,
|
|
485
|
+
type: "registry"
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
const specifier = parsePluginSpecifier(resolveDirectoryLockIdentity(entry.path, options.cwd));
|
|
489
|
+
if (specifier.type !== "directory") throw new Error(`Directory plugin lock entry "${entry.alias}" must use a directory path.`);
|
|
490
|
+
return {
|
|
491
|
+
alias: entry.alias,
|
|
492
|
+
cwd: options.cwd,
|
|
493
|
+
lockEntry: entry,
|
|
494
|
+
specifier: {
|
|
495
|
+
...specifier,
|
|
496
|
+
raw: entry.specifier
|
|
497
|
+
},
|
|
498
|
+
type: "directory"
|
|
499
|
+
};
|
|
500
|
+
});
|
|
501
|
+
const byAlias = new Map(entries.map((entry) => [entry.alias, entry]));
|
|
502
|
+
return {
|
|
503
|
+
cwd: options.cwd,
|
|
504
|
+
entries,
|
|
505
|
+
file,
|
|
506
|
+
find(reference) {
|
|
507
|
+
const entry = byAlias.get(reference.alias);
|
|
508
|
+
return entry !== void 0 && matchesReference(entry, reference) ? associateCurrentSpecifier(entry, reference) : void 0;
|
|
509
|
+
},
|
|
510
|
+
get(reference) {
|
|
511
|
+
const entry = byAlias.get(reference.alias);
|
|
512
|
+
const expected = reference.specifier.raw;
|
|
513
|
+
if (entry === void 0) throw new Error(`Plugin "${reference.alias}" requires ${expected}, but no matching lock entry exists.\nRun: alint plugin install`);
|
|
514
|
+
if (!matchesReference(entry, reference)) throw new Error(`Plugin "${reference.alias}" is locked to ${entry.lockEntry.specifier}, but config requires ${expected}.\nRun: alint plugin install`);
|
|
515
|
+
return associateCurrentSpecifier(entry, reference);
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
async function writePluginLockFile(cwd, lockFile) {
|
|
520
|
+
const content = `${JSON.stringify(parsePluginLockFileValue(lockFile), null, 2)}\n`;
|
|
521
|
+
const lockPath = getProjectPluginLockPath(cwd);
|
|
522
|
+
const lockDir = dirname(lockPath);
|
|
523
|
+
const tempPath = join(lockDir, `lock.${randomUUID()}.tmp`);
|
|
524
|
+
await mkdir(lockDir, { recursive: true });
|
|
525
|
+
try {
|
|
526
|
+
await writeFile(tempPath, content, "utf8");
|
|
527
|
+
await rename(tempPath, lockPath);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
await rm(tempPath, { force: true });
|
|
530
|
+
throw error;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function associateCurrentSpecifier(entry, reference) {
|
|
534
|
+
if (entry.type === "directory" && reference.specifier.type === "directory") return {
|
|
535
|
+
...entry,
|
|
536
|
+
specifier: reference.specifier
|
|
537
|
+
};
|
|
538
|
+
return entry;
|
|
539
|
+
}
|
|
540
|
+
function matchesReference(entry, reference) {
|
|
541
|
+
if (entry.type !== reference.specifier.type) return false;
|
|
542
|
+
if (entry.type === "directory") return entry.lockEntry.specifier === reference.specifier.raw || getPluginSpecifierKey(entry.specifier) === getPluginSpecifierKey(reference.specifier);
|
|
543
|
+
return getPluginSpecifierKey(entry.specifier) === getPluginSpecifierKey(reference.specifier);
|
|
544
|
+
}
|
|
545
|
+
function parsePluginLockFileValue(value) {
|
|
546
|
+
const parsedValue = typeof value === "string" ? JSON.parse(value) : value;
|
|
547
|
+
if (typeof parsedValue === "object" && parsedValue !== null && "version" in parsedValue && parsedValue.version === 1) throw new Error("Unsupported plugin lock version 1. Run: alint plugin install");
|
|
548
|
+
const file = parse(PluginLockFileSchema, parsedValue);
|
|
549
|
+
for (const [alias, entry] of Object.entries(file.plugins)) {
|
|
550
|
+
if (entry.alias !== alias) throw new Error(`Plugin lock entry key "${alias}" must match alias "${entry.alias}".`);
|
|
551
|
+
if (entry.type === "registry") {
|
|
552
|
+
parseIntegrity(entry.integrity, entry.specifier);
|
|
553
|
+
if (isDirectoryPluginSpecifier(entry.specifier)) throw new Error(`Registry plugin lock entry "${alias}" must use a registry specifier.`);
|
|
554
|
+
const specifier = parsePluginSpecifier(entry.specifier);
|
|
555
|
+
if (specifier.type !== "registry" || specifier.name !== entry.name || specifier.version !== entry.version) throw new Error(`Registry plugin lock entry "${alias}" identity does not match specifier "${entry.specifier}".`);
|
|
556
|
+
} else if (!isDirectoryPluginSpecifier(entry.specifier)) throw new Error(`Directory plugin lock entry "${alias}" must use a directory specifier.`);
|
|
557
|
+
}
|
|
558
|
+
return file;
|
|
559
|
+
}
|
|
560
|
+
function resolveDirectoryLockIdentity(path, cwd) {
|
|
561
|
+
return isAbsolute(path) || win32.isAbsolute(path) ? path : resolve(cwd, path);
|
|
562
|
+
}
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/plugins/sources/import.ts
|
|
565
|
+
async function importPlugin(target) {
|
|
566
|
+
const url = pathToFileURL(target.entry);
|
|
567
|
+
if (target.cache === "content") url.searchParams.set("content", createHash("sha256").update(await readFile(target.entry)).digest("hex"));
|
|
568
|
+
return getDefaultExport(await import(url.href));
|
|
569
|
+
}
|
|
570
|
+
function getDefaultExport(value) {
|
|
571
|
+
if (!isPlainObject(value)) return value;
|
|
572
|
+
const objectValue = value;
|
|
573
|
+
return Object.hasOwn(objectValue, "default") ? Reflect.get(objectValue, "default") : value;
|
|
574
|
+
}
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region src/config/static.ts
|
|
577
|
+
const filePatternSchema = union([string(), array(string())]);
|
|
578
|
+
const ruleSeveritySchema = picklist([
|
|
579
|
+
"error",
|
|
580
|
+
"off",
|
|
581
|
+
"warn"
|
|
582
|
+
]);
|
|
583
|
+
const ruleConfigEntrySchema = union([ruleSeveritySchema, tuple([ruleSeveritySchema])]);
|
|
584
|
+
const staticConfigItemSchema = looseObject({
|
|
585
|
+
agent: optional(unknown()),
|
|
586
|
+
basePath: optional(string()),
|
|
587
|
+
extends: optional(array(unknown())),
|
|
588
|
+
files: optional(array(filePatternSchema)),
|
|
589
|
+
ignore: optional(looseObject({ gitignore: optional(boolean()) })),
|
|
590
|
+
ignores: optional(array(string())),
|
|
591
|
+
language: optional(string()),
|
|
592
|
+
languageOptions: optional(record(string(), unknown())),
|
|
593
|
+
linterOptions: optional(looseObject({
|
|
594
|
+
noInlineConfig: optional(boolean()),
|
|
595
|
+
reportUnusedDisableDirectives: optional(ruleSeveritySchema)
|
|
596
|
+
})),
|
|
597
|
+
name: optional(string()),
|
|
598
|
+
plugins: optional(record(string(), unknown())),
|
|
599
|
+
processor: optional(unknown()),
|
|
600
|
+
rules: optional(record(string(), ruleConfigEntrySchema)),
|
|
601
|
+
runner: optional(unknown()),
|
|
602
|
+
settings: optional(record(string(), unknown()))
|
|
603
|
+
});
|
|
604
|
+
function parseStaticConfig(value, options = {}) {
|
|
605
|
+
const items = toAlintConfigItems(value, options);
|
|
606
|
+
const pluginsByAlias = /* @__PURE__ */ new Map();
|
|
607
|
+
return { groups: items.map((item) => {
|
|
608
|
+
return {
|
|
609
|
+
item,
|
|
610
|
+
plugins: readStaticPluginReferences(item, pluginsByAlias, options)
|
|
611
|
+
};
|
|
612
|
+
}) };
|
|
613
|
+
}
|
|
614
|
+
async function toAlintConfig(config, options) {
|
|
615
|
+
const pluginCache = /* @__PURE__ */ new Map();
|
|
616
|
+
return Promise.all(config.groups.map((group) => resolveStaticPlugins(group, options, pluginCache)));
|
|
617
|
+
}
|
|
618
|
+
function isPlainObject$2(value) {
|
|
619
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
620
|
+
}
|
|
621
|
+
function isTomlConfig(configFile) {
|
|
622
|
+
return extname(configFile ?? "").toLowerCase() === ".toml";
|
|
623
|
+
}
|
|
624
|
+
function parseStaticConfigInputArray(value, arrayStack = []) {
|
|
625
|
+
if (arrayStack.includes(value)) throw new Error("Circular static config array.");
|
|
626
|
+
return value.flatMap((item) => Array.isArray(item) ? parseStaticConfigInputArray(item, [...arrayStack, value]) : [parseStaticConfigItem(item)]);
|
|
627
|
+
}
|
|
628
|
+
function parseStaticConfigItem(value) {
|
|
629
|
+
if (Array.isArray(value)) throw new TypeError("Invalid type: Expected Object but received Array");
|
|
630
|
+
return parse(staticConfigItemSchema, value);
|
|
631
|
+
}
|
|
632
|
+
function parseStaticConfigItems(value) {
|
|
633
|
+
return value.map(parseStaticConfigItem);
|
|
634
|
+
}
|
|
635
|
+
function readConfigGroup(value) {
|
|
636
|
+
if (!isPlainObject$2(value)) return;
|
|
637
|
+
const wrapper = value;
|
|
638
|
+
if (wrapper.config === void 0) return;
|
|
639
|
+
if (!isPlainObject$2(wrapper.config) || !Array.isArray(wrapper.config.group)) throw new TypeError("Static config field \"config.group\" must be an array.");
|
|
640
|
+
return wrapper.config.group;
|
|
641
|
+
}
|
|
642
|
+
function readStaticPluginReferences(item, pluginsByAlias, options) {
|
|
643
|
+
if (!isPlainObject$2(item.plugins)) return [];
|
|
644
|
+
const references = [];
|
|
645
|
+
for (const [alias, plugin] of Object.entries(item.plugins)) {
|
|
646
|
+
if (typeof plugin !== "string") continue;
|
|
647
|
+
const specifier = parsePluginSpecifier(plugin, options);
|
|
648
|
+
const existing = pluginsByAlias.get(alias);
|
|
649
|
+
if (existing !== void 0 && getPluginSpecifierKey(existing) !== getPluginSpecifierKey(specifier)) throw new Error(`Static plugin "${alias}" is configured with multiple specifiers: "${existing.raw}" and "${specifier.raw}".`);
|
|
650
|
+
pluginsByAlias.set(alias, specifier);
|
|
651
|
+
references.push({
|
|
652
|
+
alias,
|
|
653
|
+
specifier
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
return references;
|
|
657
|
+
}
|
|
658
|
+
async function resolveStaticPlugins(group, options, pluginCache) {
|
|
659
|
+
if (group.item.plugins === void 0) {
|
|
660
|
+
const { plugins: _plugins, ...item } = group.item;
|
|
661
|
+
return item;
|
|
662
|
+
}
|
|
663
|
+
const plugins = {};
|
|
664
|
+
for (const [alias, plugin] of Object.entries(group.item.plugins)) {
|
|
665
|
+
if (typeof plugin !== "string") {
|
|
666
|
+
plugins[alias] = plugin;
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
const reference = group.plugins.find((item) => item.alias === alias);
|
|
670
|
+
if (reference === void 0) throw new Error(`Static plugin "${alias}" is missing a parsed plugin reference.`);
|
|
671
|
+
const pluginCacheKey = getPluginSpecifierKey(reference.specifier);
|
|
672
|
+
let resolvedPlugin = pluginCache.get(pluginCacheKey);
|
|
673
|
+
if (resolvedPlugin === void 0) {
|
|
674
|
+
resolvedPlugin = options.pluginResolver(reference);
|
|
675
|
+
pluginCache.set(pluginCacheKey, resolvedPlugin);
|
|
676
|
+
}
|
|
677
|
+
plugins[alias] = await resolvedPlugin;
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
...group.item,
|
|
681
|
+
plugins
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function toAlintConfigItems(value, options = {}) {
|
|
685
|
+
if (Array.isArray(value)) return parseStaticConfigInputArray(value);
|
|
686
|
+
const group = readConfigGroup(value);
|
|
687
|
+
if (group !== void 0) return parseStaticConfigItems(group);
|
|
688
|
+
if (isTomlConfig(options.configFile)) throw new Error("Static TOML config must use [[config.group]].");
|
|
689
|
+
if (value === void 0 || value === null) return [];
|
|
690
|
+
throw new Error("Static config must be a flat config array or { config: { group: [...] } }.");
|
|
691
|
+
}
|
|
692
|
+
//#endregion
|
|
10
693
|
//#region src/config/load.ts
|
|
11
694
|
async function loadAlintConfig(cwd, configFile) {
|
|
695
|
+
const staticConfig = await loadStaticConfig(cwd, configFile);
|
|
696
|
+
if (staticConfig.groups.flatMap((group) => group.plugins).length === 0) return toAlintConfig(staticConfig, { pluginResolver: async (reference) => {
|
|
697
|
+
throw new Error(`Static plugin "${reference.alias}" was not expected to require resolution.`);
|
|
698
|
+
} });
|
|
699
|
+
const lock = parsePluginLockFile(await loadPluginLockFile(cwd), { cwd });
|
|
700
|
+
const missing = listMissing(staticConfig, lock);
|
|
701
|
+
if (missing.length > 0) throw new Error(`Static plugin references are missing from the lock file: ${formatStaticPluginReferences(missing)}.\nRun: alint plugin install`);
|
|
702
|
+
const unresolved = await listUnresolved(staticConfig, lock);
|
|
703
|
+
if (unresolved.length > 0) {
|
|
704
|
+
const message = `Static plugins could not be resolved from the lock file: ${formatPluginLockEntries(unresolved)}.\nRun: alint plugin install`;
|
|
705
|
+
throw new Error(message, { cause: unresolved[0]?.resolutionError });
|
|
706
|
+
}
|
|
707
|
+
return toAlintConfig(staticConfig, { async pluginResolver(reference) {
|
|
708
|
+
return importPlugin(await resolvePluginImportTarget(lock.get(reference)));
|
|
709
|
+
} });
|
|
710
|
+
}
|
|
711
|
+
async function loadStaticConfig(cwd, configFile) {
|
|
12
712
|
const result = await loadConfig({
|
|
13
713
|
configFile,
|
|
14
714
|
cwd,
|
|
@@ -19,11 +719,17 @@ async function loadAlintConfig(cwd, configFile) {
|
|
|
19
719
|
}),
|
|
20
720
|
name: "alint"
|
|
21
721
|
});
|
|
22
|
-
if (result._configFile === void 0) return
|
|
23
|
-
return result.config
|
|
722
|
+
if (result._configFile === void 0) return parseStaticConfig(void 0);
|
|
723
|
+
return parseStaticConfig(result.config, { configFile: result._configFile });
|
|
724
|
+
}
|
|
725
|
+
function formatPluginLockEntries(entries) {
|
|
726
|
+
return entries.map((entry) => `${entry.alias} (${entry.specifier.raw})`).join(", ");
|
|
727
|
+
}
|
|
728
|
+
function formatStaticPluginReferences(references) {
|
|
729
|
+
return references.map((reference) => `${reference.alias} (${reference.specifier.raw})`).join(", ");
|
|
24
730
|
}
|
|
25
731
|
//#endregion
|
|
26
|
-
//#region src/ignore
|
|
732
|
+
//#region src/constants/patterns/ignore.ts
|
|
27
733
|
const ignorePatternsEslintDefaults = [
|
|
28
734
|
"**/node_modules/**",
|
|
29
735
|
".git/**",
|
|
@@ -63,22 +769,61 @@ const ignorePatternsCommon = [
|
|
|
63
769
|
...ignorePatternsGenerated
|
|
64
770
|
];
|
|
65
771
|
//#endregion
|
|
66
|
-
//#region src/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
772
|
+
//#region src/plugins/install.ts
|
|
773
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
774
|
+
async function installStaticPlugins(options) {
|
|
775
|
+
const configuredPlugins = (await loadStaticConfig(options.cwd, options.configFile)).groups.flatMap((group) => group.plugins);
|
|
776
|
+
const npmRegistry = options.registry ?? DEFAULT_REGISTRY;
|
|
777
|
+
const lockFile = createEmptyPluginLockFile();
|
|
778
|
+
const packageInstallations = /* @__PURE__ */ new Map();
|
|
779
|
+
const localDirectoryInstallations = /* @__PURE__ */ new Map();
|
|
780
|
+
const installedLocalDirectories = /* @__PURE__ */ new Map();
|
|
781
|
+
for (const configuredPlugin of configuredPlugins) {
|
|
782
|
+
const specifier = configuredPlugin.specifier;
|
|
783
|
+
if (specifier.type === "directory") {
|
|
784
|
+
const key = getPluginSpecifierKey(specifier);
|
|
785
|
+
let installation = localDirectoryInstallations.get(key);
|
|
786
|
+
if (installation === void 0) {
|
|
787
|
+
installation = install$1({
|
|
788
|
+
alias: configuredPlugin.alias,
|
|
789
|
+
specifier
|
|
790
|
+
});
|
|
791
|
+
localDirectoryInstallations.set(key, installation);
|
|
792
|
+
}
|
|
793
|
+
const installed = await installation;
|
|
794
|
+
const canonical = installed.path;
|
|
795
|
+
const entry = installedLocalDirectories.get(canonical) ?? installed;
|
|
796
|
+
installedLocalDirectories.set(canonical, entry);
|
|
797
|
+
lockFile.plugins[configuredPlugin.alias] = await createLockEntry$1(entry, {
|
|
798
|
+
alias: configuredPlugin.alias,
|
|
799
|
+
cwd: options.cwd,
|
|
800
|
+
specifier
|
|
801
|
+
});
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
const key = getPluginSpecifierKey(specifier);
|
|
805
|
+
let packageInstallation = packageInstallations.get(key);
|
|
806
|
+
if (packageInstallation === void 0) {
|
|
807
|
+
packageInstallation = install({
|
|
808
|
+
cwd: options.cwd,
|
|
809
|
+
npmRegistry,
|
|
810
|
+
specifier
|
|
811
|
+
});
|
|
812
|
+
packageInstallations.set(key, packageInstallation);
|
|
813
|
+
}
|
|
814
|
+
const installedPackage = await packageInstallation;
|
|
815
|
+
lockFile.plugins[configuredPlugin.alias] = createLockEntry(installedPackage, {
|
|
816
|
+
alias: configuredPlugin.alias,
|
|
817
|
+
specifier
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
await writePluginLockFile(options.cwd, lockFile);
|
|
821
|
+
return {
|
|
822
|
+
configuredPluginCount: configuredPlugins.length,
|
|
823
|
+
installedLocalDirectoryCount: installedLocalDirectories.size,
|
|
824
|
+
installedPackageCount: packageInstallations.size,
|
|
825
|
+
lock: lockFile
|
|
826
|
+
};
|
|
82
827
|
}
|
|
83
828
|
//#endregion
|
|
84
829
|
//#region src/setup/toml.ts
|
|
@@ -88,7 +833,7 @@ const modelSizes = /* @__PURE__ */ new Set([
|
|
|
88
833
|
"small"
|
|
89
834
|
]);
|
|
90
835
|
function parseSetupConfigToml(toml) {
|
|
91
|
-
const rawConfig = parse(toml);
|
|
836
|
+
const rawConfig = parse$1(toml);
|
|
92
837
|
if (rawConfig.version !== 1) throw new Error("Invalid setup config: version must be 1.");
|
|
93
838
|
if (!Array.isArray(rawConfig.providers)) throw new TypeError("Invalid setup config: providers must be an array.");
|
|
94
839
|
const parsedConfig = {
|
|
@@ -109,7 +854,7 @@ function stringifySetupConfigToml(config) {
|
|
|
109
854
|
function isModelSize(value) {
|
|
110
855
|
return typeof value === "string" && modelSizes.has(value);
|
|
111
856
|
}
|
|
112
|
-
function isPlainObject(value) {
|
|
857
|
+
function isPlainObject$1(value) {
|
|
113
858
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
114
859
|
}
|
|
115
860
|
function parseModel(providerId, model) {
|
|
@@ -155,7 +900,7 @@ function parseRunner(runner) {
|
|
|
155
900
|
}
|
|
156
901
|
function parseRunnerCache(cache) {
|
|
157
902
|
if (typeof cache === "boolean") return cache;
|
|
158
|
-
if (!isPlainObject(cache)) throw new TypeError("Invalid runner cache: must be a boolean or table.");
|
|
903
|
+
if (!isPlainObject$1(cache)) throw new TypeError("Invalid runner cache: must be a boolean or table.");
|
|
159
904
|
const tomlCache = cache;
|
|
160
905
|
const parsedCache = {};
|
|
161
906
|
if (tomlCache.enabled !== void 0) {
|
|
@@ -167,7 +912,7 @@ function parseRunnerCache(cache) {
|
|
|
167
912
|
}
|
|
168
913
|
function parseRunnerStats(stats) {
|
|
169
914
|
if (typeof stats === "boolean") return stats;
|
|
170
|
-
if (!isPlainObject(stats)) throw new TypeError("Invalid runner stats: must be a boolean or an object.");
|
|
915
|
+
if (!isPlainObject$1(stats)) throw new TypeError("Invalid runner stats: must be a boolean or an object.");
|
|
171
916
|
const tomlStats = stats;
|
|
172
917
|
const parsedStats = {};
|
|
173
918
|
if (tomlStats.enabled !== void 0) {
|
|
@@ -188,7 +933,7 @@ function readNonEmptyString(value, label) {
|
|
|
188
933
|
return stringValue;
|
|
189
934
|
}
|
|
190
935
|
function readRecord(value, label) {
|
|
191
|
-
if (!isPlainObject(value)) throw new Error(`Invalid ${label}: must be a table.`);
|
|
936
|
+
if (!isPlainObject$1(value)) throw new Error(`Invalid ${label}: must be a table.`);
|
|
192
937
|
return value;
|
|
193
938
|
}
|
|
194
939
|
function readString(value, label) {
|
|
@@ -254,7 +999,7 @@ async function loadSetupConfig(filePath) {
|
|
|
254
999
|
try {
|
|
255
1000
|
return parseSetupConfigToml(await readFile(filePath, "utf8"));
|
|
256
1001
|
} catch (error) {
|
|
257
|
-
if (
|
|
1002
|
+
if (isENOENTError(error)) return createEmptySetupConfig();
|
|
258
1003
|
throw error;
|
|
259
1004
|
}
|
|
260
1005
|
}
|
|
@@ -327,9 +1072,6 @@ function createEmptySetupConfig() {
|
|
|
327
1072
|
version: 1
|
|
328
1073
|
};
|
|
329
1074
|
}
|
|
330
|
-
function isNodeError(error) {
|
|
331
|
-
return error instanceof Error && "code" in error;
|
|
332
|
-
}
|
|
333
1075
|
function mergeModel(existingModel, incomingModel) {
|
|
334
1076
|
const existing = cloneModel(existingModel);
|
|
335
1077
|
const incoming = cloneModel(incomingModel);
|
|
@@ -348,8 +1090,8 @@ function prioritizeProviders(providers, prioritizedProviders) {
|
|
|
348
1090
|
//#endregion
|
|
349
1091
|
//#region src/setup/write.ts
|
|
350
1092
|
async function writeSetupConfig(filePath, config) {
|
|
351
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
1093
|
+
await mkdir(dirname$1(filePath), { recursive: true });
|
|
352
1094
|
await writeFile(filePath, stringifySetupConfigToml(config), "utf8");
|
|
353
1095
|
}
|
|
354
1096
|
//#endregion
|
|
355
|
-
export { emptySetupConfig, getGlobalSetupConfigPath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, loadAlintConfig, loadSetupConfig, mergeSetupConfigs, parseSetupConfigToml, stringifySetupConfigToml, writeSetupConfig };
|
|
1097
|
+
export { createEmptyPluginLockFile, emptySetupConfig, getGlobalSetupConfigPath, getProjectPluginLockPath, getProjectPluginStorePath, getProjectSetupConfigPath, getStatsDir, ignorePatternsAIAgents, ignorePatternsBuildOutputs, ignorePatternsCaches, ignorePatternsCommon, ignorePatternsEslintDefaults, ignorePatternsGenerated, installStaticPlugins, listMissing, listUnresolved, loadAlintConfig, loadPluginLockFile, loadSetupConfig, loadStaticConfig, mergeSetupConfigs, parsePluginLockFile, parsePluginSpecifier, parseSetupConfigToml, parseStaticConfig, stringifySetupConfigToml, toAlintConfig, writePluginLockFile, writeSetupConfig };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alint-js/config",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.20",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.mts",
|
|
@@ -13,15 +13,24 @@
|
|
|
13
13
|
"dist"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@moeru/std": "^0.1.0-beta.18",
|
|
17
|
+
"@package-json/types": "^0.0.13",
|
|
16
18
|
"c12": "^3.3.4",
|
|
19
|
+
"es-toolkit": "^1.49.0",
|
|
17
20
|
"jiti": "^2.7.0",
|
|
21
|
+
"ofetch": "^1.5.1",
|
|
18
22
|
"pathe": "^2.0.3",
|
|
23
|
+
"resolve.exports": "2.0.3",
|
|
19
24
|
"smol-toml": "^1.7.0",
|
|
25
|
+
"tar-stream": "^3.2.0",
|
|
26
|
+
"valibot": "^1.4.1",
|
|
20
27
|
"xdg-basedir": "^5.1.0",
|
|
21
|
-
"@alint-js/core": "0.0.
|
|
28
|
+
"@alint-js/core": "0.0.20"
|
|
22
29
|
},
|
|
23
30
|
"devDependencies": {
|
|
24
31
|
"@types/node": "^26.0.1",
|
|
32
|
+
"@types/tar-stream": "^3.1.4",
|
|
33
|
+
"h3": "2.0.1-rc.25",
|
|
25
34
|
"tsdown": "^0.22.3",
|
|
26
35
|
"typescript": "^6.0.3",
|
|
27
36
|
"vitest": "^4.1.9"
|