package-management 0.1.0 → 0.2.0

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/dist/index.cjs CHANGED
@@ -17,6 +17,7 @@ const gitignoreParser = require('parse-gitignore');
17
17
  const utils = require('pathe/utils');
18
18
  const os = require('node:os');
19
19
  const jsoncParser = require('jsonc-parser');
20
+ const confbox = require('confbox');
20
21
  const unstorage = require('unstorage');
21
22
  const defineMemoryDriver = require('unstorage/drivers/memory');
22
23
  const defineFsLiteDriver = require('unstorage/drivers/fs-lite');
@@ -101,6 +102,13 @@ function createFile(filePath, data, options) {
101
102
  node_fs.writeFileSync(filePath, data, writeFileOptions);
102
103
  }
103
104
 
105
+ function readFile(filePath, options) {
106
+ return node_fs.readFileSync(filePath, options?.encoding ?? "utf-8");
107
+ }
108
+ function readFileSafely(filePath, options) {
109
+ return node_fs.existsSync(filePath) ? readFile(filePath, options) : void 0;
110
+ }
111
+
104
112
  function isWritable(filename) {
105
113
  try {
106
114
  node_fs.accessSync(filename, node_fs.constants.W_OK);
@@ -1122,15 +1130,15 @@ function resolveJsonSource(source, as) {
1122
1130
  return resolved;
1123
1131
  }
1124
1132
 
1125
- const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1126
- function resolveEditPath(path, options) {
1133
+ const toPathSegment$1 = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1134
+ function resolveEditPath$1(path, options) {
1127
1135
  const { pathSeparator = "." } = options || {};
1128
1136
  if (Array.isArray(path)) return path;
1129
1137
  if (typeof path === "number" || pathSeparator === false) return [path];
1130
- return path.split(pathSeparator).map(toPathSegment);
1138
+ return path.split(pathSeparator).map(toPathSegment$1);
1131
1139
  }
1132
1140
  function parseJSONCEdits(edits, defaultEditOptions) {
1133
- const resolve = (path, options) => resolveEditPath(path, { ...defaultEditOptions, ...options });
1141
+ const resolve = (path, options) => resolveEditPath$1(path, { ...defaultEditOptions, ...options });
1134
1142
  if (Array.isArray(edits)) {
1135
1143
  return edits.map(({ path, ...edit }) => ({
1136
1144
  path: resolve(path, edit.options),
@@ -1188,6 +1196,154 @@ function modifyJSONFile(filepath, edits, options) {
1188
1196
  });
1189
1197
  }
1190
1198
 
1199
+ const configLanguages = {
1200
+ json: { parse: confbox.parseJSONC, stringify: confbox.stringifyJSONC, surgical: true },
1201
+ jsonc: { parse: confbox.parseJSONC, stringify: confbox.stringifyJSONC, surgical: true },
1202
+ json5: { parse: confbox.parseJSON5, stringify: confbox.stringifyJSON5, surgical: false },
1203
+ yaml: { parse: confbox.parseYAML, stringify: confbox.stringifyYAML, surgical: false },
1204
+ toml: { parse: confbox.parseTOML, stringify: confbox.stringifyTOML, surgical: false }
1205
+ };
1206
+ const extensionFormats = {
1207
+ ".json": "json",
1208
+ ".jsonc": "jsonc",
1209
+ ".json5": "json5",
1210
+ ".yaml": "yaml",
1211
+ ".yml": "yaml",
1212
+ ".toml": "toml"
1213
+ };
1214
+ function isConfigFormat(value) {
1215
+ return value in configLanguages;
1216
+ }
1217
+ function getConfigFormat(filepath) {
1218
+ const extension = path.extname(filepath).toLowerCase();
1219
+ return extension in extensionFormats ? extensionFormats[extension] : void 0;
1220
+ }
1221
+
1222
+ function resolveFormat(source) {
1223
+ if (source.format) return source.format;
1224
+ const inferred = source.filepath ? getConfigFormat(source.filepath) : void 0;
1225
+ if (!inferred) {
1226
+ throw new Error(
1227
+ source.filepath ? `Unsupported config format: ${source.filepath}` : "A `format` is required for a text or data source"
1228
+ );
1229
+ }
1230
+ return inferred;
1231
+ }
1232
+ function resolveConfigSource(source, as) {
1233
+ const format = resolveFormat(source);
1234
+ const language = configLanguages[format];
1235
+ const { text: sourceText, data: sourceData, filepath } = source;
1236
+ const resolvers = {
1237
+ data: () => ({
1238
+ text: () => language.stringify(sourceData),
1239
+ data: () => sourceData
1240
+ }),
1241
+ text: () => ({
1242
+ text: () => sourceText,
1243
+ data: () => language.parse(sourceText)
1244
+ }),
1245
+ filepath: () => {
1246
+ const text = node_fs.readFileSync(filepath, "utf-8");
1247
+ return {
1248
+ text: () => text,
1249
+ data: () => language.parse(text)
1250
+ };
1251
+ }
1252
+ };
1253
+ const selected = ["data", "text", "filepath"].find(
1254
+ (key) => source[key] !== void 0
1255
+ );
1256
+ if (!selected) {
1257
+ throw new Error("Invalid source data");
1258
+ }
1259
+ const sourceResolvers = resolvers[selected]();
1260
+ const resolved = fromEntries(
1261
+ entriesOf(sourceResolvers).flatMap(([key, value]) => {
1262
+ if (as && key !== as) return [];
1263
+ try {
1264
+ return [[key, value()]];
1265
+ } catch (error) {
1266
+ throw new Error(`Failed to resolve ${key} from ${selected}`, {
1267
+ cause: error
1268
+ });
1269
+ }
1270
+ })
1271
+ );
1272
+ if (as) {
1273
+ return resolved[as];
1274
+ }
1275
+ return resolved;
1276
+ }
1277
+
1278
+ const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1279
+ function resolveEditPath(path, options) {
1280
+ const { pathSeparator = "." } = options || {};
1281
+ if (Array.isArray(path)) return path;
1282
+ if (typeof path === "number" || pathSeparator === false) return [path];
1283
+ return path.split(pathSeparator).map(toPathSegment);
1284
+ }
1285
+ function toEditList(edits, defaultEditOptions) {
1286
+ const entries = Array.isArray(edits) ? edits : Object.entries(edits).map(([path, edit]) => ({ path, ...edit }));
1287
+ return entries.map(({ path, value, options }) => ({
1288
+ path: resolveEditPath(path, { ...defaultEditOptions, ...options }),
1289
+ value
1290
+ }));
1291
+ }
1292
+ function setPath(target, path, value) {
1293
+ const [head, ...rest] = path;
1294
+ if (head === void 0) return value;
1295
+ const container = Array.isArray(target) ? [...target] : { ...target ?? {} };
1296
+ container[head] = setPath(container[head], rest, value);
1297
+ return container;
1298
+ }
1299
+ function modifyConfig({
1300
+ config,
1301
+ edits,
1302
+ defaultEditOptions
1303
+ }) {
1304
+ return checkResult(() => {
1305
+ const format = config.format ?? (config.filepath ? getConfigFormat(config.filepath) : void 0) ?? "json";
1306
+ const language = configLanguages[format];
1307
+ if (language.surgical) {
1308
+ const { data, error } = modifyJSON({
1309
+ json: config.filepath ? { filepath: config.filepath } : config.text !== void 0 ? { text: config.text } : { data: config.data },
1310
+ edits,
1311
+ defaultEditOptions
1312
+ });
1313
+ if (error) throw error;
1314
+ return data;
1315
+ }
1316
+ const current = resolveConfigSource(config, "data");
1317
+ const updated = toEditList(edits, defaultEditOptions).reduce(
1318
+ (result, edit) => setPath(result, edit.path, edit.value),
1319
+ current
1320
+ );
1321
+ return resolveConfigSource({ text: language.stringify(updated), format });
1322
+ });
1323
+ }
1324
+ function modifyConfigFile(filepath, edits, options) {
1325
+ return checkResult(() => {
1326
+ const { autoCommit = true, defaultEditOptions, format } = options || {};
1327
+ const { data: config, error } = modifyConfig({
1328
+ config: format ? { filepath, format } : { filepath },
1329
+ edits,
1330
+ defaultEditOptions
1331
+ });
1332
+ if (error) {
1333
+ throw error;
1334
+ }
1335
+ const commit = () => node_fs.writeFileSync(filepath, config.text);
1336
+ if (autoCommit) {
1337
+ commit();
1338
+ return config;
1339
+ }
1340
+ return {
1341
+ config,
1342
+ commit
1343
+ };
1344
+ });
1345
+ }
1346
+
1191
1347
  function isStorageValue(input) {
1192
1348
  return input === null || typeof input === "string" || typeof input === "number" || typeof input === "boolean" || typeof input === "object";
1193
1349
  }
@@ -1276,6 +1432,7 @@ function defineFileSystemStorage(options) {
1276
1432
 
1277
1433
  exports._dirname = _dirname;
1278
1434
  exports._filename = _filename;
1435
+ exports.configLanguages = configLanguages;
1279
1436
  exports.createFile = createFile;
1280
1437
  exports.defineFileSystemEntries = defineFileSystemEntries;
1281
1438
  exports.defineFileSystemStorage = defineFileSystemStorage;
@@ -1292,6 +1449,7 @@ exports.findPackageManager = findPackageManager;
1292
1449
  exports.findPackageManagerSafely = findPackageManagerSafely;
1293
1450
  exports.findResolvedModulePath = findResolvedModulePath;
1294
1451
  exports.getAliasMap = getAliasMap;
1452
+ exports.getConfigFormat = getConfigFormat;
1295
1453
  exports.getFolderByPackageName = getFolderByPackageName;
1296
1454
  exports.getGitRootFolder = getGitRootFolder;
1297
1455
  exports.getGlobalVersions = getGlobalVersions;
@@ -1300,16 +1458,22 @@ exports.getPath = getPath;
1300
1458
  exports.getWorkspaceFolder = getWorkspaceFolder;
1301
1459
  exports.importMap = importMap;
1302
1460
  exports.importer = importer;
1461
+ exports.isConfigFormat = isConfigFormat;
1303
1462
  exports.isDependencyInPackageJson = isDependencyInPackageJson;
1304
1463
  exports.isPackageDependency = isPackageDependency;
1305
1464
  exports.isPackageModuleFound = isPackageModuleFound;
1306
1465
  exports.isWritable = isWritable;
1307
1466
  exports.mapPackageManagers = mapPackageManagers;
1467
+ exports.modifyConfig = modifyConfig;
1468
+ exports.modifyConfigFile = modifyConfigFile;
1308
1469
  exports.modifyJSON = modifyJSON;
1309
1470
  exports.modifyJSONFile = modifyJSONFile;
1310
1471
  exports.packageManagerConfigs = packageManagerConfigs;
1311
1472
  exports.predefinedPathAliases = predefinedPathAliases;
1312
1473
  exports.project = project;
1474
+ exports.readFile = readFile;
1475
+ exports.readFileSafely = readFileSafely;
1476
+ exports.resolveConfigSource = resolveConfigSource;
1313
1477
  exports.resolveModule = resolveModule;
1314
1478
  exports.resolveModulePath = resolveModulePath;
1315
1479
  exports.resolvePackageModulePath = resolvePackageModulePath;
package/dist/index.d.cts CHANGED
@@ -5,6 +5,7 @@ import { Options } from 'execa';
5
5
  import { ResolveOptions } from 'mlly';
6
6
  import * as parse_gitignore from 'parse-gitignore';
7
7
  import { ModificationOptions } from 'jsonc-parser';
8
+ import { parseJSONC, stringifyJSONC, parseJSON5, stringifyJSON5, parseYAML, stringifyYAML, parseTOML, stringifyTOML } from 'confbox';
8
9
  import { StorageValue, TransactionOptions, Snapshot, Storage } from 'unstorage';
9
10
  import { FSStorageOptions } from 'unstorage/drivers/fs-lite';
10
11
 
@@ -121,6 +122,18 @@ declare const _dirname: (options?: CallerLocationOptions) => string | undefined;
121
122
 
122
123
  declare function createFile(filePath: string, data: string, options?: WriteFileOptions): void;
123
124
 
125
+ interface ReadFileOptions {
126
+ /** @default "utf-8" */
127
+ encoding?: BufferEncoding;
128
+ }
129
+ declare function readFile(filePath: string, options?: ReadFileOptions): string;
130
+ /**
131
+ * `undefined` is a real answer: a config a tool has never written yet is
132
+ * absent rather than empty, and callers seeding one say so themselves
133
+ * instead of catching a throw to find out.
134
+ */
135
+ declare function readFileSafely(filePath: string, options?: ReadFileOptions): string | undefined;
136
+
124
137
  declare function isWritable(filename: string): boolean;
125
138
 
126
139
  interface PackageManagerConfig<ID extends string = string> {
@@ -895,6 +908,95 @@ type ModifyJSONFileResult<$auto_commit extends boolean = true> = $auto_commit ex
895
908
  }>;
896
909
  declare function modifyJSONFile<$auto_commit extends boolean = true>(filepath: string, edits: JSONEdits, options?: MoodifyJSONFileOptions<$auto_commit>): ModifyJSONFileResult<$auto_commit>;
897
910
 
911
+ /**
912
+ * The configuration languages a project actually ships: `package.json` and
913
+ * `tsconfig.json`, `.prettierrc.json5`, CI and Compose YAML, and the TOML
914
+ * that Cargo, Codex, and Ruff read.
915
+ */
916
+ type ConfigFormat = "json" | "jsonc" | "json5" | "yaml" | "toml";
917
+ type ConfigSourceInputType = keyof ConfigSourceInput;
918
+ interface ConfigSourceData<$config extends object = object> {
919
+ text: string;
920
+ data: $config;
921
+ }
922
+ type ConfigSourceInput<data extends object = object> = Prettify<RequireExactlyOne<{
923
+ data?: data;
924
+ filepath?: string;
925
+ text?: string;
926
+ }> & {
927
+ /**
928
+ * Inferred from a `filepath`'s extension. Required for `text` and `data`,
929
+ * which carry no extension to read it from.
930
+ */
931
+ format?: ConfigFormat;
932
+ }>;
933
+
934
+ /**
935
+ * One edit vocabulary across every language. A dot path addresses nesting,
936
+ * an array spells segments out literally, and both are what `modifyJSON`
937
+ * already accepts — so an edit written for `package.json` reads the same
938
+ * written for `config.toml`.
939
+ */
940
+ type ConfigEdits = JSONEdits;
941
+ type ConfigEditData = JSONEditData;
942
+ type ConfigEditOptions = JSONEditOptions;
943
+ interface ModifyConfigOptions<$config extends object = object> {
944
+ config: ConfigSourceInput<$config>;
945
+ edits: ConfigEdits;
946
+ defaultEditOptions?: ConfigEditOptions;
947
+ }
948
+ declare function modifyConfig({ config, edits, defaultEditOptions, }: ModifyConfigOptions): CheckResult<ConfigSourceData>;
949
+ interface ModifyConfigFileOptions<$auto_commit extends boolean = boolean> {
950
+ autoCommit?: $auto_commit;
951
+ defaultEditOptions?: ConfigEditOptions;
952
+ /** Overrides the format the file's extension implies. */
953
+ format?: ConfigFormat;
954
+ }
955
+ type ModifyConfigFileResult<$auto_commit extends boolean = true> = $auto_commit extends true ? CheckResult<ConfigSourceData> : CheckResult<{
956
+ config: ConfigSourceData;
957
+ commit: () => void;
958
+ }>;
959
+ declare function modifyConfigFile<$auto_commit extends boolean = true>(filepath: string, edits: ConfigEdits, options?: ModifyConfigFileOptions<$auto_commit>): ModifyConfigFileResult<$auto_commit>;
960
+
961
+ declare const configLanguages: {
962
+ readonly json: {
963
+ readonly parse: typeof parseJSONC;
964
+ readonly stringify: typeof stringifyJSONC;
965
+ readonly surgical: true;
966
+ };
967
+ readonly jsonc: {
968
+ readonly parse: typeof parseJSONC;
969
+ readonly stringify: typeof stringifyJSONC;
970
+ readonly surgical: true;
971
+ };
972
+ readonly json5: {
973
+ readonly parse: typeof parseJSON5;
974
+ readonly stringify: typeof stringifyJSON5;
975
+ readonly surgical: false;
976
+ };
977
+ readonly yaml: {
978
+ readonly parse: typeof parseYAML;
979
+ readonly stringify: typeof stringifyYAML;
980
+ readonly surgical: false;
981
+ };
982
+ readonly toml: {
983
+ readonly parse: typeof parseTOML;
984
+ readonly stringify: typeof stringifyTOML;
985
+ readonly surgical: false;
986
+ };
987
+ };
988
+ declare function isConfigFormat(value: string): value is ConfigFormat;
989
+ /**
990
+ * `undefined` is a real answer: a path can name an extension this module has
991
+ * no language for, and callers say what to do about it rather than receiving
992
+ * a format picked at random.
993
+ */
994
+ declare function getConfigFormat(filepath: string): ConfigFormat | undefined;
995
+
996
+ type InferConfig<$source extends ConfigSourceInput> = $source extends ConfigSourceInput<infer $config> ? $config : object;
997
+ type ResolvedConfigSourceData<$as extends "data" | "text" = never, $config extends object = object> = IsNever<$as> extends false ? $as extends string ? ConfigSourceData<$config>[$as] : ConfigSourceData<$config> : ConfigSourceData<$config>;
998
+ declare function resolveConfigSource<$source extends ConfigSourceInput, $as extends "data" | "text" = never>(source: $source, as?: $as): ResolvedConfigSourceData<$as, InferConfig<$source>>;
999
+
898
1000
  declare const storage: Storage<StorageValue>;
899
1001
  declare const tempFileSystem: FileSystemStorage<FileSystemEntriesDefinition<string>, FileSystemPathCompletion<FileSystemEntriesDefinition<string>>>;
900
1002
  type FileSystemEntriesDefinition<$filepath extends string = string> = {
@@ -957,5 +1059,5 @@ type ExtractFsEntriesDef<$fs_def> = Cast<$fs_def, FileSystemEntriesDefinition>;
957
1059
  declare function defineFileSystemStorage<const $fs_storage_def extends DefineFileSystemOptions = DefineFileSystemOptions, // prettier-ignore
958
1060
  $fs_def extends $fs_storage_def['initial'] = $fs_storage_def['initial']>(options: $fs_storage_def): FileSystemStorage<ExtractFsEntriesDef<$fs_def>>;
959
1061
 
960
- export { _dirname, _filename, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
961
- export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
1062
+ export { _dirname, _filename, configLanguages, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getConfigFormat, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isConfigFormat, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyConfig, modifyConfigFile, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, readFile, readFileSafely, resolveConfigSource, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
1063
+ export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, ConfigEditData, ConfigEditOptions, ConfigEdits, ConfigFormat, ConfigSourceData, ConfigSourceInput, ConfigSourceInputType, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyConfigFileOptions, ModifyConfigOptions, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, ReadFileOptions, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
package/dist/index.d.mts CHANGED
@@ -5,6 +5,7 @@ import { Options } from 'execa';
5
5
  import { ResolveOptions } from 'mlly';
6
6
  import * as parse_gitignore from 'parse-gitignore';
7
7
  import { ModificationOptions } from 'jsonc-parser';
8
+ import { parseJSONC, stringifyJSONC, parseJSON5, stringifyJSON5, parseYAML, stringifyYAML, parseTOML, stringifyTOML } from 'confbox';
8
9
  import { StorageValue, TransactionOptions, Snapshot, Storage } from 'unstorage';
9
10
  import { FSStorageOptions } from 'unstorage/drivers/fs-lite';
10
11
 
@@ -121,6 +122,18 @@ declare const _dirname: (options?: CallerLocationOptions) => string | undefined;
121
122
 
122
123
  declare function createFile(filePath: string, data: string, options?: WriteFileOptions): void;
123
124
 
125
+ interface ReadFileOptions {
126
+ /** @default "utf-8" */
127
+ encoding?: BufferEncoding;
128
+ }
129
+ declare function readFile(filePath: string, options?: ReadFileOptions): string;
130
+ /**
131
+ * `undefined` is a real answer: a config a tool has never written yet is
132
+ * absent rather than empty, and callers seeding one say so themselves
133
+ * instead of catching a throw to find out.
134
+ */
135
+ declare function readFileSafely(filePath: string, options?: ReadFileOptions): string | undefined;
136
+
124
137
  declare function isWritable(filename: string): boolean;
125
138
 
126
139
  interface PackageManagerConfig<ID extends string = string> {
@@ -895,6 +908,95 @@ type ModifyJSONFileResult<$auto_commit extends boolean = true> = $auto_commit ex
895
908
  }>;
896
909
  declare function modifyJSONFile<$auto_commit extends boolean = true>(filepath: string, edits: JSONEdits, options?: MoodifyJSONFileOptions<$auto_commit>): ModifyJSONFileResult<$auto_commit>;
897
910
 
911
+ /**
912
+ * The configuration languages a project actually ships: `package.json` and
913
+ * `tsconfig.json`, `.prettierrc.json5`, CI and Compose YAML, and the TOML
914
+ * that Cargo, Codex, and Ruff read.
915
+ */
916
+ type ConfigFormat = "json" | "jsonc" | "json5" | "yaml" | "toml";
917
+ type ConfigSourceInputType = keyof ConfigSourceInput;
918
+ interface ConfigSourceData<$config extends object = object> {
919
+ text: string;
920
+ data: $config;
921
+ }
922
+ type ConfigSourceInput<data extends object = object> = Prettify<RequireExactlyOne<{
923
+ data?: data;
924
+ filepath?: string;
925
+ text?: string;
926
+ }> & {
927
+ /**
928
+ * Inferred from a `filepath`'s extension. Required for `text` and `data`,
929
+ * which carry no extension to read it from.
930
+ */
931
+ format?: ConfigFormat;
932
+ }>;
933
+
934
+ /**
935
+ * One edit vocabulary across every language. A dot path addresses nesting,
936
+ * an array spells segments out literally, and both are what `modifyJSON`
937
+ * already accepts — so an edit written for `package.json` reads the same
938
+ * written for `config.toml`.
939
+ */
940
+ type ConfigEdits = JSONEdits;
941
+ type ConfigEditData = JSONEditData;
942
+ type ConfigEditOptions = JSONEditOptions;
943
+ interface ModifyConfigOptions<$config extends object = object> {
944
+ config: ConfigSourceInput<$config>;
945
+ edits: ConfigEdits;
946
+ defaultEditOptions?: ConfigEditOptions;
947
+ }
948
+ declare function modifyConfig({ config, edits, defaultEditOptions, }: ModifyConfigOptions): CheckResult<ConfigSourceData>;
949
+ interface ModifyConfigFileOptions<$auto_commit extends boolean = boolean> {
950
+ autoCommit?: $auto_commit;
951
+ defaultEditOptions?: ConfigEditOptions;
952
+ /** Overrides the format the file's extension implies. */
953
+ format?: ConfigFormat;
954
+ }
955
+ type ModifyConfigFileResult<$auto_commit extends boolean = true> = $auto_commit extends true ? CheckResult<ConfigSourceData> : CheckResult<{
956
+ config: ConfigSourceData;
957
+ commit: () => void;
958
+ }>;
959
+ declare function modifyConfigFile<$auto_commit extends boolean = true>(filepath: string, edits: ConfigEdits, options?: ModifyConfigFileOptions<$auto_commit>): ModifyConfigFileResult<$auto_commit>;
960
+
961
+ declare const configLanguages: {
962
+ readonly json: {
963
+ readonly parse: typeof parseJSONC;
964
+ readonly stringify: typeof stringifyJSONC;
965
+ readonly surgical: true;
966
+ };
967
+ readonly jsonc: {
968
+ readonly parse: typeof parseJSONC;
969
+ readonly stringify: typeof stringifyJSONC;
970
+ readonly surgical: true;
971
+ };
972
+ readonly json5: {
973
+ readonly parse: typeof parseJSON5;
974
+ readonly stringify: typeof stringifyJSON5;
975
+ readonly surgical: false;
976
+ };
977
+ readonly yaml: {
978
+ readonly parse: typeof parseYAML;
979
+ readonly stringify: typeof stringifyYAML;
980
+ readonly surgical: false;
981
+ };
982
+ readonly toml: {
983
+ readonly parse: typeof parseTOML;
984
+ readonly stringify: typeof stringifyTOML;
985
+ readonly surgical: false;
986
+ };
987
+ };
988
+ declare function isConfigFormat(value: string): value is ConfigFormat;
989
+ /**
990
+ * `undefined` is a real answer: a path can name an extension this module has
991
+ * no language for, and callers say what to do about it rather than receiving
992
+ * a format picked at random.
993
+ */
994
+ declare function getConfigFormat(filepath: string): ConfigFormat | undefined;
995
+
996
+ type InferConfig<$source extends ConfigSourceInput> = $source extends ConfigSourceInput<infer $config> ? $config : object;
997
+ type ResolvedConfigSourceData<$as extends "data" | "text" = never, $config extends object = object> = IsNever<$as> extends false ? $as extends string ? ConfigSourceData<$config>[$as] : ConfigSourceData<$config> : ConfigSourceData<$config>;
998
+ declare function resolveConfigSource<$source extends ConfigSourceInput, $as extends "data" | "text" = never>(source: $source, as?: $as): ResolvedConfigSourceData<$as, InferConfig<$source>>;
999
+
898
1000
  declare const storage: Storage<StorageValue>;
899
1001
  declare const tempFileSystem: FileSystemStorage<FileSystemEntriesDefinition<string>, FileSystemPathCompletion<FileSystemEntriesDefinition<string>>>;
900
1002
  type FileSystemEntriesDefinition<$filepath extends string = string> = {
@@ -957,5 +1059,5 @@ type ExtractFsEntriesDef<$fs_def> = Cast<$fs_def, FileSystemEntriesDefinition>;
957
1059
  declare function defineFileSystemStorage<const $fs_storage_def extends DefineFileSystemOptions = DefineFileSystemOptions, // prettier-ignore
958
1060
  $fs_def extends $fs_storage_def['initial'] = $fs_storage_def['initial']>(options: $fs_storage_def): FileSystemStorage<ExtractFsEntriesDef<$fs_def>>;
959
1061
 
960
- export { _dirname, _filename, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
961
- export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
1062
+ export { _dirname, _filename, configLanguages, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getConfigFormat, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isConfigFormat, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyConfig, modifyConfigFile, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, readFile, readFileSafely, resolveConfigSource, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
1063
+ export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, ConfigEditData, ConfigEditOptions, ConfigEdits, ConfigFormat, ConfigSourceData, ConfigSourceInput, ConfigSourceInputType, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyConfigFileOptions, ModifyConfigOptions, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, ReadFileOptions, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { Options } from 'execa';
5
5
  import { ResolveOptions } from 'mlly';
6
6
  import * as parse_gitignore from 'parse-gitignore';
7
7
  import { ModificationOptions } from 'jsonc-parser';
8
+ import { parseJSONC, stringifyJSONC, parseJSON5, stringifyJSON5, parseYAML, stringifyYAML, parseTOML, stringifyTOML } from 'confbox';
8
9
  import { StorageValue, TransactionOptions, Snapshot, Storage } from 'unstorage';
9
10
  import { FSStorageOptions } from 'unstorage/drivers/fs-lite';
10
11
 
@@ -121,6 +122,18 @@ declare const _dirname: (options?: CallerLocationOptions) => string | undefined;
121
122
 
122
123
  declare function createFile(filePath: string, data: string, options?: WriteFileOptions): void;
123
124
 
125
+ interface ReadFileOptions {
126
+ /** @default "utf-8" */
127
+ encoding?: BufferEncoding;
128
+ }
129
+ declare function readFile(filePath: string, options?: ReadFileOptions): string;
130
+ /**
131
+ * `undefined` is a real answer: a config a tool has never written yet is
132
+ * absent rather than empty, and callers seeding one say so themselves
133
+ * instead of catching a throw to find out.
134
+ */
135
+ declare function readFileSafely(filePath: string, options?: ReadFileOptions): string | undefined;
136
+
124
137
  declare function isWritable(filename: string): boolean;
125
138
 
126
139
  interface PackageManagerConfig<ID extends string = string> {
@@ -895,6 +908,95 @@ type ModifyJSONFileResult<$auto_commit extends boolean = true> = $auto_commit ex
895
908
  }>;
896
909
  declare function modifyJSONFile<$auto_commit extends boolean = true>(filepath: string, edits: JSONEdits, options?: MoodifyJSONFileOptions<$auto_commit>): ModifyJSONFileResult<$auto_commit>;
897
910
 
911
+ /**
912
+ * The configuration languages a project actually ships: `package.json` and
913
+ * `tsconfig.json`, `.prettierrc.json5`, CI and Compose YAML, and the TOML
914
+ * that Cargo, Codex, and Ruff read.
915
+ */
916
+ type ConfigFormat = "json" | "jsonc" | "json5" | "yaml" | "toml";
917
+ type ConfigSourceInputType = keyof ConfigSourceInput;
918
+ interface ConfigSourceData<$config extends object = object> {
919
+ text: string;
920
+ data: $config;
921
+ }
922
+ type ConfigSourceInput<data extends object = object> = Prettify<RequireExactlyOne<{
923
+ data?: data;
924
+ filepath?: string;
925
+ text?: string;
926
+ }> & {
927
+ /**
928
+ * Inferred from a `filepath`'s extension. Required for `text` and `data`,
929
+ * which carry no extension to read it from.
930
+ */
931
+ format?: ConfigFormat;
932
+ }>;
933
+
934
+ /**
935
+ * One edit vocabulary across every language. A dot path addresses nesting,
936
+ * an array spells segments out literally, and both are what `modifyJSON`
937
+ * already accepts — so an edit written for `package.json` reads the same
938
+ * written for `config.toml`.
939
+ */
940
+ type ConfigEdits = JSONEdits;
941
+ type ConfigEditData = JSONEditData;
942
+ type ConfigEditOptions = JSONEditOptions;
943
+ interface ModifyConfigOptions<$config extends object = object> {
944
+ config: ConfigSourceInput<$config>;
945
+ edits: ConfigEdits;
946
+ defaultEditOptions?: ConfigEditOptions;
947
+ }
948
+ declare function modifyConfig({ config, edits, defaultEditOptions, }: ModifyConfigOptions): CheckResult<ConfigSourceData>;
949
+ interface ModifyConfigFileOptions<$auto_commit extends boolean = boolean> {
950
+ autoCommit?: $auto_commit;
951
+ defaultEditOptions?: ConfigEditOptions;
952
+ /** Overrides the format the file's extension implies. */
953
+ format?: ConfigFormat;
954
+ }
955
+ type ModifyConfigFileResult<$auto_commit extends boolean = true> = $auto_commit extends true ? CheckResult<ConfigSourceData> : CheckResult<{
956
+ config: ConfigSourceData;
957
+ commit: () => void;
958
+ }>;
959
+ declare function modifyConfigFile<$auto_commit extends boolean = true>(filepath: string, edits: ConfigEdits, options?: ModifyConfigFileOptions<$auto_commit>): ModifyConfigFileResult<$auto_commit>;
960
+
961
+ declare const configLanguages: {
962
+ readonly json: {
963
+ readonly parse: typeof parseJSONC;
964
+ readonly stringify: typeof stringifyJSONC;
965
+ readonly surgical: true;
966
+ };
967
+ readonly jsonc: {
968
+ readonly parse: typeof parseJSONC;
969
+ readonly stringify: typeof stringifyJSONC;
970
+ readonly surgical: true;
971
+ };
972
+ readonly json5: {
973
+ readonly parse: typeof parseJSON5;
974
+ readonly stringify: typeof stringifyJSON5;
975
+ readonly surgical: false;
976
+ };
977
+ readonly yaml: {
978
+ readonly parse: typeof parseYAML;
979
+ readonly stringify: typeof stringifyYAML;
980
+ readonly surgical: false;
981
+ };
982
+ readonly toml: {
983
+ readonly parse: typeof parseTOML;
984
+ readonly stringify: typeof stringifyTOML;
985
+ readonly surgical: false;
986
+ };
987
+ };
988
+ declare function isConfigFormat(value: string): value is ConfigFormat;
989
+ /**
990
+ * `undefined` is a real answer: a path can name an extension this module has
991
+ * no language for, and callers say what to do about it rather than receiving
992
+ * a format picked at random.
993
+ */
994
+ declare function getConfigFormat(filepath: string): ConfigFormat | undefined;
995
+
996
+ type InferConfig<$source extends ConfigSourceInput> = $source extends ConfigSourceInput<infer $config> ? $config : object;
997
+ type ResolvedConfigSourceData<$as extends "data" | "text" = never, $config extends object = object> = IsNever<$as> extends false ? $as extends string ? ConfigSourceData<$config>[$as] : ConfigSourceData<$config> : ConfigSourceData<$config>;
998
+ declare function resolveConfigSource<$source extends ConfigSourceInput, $as extends "data" | "text" = never>(source: $source, as?: $as): ResolvedConfigSourceData<$as, InferConfig<$source>>;
999
+
898
1000
  declare const storage: Storage<StorageValue>;
899
1001
  declare const tempFileSystem: FileSystemStorage<FileSystemEntriesDefinition<string>, FileSystemPathCompletion<FileSystemEntriesDefinition<string>>>;
900
1002
  type FileSystemEntriesDefinition<$filepath extends string = string> = {
@@ -957,5 +1059,5 @@ type ExtractFsEntriesDef<$fs_def> = Cast<$fs_def, FileSystemEntriesDefinition>;
957
1059
  declare function defineFileSystemStorage<const $fs_storage_def extends DefineFileSystemOptions = DefineFileSystemOptions, // prettier-ignore
958
1060
  $fs_def extends $fs_storage_def['initial'] = $fs_storage_def['initial']>(options: $fs_storage_def): FileSystemStorage<ExtractFsEntriesDef<$fs_def>>;
959
1061
 
960
- export { _dirname, _filename, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
961
- export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
1062
+ export { _dirname, _filename, configLanguages, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, dependencyTypeMap, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getConfigFormat, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isConfigFormat, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyConfig, modifyConfigFile, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, readFile, readFileSafely, resolveConfigSource, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
1063
+ export type { AliasDefinition, AliasDefinitionMap, AliasMap, AnyFunction, AsyncCacheFn, Awaitable, CallerLocationOptions, Cast, CheckResult, ConfigEditData, ConfigEditOptions, ConfigEdits, ConfigFormat, ConfigSourceData, ConfigSourceInput, ConfigSourceInputType, DefinePackageFn, DetectPackageManagerOptions, Entry, EntryOf, EnumToLiteral, Equals, ExtractImportOptionModule, FileSystemEntriesDefinition, FindDependencyInPackageJsonOptions, FromEntries, GetGitRootFolderOptions, GetPathOptions, GetWorkspaceFolderOptions, ImportCallback, ImportList, ImportMap, ImportMapFn, ImportMapOptions, ImportModuleData, ImportModuleFn, ImportOption, ImportOptionData, ImportPackageData, ImporterOptions, InstallPackageOptions, InstallerFn, IsExactBoolean, IsNever, IsUnion, IsUnknown, JSONEditData, JSONEditMap, JSONEditOptions, JSONEdits, KeyOf, KnownPackageJson, MergeObject, ModifyConfigFileOptions, ModifyConfigOptions, ModifyJSONCDataOptions, Module, MoodifyJSONFileOptions, NoInfer, OmitByValue, OmitIndexSignature, OmitNever, PackageDependencyItem, PackageDependencyType, PackageInfo, PackageInfoList, PackageInfoMap, PackageJson, PackageJsonPerson, PackageManager, PackageManagerCommandName, PackageManagerCommandSpec, PackageManagerCommands, PackageManagerConfig, PackageManagerId, PackageManagerScriptOptions, PackageManagers, PackageName, PathAlias, PathAliasResolveOptions, PathOptions, PathTo, PickByValue, PickKeyOf, PickPathAlias, PredefinedPathAliases, Prettify, ProjectParams, ReadFileOptions, RequireExactlyOne, ResolveModulePathOptions, ResolvedImportList, ResolvedImportListPromise, ResolvedImportMap, ResolvedImportMapPromise, ResolvedImportOption, ResolvedPromise, Select, SelectionMap, SingleProp, StringLiteral, UninstallPackageOptions, ValueAtPath, ValueKeyOf, ValueKeyOfDeep, ValueOf, __ };
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
- import path, { isAbsolute, dirname, normalize, join } from 'pathe';
1
+ import path, { isAbsolute, dirname, normalize, join, extname } from 'pathe';
2
2
  import util from 'node:util';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { existsSync, mkdirSync, writeFileSync, accessSync, constants, statSync, readFileSync } from 'node:fs';
4
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, accessSync, constants, statSync } from 'node:fs';
5
5
  import { asyncCacheFn } from 'async-cache-fn';
6
6
  import { execaSync, execa } from 'execa';
7
7
  import { findUp } from 'find-up';
8
- import { readFile, rm } from 'node:fs/promises';
8
+ import { readFile as readFile$1, rm } from 'node:fs/promises';
9
9
  import { resolvePathSync } from 'mlly';
10
10
  import * as WST from 'workspace-tools';
11
11
  import process from 'node:process';
@@ -15,6 +15,7 @@ import gitignoreParser from 'parse-gitignore';
15
15
  import { resolveAlias } from 'pathe/utils';
16
16
  import os from 'node:os';
17
17
  import { parse, applyEdits, modify } from 'jsonc-parser';
18
+ import { stringifyTOML, parseTOML, stringifyYAML, parseYAML, stringifyJSON5, parseJSON5, stringifyJSONC, parseJSONC } from 'confbox';
18
19
  import { createStorage, restoreSnapshot, snapshot } from 'unstorage';
19
20
  import defineMemoryDriver from 'unstorage/drivers/memory';
20
21
  import defineFsLiteDriver from 'unstorage/drivers/fs-lite';
@@ -74,6 +75,13 @@ function createFile(filePath, data, options) {
74
75
  writeFileSync(filePath, data, writeFileOptions);
75
76
  }
76
77
 
78
+ function readFile(filePath, options) {
79
+ return readFileSync(filePath, options?.encoding ?? "utf-8");
80
+ }
81
+ function readFileSafely(filePath, options) {
82
+ return existsSync(filePath) ? readFile(filePath, options) : void 0;
83
+ }
84
+
77
85
  function isWritable(filename) {
78
86
  try {
79
87
  accessSync(filename, constants.W_OK);
@@ -576,7 +584,7 @@ function definePackageManager(config, options) {
576
584
  readLockfile: asyncCacheFn(async (...args) => {
577
585
  const lockfilePath = await findLockfilePath.noCache(...args);
578
586
  if (!lockfilePath) return void 0;
579
- return readFile(lockfilePath, "utf8");
587
+ return readFile$1(lockfilePath, "utf8");
580
588
  }),
581
589
  globalVersion,
582
590
  matchesVersion: asyncCacheFn(async (...args) => {
@@ -1095,15 +1103,15 @@ function resolveJsonSource(source, as) {
1095
1103
  return resolved;
1096
1104
  }
1097
1105
 
1098
- const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1099
- function resolveEditPath(path, options) {
1106
+ const toPathSegment$1 = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1107
+ function resolveEditPath$1(path, options) {
1100
1108
  const { pathSeparator = "." } = options || {};
1101
1109
  if (Array.isArray(path)) return path;
1102
1110
  if (typeof path === "number" || pathSeparator === false) return [path];
1103
- return path.split(pathSeparator).map(toPathSegment);
1111
+ return path.split(pathSeparator).map(toPathSegment$1);
1104
1112
  }
1105
1113
  function parseJSONCEdits(edits, defaultEditOptions) {
1106
- const resolve = (path, options) => resolveEditPath(path, { ...defaultEditOptions, ...options });
1114
+ const resolve = (path, options) => resolveEditPath$1(path, { ...defaultEditOptions, ...options });
1107
1115
  if (Array.isArray(edits)) {
1108
1116
  return edits.map(({ path, ...edit }) => ({
1109
1117
  path: resolve(path, edit.options),
@@ -1161,6 +1169,154 @@ function modifyJSONFile(filepath, edits, options) {
1161
1169
  });
1162
1170
  }
1163
1171
 
1172
+ const configLanguages = {
1173
+ json: { parse: parseJSONC, stringify: stringifyJSONC, surgical: true },
1174
+ jsonc: { parse: parseJSONC, stringify: stringifyJSONC, surgical: true },
1175
+ json5: { parse: parseJSON5, stringify: stringifyJSON5, surgical: false },
1176
+ yaml: { parse: parseYAML, stringify: stringifyYAML, surgical: false },
1177
+ toml: { parse: parseTOML, stringify: stringifyTOML, surgical: false }
1178
+ };
1179
+ const extensionFormats = {
1180
+ ".json": "json",
1181
+ ".jsonc": "jsonc",
1182
+ ".json5": "json5",
1183
+ ".yaml": "yaml",
1184
+ ".yml": "yaml",
1185
+ ".toml": "toml"
1186
+ };
1187
+ function isConfigFormat(value) {
1188
+ return value in configLanguages;
1189
+ }
1190
+ function getConfigFormat(filepath) {
1191
+ const extension = extname(filepath).toLowerCase();
1192
+ return extension in extensionFormats ? extensionFormats[extension] : void 0;
1193
+ }
1194
+
1195
+ function resolveFormat(source) {
1196
+ if (source.format) return source.format;
1197
+ const inferred = source.filepath ? getConfigFormat(source.filepath) : void 0;
1198
+ if (!inferred) {
1199
+ throw new Error(
1200
+ source.filepath ? `Unsupported config format: ${source.filepath}` : "A `format` is required for a text or data source"
1201
+ );
1202
+ }
1203
+ return inferred;
1204
+ }
1205
+ function resolveConfigSource(source, as) {
1206
+ const format = resolveFormat(source);
1207
+ const language = configLanguages[format];
1208
+ const { text: sourceText, data: sourceData, filepath } = source;
1209
+ const resolvers = {
1210
+ data: () => ({
1211
+ text: () => language.stringify(sourceData),
1212
+ data: () => sourceData
1213
+ }),
1214
+ text: () => ({
1215
+ text: () => sourceText,
1216
+ data: () => language.parse(sourceText)
1217
+ }),
1218
+ filepath: () => {
1219
+ const text = readFileSync(filepath, "utf-8");
1220
+ return {
1221
+ text: () => text,
1222
+ data: () => language.parse(text)
1223
+ };
1224
+ }
1225
+ };
1226
+ const selected = ["data", "text", "filepath"].find(
1227
+ (key) => source[key] !== void 0
1228
+ );
1229
+ if (!selected) {
1230
+ throw new Error("Invalid source data");
1231
+ }
1232
+ const sourceResolvers = resolvers[selected]();
1233
+ const resolved = fromEntries(
1234
+ entriesOf(sourceResolvers).flatMap(([key, value]) => {
1235
+ if (as && key !== as) return [];
1236
+ try {
1237
+ return [[key, value()]];
1238
+ } catch (error) {
1239
+ throw new Error(`Failed to resolve ${key} from ${selected}`, {
1240
+ cause: error
1241
+ });
1242
+ }
1243
+ })
1244
+ );
1245
+ if (as) {
1246
+ return resolved[as];
1247
+ }
1248
+ return resolved;
1249
+ }
1250
+
1251
+ const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
1252
+ function resolveEditPath(path, options) {
1253
+ const { pathSeparator = "." } = options || {};
1254
+ if (Array.isArray(path)) return path;
1255
+ if (typeof path === "number" || pathSeparator === false) return [path];
1256
+ return path.split(pathSeparator).map(toPathSegment);
1257
+ }
1258
+ function toEditList(edits, defaultEditOptions) {
1259
+ const entries = Array.isArray(edits) ? edits : Object.entries(edits).map(([path, edit]) => ({ path, ...edit }));
1260
+ return entries.map(({ path, value, options }) => ({
1261
+ path: resolveEditPath(path, { ...defaultEditOptions, ...options }),
1262
+ value
1263
+ }));
1264
+ }
1265
+ function setPath(target, path, value) {
1266
+ const [head, ...rest] = path;
1267
+ if (head === void 0) return value;
1268
+ const container = Array.isArray(target) ? [...target] : { ...target ?? {} };
1269
+ container[head] = setPath(container[head], rest, value);
1270
+ return container;
1271
+ }
1272
+ function modifyConfig({
1273
+ config,
1274
+ edits,
1275
+ defaultEditOptions
1276
+ }) {
1277
+ return checkResult(() => {
1278
+ const format = config.format ?? (config.filepath ? getConfigFormat(config.filepath) : void 0) ?? "json";
1279
+ const language = configLanguages[format];
1280
+ if (language.surgical) {
1281
+ const { data, error } = modifyJSON({
1282
+ json: config.filepath ? { filepath: config.filepath } : config.text !== void 0 ? { text: config.text } : { data: config.data },
1283
+ edits,
1284
+ defaultEditOptions
1285
+ });
1286
+ if (error) throw error;
1287
+ return data;
1288
+ }
1289
+ const current = resolveConfigSource(config, "data");
1290
+ const updated = toEditList(edits, defaultEditOptions).reduce(
1291
+ (result, edit) => setPath(result, edit.path, edit.value),
1292
+ current
1293
+ );
1294
+ return resolveConfigSource({ text: language.stringify(updated), format });
1295
+ });
1296
+ }
1297
+ function modifyConfigFile(filepath, edits, options) {
1298
+ return checkResult(() => {
1299
+ const { autoCommit = true, defaultEditOptions, format } = options || {};
1300
+ const { data: config, error } = modifyConfig({
1301
+ config: format ? { filepath, format } : { filepath },
1302
+ edits,
1303
+ defaultEditOptions
1304
+ });
1305
+ if (error) {
1306
+ throw error;
1307
+ }
1308
+ const commit = () => writeFileSync(filepath, config.text);
1309
+ if (autoCommit) {
1310
+ commit();
1311
+ return config;
1312
+ }
1313
+ return {
1314
+ config,
1315
+ commit
1316
+ };
1317
+ });
1318
+ }
1319
+
1164
1320
  function isStorageValue(input) {
1165
1321
  return input === null || typeof input === "string" || typeof input === "number" || typeof input === "boolean" || typeof input === "object";
1166
1322
  }
@@ -1247,4 +1403,4 @@ function defineFileSystemStorage(options) {
1247
1403
  return fileStorage;
1248
1404
  }
1249
1405
 
1250
- export { _dirname, _filename, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
1406
+ export { _dirname, _filename, configLanguages, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getConfigFormat, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isConfigFormat, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyConfig, modifyConfigFile, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, readFile, readFileSafely, resolveConfigSource, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "package-management",
6
6
  "package-manager"
7
7
  ],
8
- "version": "0.1.0",
8
+ "version": "0.2.0",
9
9
  "license": "MIT",
10
10
  "type": "module",
11
11
  "engines": {
@@ -48,6 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "async-cache-fn": "^0.0.3",
51
+ "confbox": "^0.2.4",
51
52
  "execa": "^10.0.1",
52
53
  "find-up": "^8.0.0",
53
54
  "globby": "^16.2.4",