package-management 0.0.1

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 ADDED
@@ -0,0 +1,314 @@
1
+ 'use strict';
2
+
3
+ const installPkg = require('@antfu/install-pkg');
4
+ require('node:fs');
5
+ const execa = require('execa');
6
+ const localPkg = require('local-pkg');
7
+ const asyncCacheFn = require('async-cache-fn');
8
+ const findUp = require('find-up');
9
+ const promises = require('node:fs/promises');
10
+
11
+ async function resolveModule(m) {
12
+ const resolved = await m;
13
+ return resolved.default || resolved;
14
+ }
15
+
16
+ function importer(imports, options) {
17
+ const { install = true } = options ?? {};
18
+ return Promise.all(
19
+ imports.map(async (option) => {
20
+ if (install && "name" in option) {
21
+ await ensurePackage(option.name);
22
+ }
23
+ const importStatement = getImportStatement(option);
24
+ return resolveModule(importStatement);
25
+ })
26
+ );
27
+ }
28
+ function getImportStatement(option) {
29
+ if (typeof option === "function") {
30
+ return option();
31
+ }
32
+ if (typeof option === "object" && "import" in option && "name" in option) {
33
+ return option.import();
34
+ }
35
+ return option;
36
+ }
37
+
38
+ async function importMap(importMap2, options) {
39
+ const keys = Object.keys(importMap2);
40
+ const imported = await importer(
41
+ keys.map((key) => importMap2[key]),
42
+ options
43
+ );
44
+ return Object.fromEntries(
45
+ keys.map((key, index) => [key, imported[index]])
46
+ );
47
+ }
48
+
49
+ const toArray = (data) => Array.isArray(data) ? data : [data];
50
+ const entriesOf = (o) => Object.entries(o);
51
+ const fromEntries = (entries) => Object.fromEntries(entries);
52
+ const notFalsy = (value) => [false, null, void 0].every((v) => v !== value);
53
+ const select = (obj, selection, mode) => {
54
+ if (!selection)
55
+ return obj;
56
+ const filtered = entriesOf(obj).filter(([key]) => {
57
+ return mode === "omit" ? !selection[key] : selection[key];
58
+ });
59
+ return fromEntries(filtered);
60
+ };
61
+ const invariant = (predicate, message) => {
62
+ if (!predicate) {
63
+ throw new Error(message);
64
+ }
65
+ };
66
+
67
+ function isPackageDependency(packageName) {
68
+ return toArray(packageName).every((name) => localPkg.isPackageExists(name));
69
+ }
70
+
71
+ async function ensurePackage(name, options) {
72
+ if (isPackageDependency(name))
73
+ return;
74
+ await installPkg.installPackage(name, options);
75
+ }
76
+
77
+ function definePackageManager(config) {
78
+ const { command, args: agentArgs, options: agentOptions } = config;
79
+ const findLockfilePath = asyncCacheFn.asyncCacheFn(async (options) => {
80
+ const { cwd } = options ?? {};
81
+ const lockfiles = toArray(config.meta.lockfile);
82
+ return await findUp.findUp(lockfiles, { cwd });
83
+ });
84
+ return {
85
+ id: config.id,
86
+ config,
87
+ findLockfilePath,
88
+ hasLockfile: asyncCacheFn.asyncCacheFn(async (...args) => {
89
+ const lockfilePath = await findLockfilePath.noCache(...args);
90
+ return Boolean(lockfilePath);
91
+ }),
92
+ readLockfile: asyncCacheFn.asyncCacheFn(async (...args) => {
93
+ const lockfilePath = await findLockfilePath.noCache(...args);
94
+ if (!lockfilePath)
95
+ return void 0;
96
+ return promises.readFile(lockfilePath, "utf8");
97
+ }),
98
+ globalVersion: asyncCacheFn.asyncCacheFn(async (...args) => {
99
+ const [options] = args;
100
+ try {
101
+ const { stdout } = await $$({
102
+ command,
103
+ args: [agentOptions.version],
104
+ ...options
105
+ });
106
+ return `${stdout}`;
107
+ } catch (e) {
108
+ return void 0;
109
+ }
110
+ }),
111
+ installPackage: async (packageName, options) => {
112
+ const install = agentArgs.install;
113
+ const { isDevDependency, preferOffline } = select(
114
+ install.options,
115
+ options ?? {},
116
+ "pick"
117
+ );
118
+ const packageNames = toArray(packageName);
119
+ try {
120
+ await $$({
121
+ command,
122
+ args: [
123
+ install.command,
124
+ isDevDependency,
125
+ preferOffline,
126
+ ...packageNames
127
+ ],
128
+ ...options
129
+ });
130
+ } catch (e) {
131
+ throw new Error(`Failed to install: ${packageNames.join(", ")}`);
132
+ }
133
+ },
134
+ uninstallPackage: async (packageName, options) => {
135
+ const uninstall = agentArgs.uninstall;
136
+ if (!isPackageDependency(packageName))
137
+ return;
138
+ await $$({
139
+ command,
140
+ args: [uninstall.command, ...toArray(packageName)],
141
+ ...options
142
+ }).catch((e) => {
143
+ });
144
+ }
145
+ };
146
+ }
147
+ async function $$(options) {
148
+ const { command, args = [], silent = true, cwd, shellOptions } = options;
149
+ return execa.execa(command, args.filter(notFalsy), {
150
+ cwd,
151
+ ...shellOptions,
152
+ stdio: silent ? "ignore" : "inherit"
153
+ });
154
+ }
155
+
156
+ const bun = definePackageManager({
157
+ id: "bun",
158
+ name: "Bun",
159
+ command: "bun",
160
+ runner: "bunx",
161
+ meta: {
162
+ lockfile: "bun.lockb"
163
+ },
164
+ args: {
165
+ install: {
166
+ command: "install",
167
+ options: {
168
+ isDevDependency: "-D",
169
+ preferOffline: "--prefer-offline"
170
+ }
171
+ },
172
+ uninstall: {
173
+ command: "uninstall"
174
+ }
175
+ },
176
+ options: {
177
+ version: "--version"
178
+ }
179
+ });
180
+
181
+ const npm = definePackageManager({
182
+ id: "npm",
183
+ name: "NPM",
184
+ command: "npm",
185
+ runner: "npx",
186
+ meta: {
187
+ lockfile: ["package-lock.json", "npm-shrinkwrap.json"]
188
+ },
189
+ args: {
190
+ install: {
191
+ command: "install",
192
+ options: {
193
+ isDevDependency: "-D",
194
+ preferOffline: "--prefer-offline"
195
+ }
196
+ },
197
+ uninstall: {
198
+ command: "uninstall"
199
+ }
200
+ },
201
+ options: {
202
+ version: "--version"
203
+ }
204
+ });
205
+
206
+ const pnpm = definePackageManager({
207
+ id: "pnpm",
208
+ name: "PNPM",
209
+ command: "pnpm",
210
+ runner: "pnpx",
211
+ meta: {
212
+ lockfile: "pnpm-lock.yaml"
213
+ },
214
+ args: {
215
+ install: {
216
+ command: "install",
217
+ options: {
218
+ isDevDependency: "-D",
219
+ preferOffline: "--prefer-offline"
220
+ }
221
+ },
222
+ uninstall: {
223
+ command: "uninstall"
224
+ }
225
+ },
226
+ options: {
227
+ version: "--version"
228
+ }
229
+ });
230
+
231
+ const yarn = definePackageManager({
232
+ id: "yarn",
233
+ name: "Yarn",
234
+ command: "yarn",
235
+ runner: "yarn dlx",
236
+ meta: {
237
+ lockfile: "yarn.lock"
238
+ },
239
+ args: {
240
+ install: {
241
+ command: "add",
242
+ options: {
243
+ isDevDependency: "-D",
244
+ preferOffline: "--prefer-offline"
245
+ }
246
+ },
247
+ uninstall: {
248
+ command: "remove"
249
+ }
250
+ },
251
+ options: {
252
+ version: "--version"
253
+ }
254
+ });
255
+
256
+ const packageManagers = fromEntries(
257
+ [pnpm, yarn, bun, npm].map((e) => [e.id, e])
258
+ );
259
+
260
+ async function findPackageManager(options) {
261
+ const { ...rest } = options ?? {};
262
+ const packageManager = await findPackageManagerSafely(rest);
263
+ invariant(packageManager, "No package manager found");
264
+ return packageManager;
265
+ }
266
+ async function findPackageManagerSafely(options) {
267
+ const lockfilePm = (await detectLockfilePackageManagers(options))[0];
268
+ if (lockfilePm) {
269
+ return lockfilePm;
270
+ }
271
+ const globalPm = (await detectGlobalPackageManagers(options))[0];
272
+ return globalPm;
273
+ }
274
+ async function detectPackageManagers(options) {
275
+ return [
276
+ ...await detectLockfilePackageManagers(options),
277
+ ...await detectGlobalPackageManagers(options)
278
+ ];
279
+ }
280
+ async function detectLockfilePackageManagers(options) {
281
+ ({ cwd: options?.cwd });
282
+ return filterPackageManagers((e) => e.hasLockfile(options), options);
283
+ }
284
+ async function detectGlobalPackageManagers(options) {
285
+ return filterPackageManagers(async (e) => e.globalVersion(options), options);
286
+ }
287
+ async function filterPackageManagers(filterFn, options) {
288
+ const allowedPackageManagers = Object.entries(packageManagers).filter(
289
+ ([key]) => {
290
+ if (!options?.allowed)
291
+ return true;
292
+ return key in options.allowed;
293
+ }
294
+ );
295
+ return (await Promise.all(
296
+ allowedPackageManagers.map(async ([key, pm]) => {
297
+ const valid = await filterFn(pm);
298
+ return valid ? pm : void 0;
299
+ })
300
+ )).filter(notFalsy);
301
+ }
302
+
303
+ exports.detectGlobalPackageManagers = detectGlobalPackageManagers;
304
+ exports.detectLockfilePackageManagers = detectLockfilePackageManagers;
305
+ exports.detectPackageManagers = detectPackageManagers;
306
+ exports.ensurePackage = ensurePackage;
307
+ exports.filterPackageManagers = filterPackageManagers;
308
+ exports.findPackageManager = findPackageManager;
309
+ exports.findPackageManagerSafely = findPackageManagerSafely;
310
+ exports.importMap = importMap;
311
+ exports.importer = importer;
312
+ exports.isPackageDependency = isPackageDependency;
313
+ exports.packageManagers = packageManagers;
314
+ exports.resolveModule = resolveModule;
@@ -0,0 +1,227 @@
1
+ import { InstallPackageOptions } from '@antfu/install-pkg';
2
+ import { AsyncCacheFn as AsyncCacheFn$1 } from 'async-cache-fn';
3
+ import { Options } from 'execa';
4
+
5
+ interface ImportPackageOption<T = any> {
6
+ /**
7
+ * The name of the package to import. This should match the name used in the import statement.
8
+ */
9
+ name: string;
10
+ /**
11
+ * When enabled, the package will be installed if it is not found
12
+ * @default true
13
+ */
14
+ install?: boolean;
15
+ /**
16
+ * The import statement for the package.
17
+ */
18
+ import: () => ImportStatement<T>;
19
+ /**
20
+ * When enabled, the package will be installed as a dev dependency
21
+ */
22
+ isDevDependency?: boolean;
23
+ /**
24
+ * Additional installation options
25
+ */
26
+ installOptions?: Omit<InstallPackageOptions, "dev">;
27
+ }
28
+ type ImportMap = Record<string, ImportOption>;
29
+ type ImportList = ImportOption[];
30
+ type ImportOption<T = any> = ImportStatement<T> | (() => ImportStatement<T>) | ImportPackageOption<T>;
31
+ type ImportStatement<T = any> = Promise<T>;
32
+ type ResolvedImportOption<T extends ImportOption> = ResolvedPromise<ExtractImportStatement<T>>;
33
+ type ResolvedImportMap<$Imports extends ImportMap> = {
34
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
35
+ };
36
+ type ResolvedImportMapPromise<T extends ImportMap> = Promise<ResolvedImportMap<T>>;
37
+ type ResolvedImportList<$Imports extends ImportList> = {
38
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
39
+ };
40
+ type ResolvedImportListPromise<T extends ImportList> = Promise<ResolvedImportList<T>>;
41
+ type ExtractImportStatement<T extends ImportOption> = ExtractImport<T> extends () => infer I ? I : T;
42
+ type ExtractImport<T extends ImportOption> = T extends {
43
+ import: infer I;
44
+ } ? I : T;
45
+
46
+ type __<T> = {
47
+ [K in keyof T]: T[K];
48
+ } & {};
49
+ type Awaitable<T> = T | Promise<T>;
50
+ type ResolvedPromise<T> = T extends Promise<infer U> ? U : never;
51
+ type KeyOf<T, K> = K extends keyof T ? K : never;
52
+ type KeyOfValue<T, K> = T[KeyOf<T, K>];
53
+ type SelectionMap<T> = __<{
54
+ [K in keyof T]?: boolean;
55
+ }>;
56
+ type PickByValue<T, V> = {
57
+ [K in keyof T as T[K] extends V ? K : never]: T[K];
58
+ };
59
+ type Entry<key extends PropertyKey = PropertyKey, value = unknown> = readonly [key: key, value: value];
60
+ type EntryOf<O> = {
61
+ [k in keyof O]-?: [k, O[k] & ({} | null)];
62
+ }[O extends readonly unknown[] ? keyof O & number : keyof O] & unknown;
63
+ type FromEntries<entries extends readonly Entry[]> = {
64
+ [entry in entries[number] as entry[0]]: entry[1];
65
+ };
66
+ type UnionizedSelectionMap<T, TSelection extends SelectionMap<T>, V> = __<V & {
67
+ [K in keyof T as IsExactBoolean<TSelection[K]> extends true ? K : never]?: T[K];
68
+ }>;
69
+ type Select<T, TSelection extends SelectionMap<T>, TMode extends "pick" | "omit"> = __<UnionizedSelectionMap<T, TSelection, TMode extends "pick" ? Pick<T, KeyOf<T, keyof PickByValue<TSelection, true>>> : Omit<T, KeyOf<T, keyof PickByValue<TSelection, true>>>>>;
70
+ type IsExactBoolean<T> = true extends T ? false extends T ? true : false : false;
71
+ type IsUnion<T, U = T> = T extends U ? [U] extends [T] ? false : true : never;
72
+ type MergeObject<T extends object, O extends object | unknown = unknown> = __<T & (O extends object ? O : Record<never, never>)>;
73
+ type AsyncCacheFn<TReturn = unknown, TOption extends object | undefined = undefined, C extends "required" | "optional" = "optional"> = AsyncCacheFn$1<TReturn, C extends "optional" ? [TOption | undefined] | [] : [TOption]>;
74
+
75
+ interface ImporterOptions {
76
+ /**
77
+ * When enabled, the default behavior is to install packages that are not found.
78
+ * The name of the package is inferred from the key of the import map.
79
+ * @default true
80
+ */
81
+ install?: boolean;
82
+ }
83
+ /**
84
+ * Dynamically imports modules and returns their exports in a tuple.
85
+ * This function ensures type safety and maintains the order of imports.
86
+ *
87
+ * @param imports An array of dynamic import promises.
88
+ * @returns A promise that resolves to a tuple containing the default exports of the imported modules.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * // Usage example with dynamic imports
93
+ * const [package1, package2] = await importer([
94
+ * import('@antfu/eslint-config'),
95
+ * import('@antfu/install-pkg')
96
+ * ]);
97
+ *
98
+ * // package1 and package2 will be the default exports of the respective modules
99
+ * ```
100
+ *
101
+ * @typeparam Imports An array type representing the dynamic imports.
102
+ */
103
+ declare function importer<T extends ImportList>(imports: [...T], options?: ImporterOptions): Promise<ResolvedImportList<T>>;
104
+
105
+ /**
106
+ * Asynchronously imports modules from a record of dynamic import promises.
107
+ * Returns a promise that resolves to a record with the same keys, each mapped to the resolved import.
108
+ * This function maintains the key-value mapping and ensures type safety.
109
+ *
110
+ * @param importMap A record where each key is associated with a dynamic import promise.
111
+ * @returns A promise that resolves to a record containing the default exports of the imported modules,
112
+ * maintaining the original key structure.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Usage example with a record of dynamic imports
117
+ *
118
+ * const { package1, package2 } = await importMap({
119
+ * package1: import('package-1'),
120
+ * package2: import('package-2')
121
+ * });
122
+ *
123
+ * // importedModules.config and importedModules.pkg will be the default exports of the respective modules
124
+ * ```
125
+ *
126
+ */
127
+ declare function importMap<T extends ImportMap>(importMap: T, options?: ImporterOptions): Promise<ResolvedImportMap<T>>;
128
+
129
+ declare function ensurePackage(name: string | string[], options?: InstallPackageOptions): Promise<void>;
130
+
131
+ /**
132
+ * Asynchronously handles the resolution of a module, extracting its default export if available.
133
+ * If the module does not have a default export, it returns the module itself.
134
+ *
135
+ * @param m A promise representing a module import (either ES Module or CommonJS).
136
+ * @returns A promise that resolves to either the default export of the module or the module itself.
137
+ *
138
+ * @typeparam T The type of the module being imported.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // Usage example with dynamic import
143
+ * const module = await interopDefault(import('some-module'));
144
+ *
145
+ * // 'module' will be either the default export of 'some-module' or the module itself if no default export exists
146
+ * ```
147
+ */
148
+ declare function resolveModule<T>(m: Awaitable<T>): Promise<T extends {
149
+ default: infer U;
150
+ } ? U : T>;
151
+
152
+ declare function isPackageDependency(packageName: string | string[]): boolean;
153
+
154
+ interface PackageManagerConfig<ID extends string = string> {
155
+ id: ID;
156
+ command: string;
157
+ name: string;
158
+ meta: {
159
+ lockfile: string | string[];
160
+ };
161
+ runner: string;
162
+ args: {
163
+ install: {
164
+ command: string;
165
+ options: {
166
+ preferOffline: string;
167
+ isDevDependency: string;
168
+ };
169
+ };
170
+ uninstall: {
171
+ command: string;
172
+ };
173
+ };
174
+ options: {
175
+ version: string;
176
+ };
177
+ }
178
+ type PackageManagerCommands = PackageManagerConfig["args"];
179
+ type PackageManagerCommandName = keyof PackageManagerCommands;
180
+ type PackageManagerCommandSpec<K> = KeyOfValue<PackageManagerCommands, K> extends {
181
+ options: infer O;
182
+ } ? SelectionMap<O> : Record<never, never>;
183
+
184
+ type ScriptOptions<K extends PackageManagerCommandName | undefined = undefined> = __<{
185
+ cwd?: string;
186
+ silent?: boolean;
187
+ shellOptions?: Options;
188
+ } & PackageManagerCommandSpec<K>>;
189
+ interface PackageManager<ID extends string = PackageManagerId> {
190
+ id: ID;
191
+ config: PackageManagerConfig;
192
+ findLockfilePath: AsyncCacheFn<string | undefined, {
193
+ cwd?: string;
194
+ }>;
195
+ hasLockfile: AsyncCacheFn<boolean, {
196
+ cwd?: string;
197
+ }>;
198
+ readLockfile: AsyncCacheFn<string | undefined, {
199
+ cwd?: string;
200
+ }>;
201
+ globalVersion: AsyncCacheFn<string | undefined, ScriptOptions>;
202
+ uninstallPackage: (packageNames: string | string[], options?: ScriptOptions<"uninstall">) => Promise<void>;
203
+ installPackage: (packageNames: string | string[], options?: ScriptOptions<"install">) => Promise<void>;
204
+ }
205
+
206
+ type PackageManagerId = keyof typeof packageManagers;
207
+ declare const packageManagers: {
208
+ pnpm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
209
+ yarn: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
210
+ bun: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
211
+ npm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
212
+ };
213
+
214
+ interface DetectPackageManagerOptions {
215
+ allowed?: SelectionMap<typeof packageManagers>;
216
+ cwd?: string;
217
+ }
218
+ declare function findPackageManager(options?: DetectPackageManagerOptions): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">>;
219
+ declare function findPackageManagerSafely<TAssert extends boolean = true>(options?: DetectPackageManagerOptions & {
220
+ assert?: TAssert;
221
+ }): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm"> | undefined>;
222
+ declare function detectPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
223
+ declare function detectLockfilePackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
224
+ declare function detectGlobalPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
225
+ declare function filterPackageManagers(filterFn: (packageManager: PackageManager) => Promise<unknown>, options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
226
+
227
+ export { type AsyncCacheFn, type Awaitable, type Entry, type EntryOf, type FromEntries, type ImportList, type ImportMap, type ImportOption, type ImportPackageOption, type ImportStatement, type ImporterOptions, type IsExactBoolean, type IsUnion, type KeyOf, type KeyOfValue, type MergeObject, type PackageManagerCommandName, type PackageManagerCommandSpec, type PackageManagerCommands, type PackageManagerConfig, type PackageManagerId, type PickByValue, type ResolvedImportList, type ResolvedImportListPromise, type ResolvedImportMap, type ResolvedImportMapPromise, type ResolvedImportOption, type ResolvedPromise, type Select, type SelectionMap, type __, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, ensurePackage, filterPackageManagers, findPackageManager, findPackageManagerSafely, importMap, importer, isPackageDependency, packageManagers, resolveModule };
@@ -0,0 +1,227 @@
1
+ import { InstallPackageOptions } from '@antfu/install-pkg';
2
+ import { AsyncCacheFn as AsyncCacheFn$1 } from 'async-cache-fn';
3
+ import { Options } from 'execa';
4
+
5
+ interface ImportPackageOption<T = any> {
6
+ /**
7
+ * The name of the package to import. This should match the name used in the import statement.
8
+ */
9
+ name: string;
10
+ /**
11
+ * When enabled, the package will be installed if it is not found
12
+ * @default true
13
+ */
14
+ install?: boolean;
15
+ /**
16
+ * The import statement for the package.
17
+ */
18
+ import: () => ImportStatement<T>;
19
+ /**
20
+ * When enabled, the package will be installed as a dev dependency
21
+ */
22
+ isDevDependency?: boolean;
23
+ /**
24
+ * Additional installation options
25
+ */
26
+ installOptions?: Omit<InstallPackageOptions, "dev">;
27
+ }
28
+ type ImportMap = Record<string, ImportOption>;
29
+ type ImportList = ImportOption[];
30
+ type ImportOption<T = any> = ImportStatement<T> | (() => ImportStatement<T>) | ImportPackageOption<T>;
31
+ type ImportStatement<T = any> = Promise<T>;
32
+ type ResolvedImportOption<T extends ImportOption> = ResolvedPromise<ExtractImportStatement<T>>;
33
+ type ResolvedImportMap<$Imports extends ImportMap> = {
34
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
35
+ };
36
+ type ResolvedImportMapPromise<T extends ImportMap> = Promise<ResolvedImportMap<T>>;
37
+ type ResolvedImportList<$Imports extends ImportList> = {
38
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
39
+ };
40
+ type ResolvedImportListPromise<T extends ImportList> = Promise<ResolvedImportList<T>>;
41
+ type ExtractImportStatement<T extends ImportOption> = ExtractImport<T> extends () => infer I ? I : T;
42
+ type ExtractImport<T extends ImportOption> = T extends {
43
+ import: infer I;
44
+ } ? I : T;
45
+
46
+ type __<T> = {
47
+ [K in keyof T]: T[K];
48
+ } & {};
49
+ type Awaitable<T> = T | Promise<T>;
50
+ type ResolvedPromise<T> = T extends Promise<infer U> ? U : never;
51
+ type KeyOf<T, K> = K extends keyof T ? K : never;
52
+ type KeyOfValue<T, K> = T[KeyOf<T, K>];
53
+ type SelectionMap<T> = __<{
54
+ [K in keyof T]?: boolean;
55
+ }>;
56
+ type PickByValue<T, V> = {
57
+ [K in keyof T as T[K] extends V ? K : never]: T[K];
58
+ };
59
+ type Entry<key extends PropertyKey = PropertyKey, value = unknown> = readonly [key: key, value: value];
60
+ type EntryOf<O> = {
61
+ [k in keyof O]-?: [k, O[k] & ({} | null)];
62
+ }[O extends readonly unknown[] ? keyof O & number : keyof O] & unknown;
63
+ type FromEntries<entries extends readonly Entry[]> = {
64
+ [entry in entries[number] as entry[0]]: entry[1];
65
+ };
66
+ type UnionizedSelectionMap<T, TSelection extends SelectionMap<T>, V> = __<V & {
67
+ [K in keyof T as IsExactBoolean<TSelection[K]> extends true ? K : never]?: T[K];
68
+ }>;
69
+ type Select<T, TSelection extends SelectionMap<T>, TMode extends "pick" | "omit"> = __<UnionizedSelectionMap<T, TSelection, TMode extends "pick" ? Pick<T, KeyOf<T, keyof PickByValue<TSelection, true>>> : Omit<T, KeyOf<T, keyof PickByValue<TSelection, true>>>>>;
70
+ type IsExactBoolean<T> = true extends T ? false extends T ? true : false : false;
71
+ type IsUnion<T, U = T> = T extends U ? [U] extends [T] ? false : true : never;
72
+ type MergeObject<T extends object, O extends object | unknown = unknown> = __<T & (O extends object ? O : Record<never, never>)>;
73
+ type AsyncCacheFn<TReturn = unknown, TOption extends object | undefined = undefined, C extends "required" | "optional" = "optional"> = AsyncCacheFn$1<TReturn, C extends "optional" ? [TOption | undefined] | [] : [TOption]>;
74
+
75
+ interface ImporterOptions {
76
+ /**
77
+ * When enabled, the default behavior is to install packages that are not found.
78
+ * The name of the package is inferred from the key of the import map.
79
+ * @default true
80
+ */
81
+ install?: boolean;
82
+ }
83
+ /**
84
+ * Dynamically imports modules and returns their exports in a tuple.
85
+ * This function ensures type safety and maintains the order of imports.
86
+ *
87
+ * @param imports An array of dynamic import promises.
88
+ * @returns A promise that resolves to a tuple containing the default exports of the imported modules.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * // Usage example with dynamic imports
93
+ * const [package1, package2] = await importer([
94
+ * import('@antfu/eslint-config'),
95
+ * import('@antfu/install-pkg')
96
+ * ]);
97
+ *
98
+ * // package1 and package2 will be the default exports of the respective modules
99
+ * ```
100
+ *
101
+ * @typeparam Imports An array type representing the dynamic imports.
102
+ */
103
+ declare function importer<T extends ImportList>(imports: [...T], options?: ImporterOptions): Promise<ResolvedImportList<T>>;
104
+
105
+ /**
106
+ * Asynchronously imports modules from a record of dynamic import promises.
107
+ * Returns a promise that resolves to a record with the same keys, each mapped to the resolved import.
108
+ * This function maintains the key-value mapping and ensures type safety.
109
+ *
110
+ * @param importMap A record where each key is associated with a dynamic import promise.
111
+ * @returns A promise that resolves to a record containing the default exports of the imported modules,
112
+ * maintaining the original key structure.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Usage example with a record of dynamic imports
117
+ *
118
+ * const { package1, package2 } = await importMap({
119
+ * package1: import('package-1'),
120
+ * package2: import('package-2')
121
+ * });
122
+ *
123
+ * // importedModules.config and importedModules.pkg will be the default exports of the respective modules
124
+ * ```
125
+ *
126
+ */
127
+ declare function importMap<T extends ImportMap>(importMap: T, options?: ImporterOptions): Promise<ResolvedImportMap<T>>;
128
+
129
+ declare function ensurePackage(name: string | string[], options?: InstallPackageOptions): Promise<void>;
130
+
131
+ /**
132
+ * Asynchronously handles the resolution of a module, extracting its default export if available.
133
+ * If the module does not have a default export, it returns the module itself.
134
+ *
135
+ * @param m A promise representing a module import (either ES Module or CommonJS).
136
+ * @returns A promise that resolves to either the default export of the module or the module itself.
137
+ *
138
+ * @typeparam T The type of the module being imported.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // Usage example with dynamic import
143
+ * const module = await interopDefault(import('some-module'));
144
+ *
145
+ * // 'module' will be either the default export of 'some-module' or the module itself if no default export exists
146
+ * ```
147
+ */
148
+ declare function resolveModule<T>(m: Awaitable<T>): Promise<T extends {
149
+ default: infer U;
150
+ } ? U : T>;
151
+
152
+ declare function isPackageDependency(packageName: string | string[]): boolean;
153
+
154
+ interface PackageManagerConfig<ID extends string = string> {
155
+ id: ID;
156
+ command: string;
157
+ name: string;
158
+ meta: {
159
+ lockfile: string | string[];
160
+ };
161
+ runner: string;
162
+ args: {
163
+ install: {
164
+ command: string;
165
+ options: {
166
+ preferOffline: string;
167
+ isDevDependency: string;
168
+ };
169
+ };
170
+ uninstall: {
171
+ command: string;
172
+ };
173
+ };
174
+ options: {
175
+ version: string;
176
+ };
177
+ }
178
+ type PackageManagerCommands = PackageManagerConfig["args"];
179
+ type PackageManagerCommandName = keyof PackageManagerCommands;
180
+ type PackageManagerCommandSpec<K> = KeyOfValue<PackageManagerCommands, K> extends {
181
+ options: infer O;
182
+ } ? SelectionMap<O> : Record<never, never>;
183
+
184
+ type ScriptOptions<K extends PackageManagerCommandName | undefined = undefined> = __<{
185
+ cwd?: string;
186
+ silent?: boolean;
187
+ shellOptions?: Options;
188
+ } & PackageManagerCommandSpec<K>>;
189
+ interface PackageManager<ID extends string = PackageManagerId> {
190
+ id: ID;
191
+ config: PackageManagerConfig;
192
+ findLockfilePath: AsyncCacheFn<string | undefined, {
193
+ cwd?: string;
194
+ }>;
195
+ hasLockfile: AsyncCacheFn<boolean, {
196
+ cwd?: string;
197
+ }>;
198
+ readLockfile: AsyncCacheFn<string | undefined, {
199
+ cwd?: string;
200
+ }>;
201
+ globalVersion: AsyncCacheFn<string | undefined, ScriptOptions>;
202
+ uninstallPackage: (packageNames: string | string[], options?: ScriptOptions<"uninstall">) => Promise<void>;
203
+ installPackage: (packageNames: string | string[], options?: ScriptOptions<"install">) => Promise<void>;
204
+ }
205
+
206
+ type PackageManagerId = keyof typeof packageManagers;
207
+ declare const packageManagers: {
208
+ pnpm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
209
+ yarn: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
210
+ bun: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
211
+ npm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
212
+ };
213
+
214
+ interface DetectPackageManagerOptions {
215
+ allowed?: SelectionMap<typeof packageManagers>;
216
+ cwd?: string;
217
+ }
218
+ declare function findPackageManager(options?: DetectPackageManagerOptions): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">>;
219
+ declare function findPackageManagerSafely<TAssert extends boolean = true>(options?: DetectPackageManagerOptions & {
220
+ assert?: TAssert;
221
+ }): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm"> | undefined>;
222
+ declare function detectPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
223
+ declare function detectLockfilePackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
224
+ declare function detectGlobalPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
225
+ declare function filterPackageManagers(filterFn: (packageManager: PackageManager) => Promise<unknown>, options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
226
+
227
+ export { type AsyncCacheFn, type Awaitable, type Entry, type EntryOf, type FromEntries, type ImportList, type ImportMap, type ImportOption, type ImportPackageOption, type ImportStatement, type ImporterOptions, type IsExactBoolean, type IsUnion, type KeyOf, type KeyOfValue, type MergeObject, type PackageManagerCommandName, type PackageManagerCommandSpec, type PackageManagerCommands, type PackageManagerConfig, type PackageManagerId, type PickByValue, type ResolvedImportList, type ResolvedImportListPromise, type ResolvedImportMap, type ResolvedImportMapPromise, type ResolvedImportOption, type ResolvedPromise, type Select, type SelectionMap, type __, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, ensurePackage, filterPackageManagers, findPackageManager, findPackageManagerSafely, importMap, importer, isPackageDependency, packageManagers, resolveModule };
@@ -0,0 +1,227 @@
1
+ import { InstallPackageOptions } from '@antfu/install-pkg';
2
+ import { AsyncCacheFn as AsyncCacheFn$1 } from 'async-cache-fn';
3
+ import { Options } from 'execa';
4
+
5
+ interface ImportPackageOption<T = any> {
6
+ /**
7
+ * The name of the package to import. This should match the name used in the import statement.
8
+ */
9
+ name: string;
10
+ /**
11
+ * When enabled, the package will be installed if it is not found
12
+ * @default true
13
+ */
14
+ install?: boolean;
15
+ /**
16
+ * The import statement for the package.
17
+ */
18
+ import: () => ImportStatement<T>;
19
+ /**
20
+ * When enabled, the package will be installed as a dev dependency
21
+ */
22
+ isDevDependency?: boolean;
23
+ /**
24
+ * Additional installation options
25
+ */
26
+ installOptions?: Omit<InstallPackageOptions, "dev">;
27
+ }
28
+ type ImportMap = Record<string, ImportOption>;
29
+ type ImportList = ImportOption[];
30
+ type ImportOption<T = any> = ImportStatement<T> | (() => ImportStatement<T>) | ImportPackageOption<T>;
31
+ type ImportStatement<T = any> = Promise<T>;
32
+ type ResolvedImportOption<T extends ImportOption> = ResolvedPromise<ExtractImportStatement<T>>;
33
+ type ResolvedImportMap<$Imports extends ImportMap> = {
34
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
35
+ };
36
+ type ResolvedImportMapPromise<T extends ImportMap> = Promise<ResolvedImportMap<T>>;
37
+ type ResolvedImportList<$Imports extends ImportList> = {
38
+ [P in keyof $Imports]: ResolvedImportOption<$Imports[P]>;
39
+ };
40
+ type ResolvedImportListPromise<T extends ImportList> = Promise<ResolvedImportList<T>>;
41
+ type ExtractImportStatement<T extends ImportOption> = ExtractImport<T> extends () => infer I ? I : T;
42
+ type ExtractImport<T extends ImportOption> = T extends {
43
+ import: infer I;
44
+ } ? I : T;
45
+
46
+ type __<T> = {
47
+ [K in keyof T]: T[K];
48
+ } & {};
49
+ type Awaitable<T> = T | Promise<T>;
50
+ type ResolvedPromise<T> = T extends Promise<infer U> ? U : never;
51
+ type KeyOf<T, K> = K extends keyof T ? K : never;
52
+ type KeyOfValue<T, K> = T[KeyOf<T, K>];
53
+ type SelectionMap<T> = __<{
54
+ [K in keyof T]?: boolean;
55
+ }>;
56
+ type PickByValue<T, V> = {
57
+ [K in keyof T as T[K] extends V ? K : never]: T[K];
58
+ };
59
+ type Entry<key extends PropertyKey = PropertyKey, value = unknown> = readonly [key: key, value: value];
60
+ type EntryOf<O> = {
61
+ [k in keyof O]-?: [k, O[k] & ({} | null)];
62
+ }[O extends readonly unknown[] ? keyof O & number : keyof O] & unknown;
63
+ type FromEntries<entries extends readonly Entry[]> = {
64
+ [entry in entries[number] as entry[0]]: entry[1];
65
+ };
66
+ type UnionizedSelectionMap<T, TSelection extends SelectionMap<T>, V> = __<V & {
67
+ [K in keyof T as IsExactBoolean<TSelection[K]> extends true ? K : never]?: T[K];
68
+ }>;
69
+ type Select<T, TSelection extends SelectionMap<T>, TMode extends "pick" | "omit"> = __<UnionizedSelectionMap<T, TSelection, TMode extends "pick" ? Pick<T, KeyOf<T, keyof PickByValue<TSelection, true>>> : Omit<T, KeyOf<T, keyof PickByValue<TSelection, true>>>>>;
70
+ type IsExactBoolean<T> = true extends T ? false extends T ? true : false : false;
71
+ type IsUnion<T, U = T> = T extends U ? [U] extends [T] ? false : true : never;
72
+ type MergeObject<T extends object, O extends object | unknown = unknown> = __<T & (O extends object ? O : Record<never, never>)>;
73
+ type AsyncCacheFn<TReturn = unknown, TOption extends object | undefined = undefined, C extends "required" | "optional" = "optional"> = AsyncCacheFn$1<TReturn, C extends "optional" ? [TOption | undefined] | [] : [TOption]>;
74
+
75
+ interface ImporterOptions {
76
+ /**
77
+ * When enabled, the default behavior is to install packages that are not found.
78
+ * The name of the package is inferred from the key of the import map.
79
+ * @default true
80
+ */
81
+ install?: boolean;
82
+ }
83
+ /**
84
+ * Dynamically imports modules and returns their exports in a tuple.
85
+ * This function ensures type safety and maintains the order of imports.
86
+ *
87
+ * @param imports An array of dynamic import promises.
88
+ * @returns A promise that resolves to a tuple containing the default exports of the imported modules.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * // Usage example with dynamic imports
93
+ * const [package1, package2] = await importer([
94
+ * import('@antfu/eslint-config'),
95
+ * import('@antfu/install-pkg')
96
+ * ]);
97
+ *
98
+ * // package1 and package2 will be the default exports of the respective modules
99
+ * ```
100
+ *
101
+ * @typeparam Imports An array type representing the dynamic imports.
102
+ */
103
+ declare function importer<T extends ImportList>(imports: [...T], options?: ImporterOptions): Promise<ResolvedImportList<T>>;
104
+
105
+ /**
106
+ * Asynchronously imports modules from a record of dynamic import promises.
107
+ * Returns a promise that resolves to a record with the same keys, each mapped to the resolved import.
108
+ * This function maintains the key-value mapping and ensures type safety.
109
+ *
110
+ * @param importMap A record where each key is associated with a dynamic import promise.
111
+ * @returns A promise that resolves to a record containing the default exports of the imported modules,
112
+ * maintaining the original key structure.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * // Usage example with a record of dynamic imports
117
+ *
118
+ * const { package1, package2 } = await importMap({
119
+ * package1: import('package-1'),
120
+ * package2: import('package-2')
121
+ * });
122
+ *
123
+ * // importedModules.config and importedModules.pkg will be the default exports of the respective modules
124
+ * ```
125
+ *
126
+ */
127
+ declare function importMap<T extends ImportMap>(importMap: T, options?: ImporterOptions): Promise<ResolvedImportMap<T>>;
128
+
129
+ declare function ensurePackage(name: string | string[], options?: InstallPackageOptions): Promise<void>;
130
+
131
+ /**
132
+ * Asynchronously handles the resolution of a module, extracting its default export if available.
133
+ * If the module does not have a default export, it returns the module itself.
134
+ *
135
+ * @param m A promise representing a module import (either ES Module or CommonJS).
136
+ * @returns A promise that resolves to either the default export of the module or the module itself.
137
+ *
138
+ * @typeparam T The type of the module being imported.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // Usage example with dynamic import
143
+ * const module = await interopDefault(import('some-module'));
144
+ *
145
+ * // 'module' will be either the default export of 'some-module' or the module itself if no default export exists
146
+ * ```
147
+ */
148
+ declare function resolveModule<T>(m: Awaitable<T>): Promise<T extends {
149
+ default: infer U;
150
+ } ? U : T>;
151
+
152
+ declare function isPackageDependency(packageName: string | string[]): boolean;
153
+
154
+ interface PackageManagerConfig<ID extends string = string> {
155
+ id: ID;
156
+ command: string;
157
+ name: string;
158
+ meta: {
159
+ lockfile: string | string[];
160
+ };
161
+ runner: string;
162
+ args: {
163
+ install: {
164
+ command: string;
165
+ options: {
166
+ preferOffline: string;
167
+ isDevDependency: string;
168
+ };
169
+ };
170
+ uninstall: {
171
+ command: string;
172
+ };
173
+ };
174
+ options: {
175
+ version: string;
176
+ };
177
+ }
178
+ type PackageManagerCommands = PackageManagerConfig["args"];
179
+ type PackageManagerCommandName = keyof PackageManagerCommands;
180
+ type PackageManagerCommandSpec<K> = KeyOfValue<PackageManagerCommands, K> extends {
181
+ options: infer O;
182
+ } ? SelectionMap<O> : Record<never, never>;
183
+
184
+ type ScriptOptions<K extends PackageManagerCommandName | undefined = undefined> = __<{
185
+ cwd?: string;
186
+ silent?: boolean;
187
+ shellOptions?: Options;
188
+ } & PackageManagerCommandSpec<K>>;
189
+ interface PackageManager<ID extends string = PackageManagerId> {
190
+ id: ID;
191
+ config: PackageManagerConfig;
192
+ findLockfilePath: AsyncCacheFn<string | undefined, {
193
+ cwd?: string;
194
+ }>;
195
+ hasLockfile: AsyncCacheFn<boolean, {
196
+ cwd?: string;
197
+ }>;
198
+ readLockfile: AsyncCacheFn<string | undefined, {
199
+ cwd?: string;
200
+ }>;
201
+ globalVersion: AsyncCacheFn<string | undefined, ScriptOptions>;
202
+ uninstallPackage: (packageNames: string | string[], options?: ScriptOptions<"uninstall">) => Promise<void>;
203
+ installPackage: (packageNames: string | string[], options?: ScriptOptions<"install">) => Promise<void>;
204
+ }
205
+
206
+ type PackageManagerId = keyof typeof packageManagers;
207
+ declare const packageManagers: {
208
+ pnpm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
209
+ yarn: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
210
+ bun: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
211
+ npm: PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">;
212
+ };
213
+
214
+ interface DetectPackageManagerOptions {
215
+ allowed?: SelectionMap<typeof packageManagers>;
216
+ cwd?: string;
217
+ }
218
+ declare function findPackageManager(options?: DetectPackageManagerOptions): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">>;
219
+ declare function findPackageManagerSafely<TAssert extends boolean = true>(options?: DetectPackageManagerOptions & {
220
+ assert?: TAssert;
221
+ }): Promise<PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm"> | undefined>;
222
+ declare function detectPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
223
+ declare function detectLockfilePackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
224
+ declare function detectGlobalPackageManagers(options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
225
+ declare function filterPackageManagers(filterFn: (packageManager: PackageManager) => Promise<unknown>, options?: DetectPackageManagerOptions): Promise<(PackageManager<"pnpm"> | PackageManager<"yarn"> | PackageManager<"bun"> | PackageManager<"npm">)[]>;
226
+
227
+ export { type AsyncCacheFn, type Awaitable, type Entry, type EntryOf, type FromEntries, type ImportList, type ImportMap, type ImportOption, type ImportPackageOption, type ImportStatement, type ImporterOptions, type IsExactBoolean, type IsUnion, type KeyOf, type KeyOfValue, type MergeObject, type PackageManagerCommandName, type PackageManagerCommandSpec, type PackageManagerCommands, type PackageManagerConfig, type PackageManagerId, type PickByValue, type ResolvedImportList, type ResolvedImportListPromise, type ResolvedImportMap, type ResolvedImportMapPromise, type ResolvedImportOption, type ResolvedPromise, type Select, type SelectionMap, type __, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, ensurePackage, filterPackageManagers, findPackageManager, findPackageManagerSafely, importMap, importer, isPackageDependency, packageManagers, resolveModule };
package/dist/index.mjs ADDED
@@ -0,0 +1,301 @@
1
+ import { installPackage } from '@antfu/install-pkg';
2
+ import 'node:fs';
3
+ import { execa } from 'execa';
4
+ import { isPackageExists } from 'local-pkg';
5
+ import { asyncCacheFn } from 'async-cache-fn';
6
+ import { findUp } from 'find-up';
7
+ import { readFile } from 'node:fs/promises';
8
+
9
+ async function resolveModule(m) {
10
+ const resolved = await m;
11
+ return resolved.default || resolved;
12
+ }
13
+
14
+ function importer(imports, options) {
15
+ const { install = true } = options ?? {};
16
+ return Promise.all(
17
+ imports.map(async (option) => {
18
+ if (install && "name" in option) {
19
+ await ensurePackage(option.name);
20
+ }
21
+ const importStatement = getImportStatement(option);
22
+ return resolveModule(importStatement);
23
+ })
24
+ );
25
+ }
26
+ function getImportStatement(option) {
27
+ if (typeof option === "function") {
28
+ return option();
29
+ }
30
+ if (typeof option === "object" && "import" in option && "name" in option) {
31
+ return option.import();
32
+ }
33
+ return option;
34
+ }
35
+
36
+ async function importMap(importMap2, options) {
37
+ const keys = Object.keys(importMap2);
38
+ const imported = await importer(
39
+ keys.map((key) => importMap2[key]),
40
+ options
41
+ );
42
+ return Object.fromEntries(
43
+ keys.map((key, index) => [key, imported[index]])
44
+ );
45
+ }
46
+
47
+ const toArray = (data) => Array.isArray(data) ? data : [data];
48
+ const entriesOf = (o) => Object.entries(o);
49
+ const fromEntries = (entries) => Object.fromEntries(entries);
50
+ const notFalsy = (value) => [false, null, void 0].every((v) => v !== value);
51
+ const select = (obj, selection, mode) => {
52
+ if (!selection)
53
+ return obj;
54
+ const filtered = entriesOf(obj).filter(([key]) => {
55
+ return mode === "omit" ? !selection[key] : selection[key];
56
+ });
57
+ return fromEntries(filtered);
58
+ };
59
+ const invariant = (predicate, message) => {
60
+ if (!predicate) {
61
+ throw new Error(message);
62
+ }
63
+ };
64
+
65
+ function isPackageDependency(packageName) {
66
+ return toArray(packageName).every((name) => isPackageExists(name));
67
+ }
68
+
69
+ async function ensurePackage(name, options) {
70
+ if (isPackageDependency(name))
71
+ return;
72
+ await installPackage(name, options);
73
+ }
74
+
75
+ function definePackageManager(config) {
76
+ const { command, args: agentArgs, options: agentOptions } = config;
77
+ const findLockfilePath = asyncCacheFn(async (options) => {
78
+ const { cwd } = options ?? {};
79
+ const lockfiles = toArray(config.meta.lockfile);
80
+ return await findUp(lockfiles, { cwd });
81
+ });
82
+ return {
83
+ id: config.id,
84
+ config,
85
+ findLockfilePath,
86
+ hasLockfile: asyncCacheFn(async (...args) => {
87
+ const lockfilePath = await findLockfilePath.noCache(...args);
88
+ return Boolean(lockfilePath);
89
+ }),
90
+ readLockfile: asyncCacheFn(async (...args) => {
91
+ const lockfilePath = await findLockfilePath.noCache(...args);
92
+ if (!lockfilePath)
93
+ return void 0;
94
+ return readFile(lockfilePath, "utf8");
95
+ }),
96
+ globalVersion: asyncCacheFn(async (...args) => {
97
+ const [options] = args;
98
+ try {
99
+ const { stdout } = await $$({
100
+ command,
101
+ args: [agentOptions.version],
102
+ ...options
103
+ });
104
+ return `${stdout}`;
105
+ } catch (e) {
106
+ return void 0;
107
+ }
108
+ }),
109
+ installPackage: async (packageName, options) => {
110
+ const install = agentArgs.install;
111
+ const { isDevDependency, preferOffline } = select(
112
+ install.options,
113
+ options ?? {},
114
+ "pick"
115
+ );
116
+ const packageNames = toArray(packageName);
117
+ try {
118
+ await $$({
119
+ command,
120
+ args: [
121
+ install.command,
122
+ isDevDependency,
123
+ preferOffline,
124
+ ...packageNames
125
+ ],
126
+ ...options
127
+ });
128
+ } catch (e) {
129
+ throw new Error(`Failed to install: ${packageNames.join(", ")}`);
130
+ }
131
+ },
132
+ uninstallPackage: async (packageName, options) => {
133
+ const uninstall = agentArgs.uninstall;
134
+ if (!isPackageDependency(packageName))
135
+ return;
136
+ await $$({
137
+ command,
138
+ args: [uninstall.command, ...toArray(packageName)],
139
+ ...options
140
+ }).catch((e) => {
141
+ });
142
+ }
143
+ };
144
+ }
145
+ async function $$(options) {
146
+ const { command, args = [], silent = true, cwd, shellOptions } = options;
147
+ return execa(command, args.filter(notFalsy), {
148
+ cwd,
149
+ ...shellOptions,
150
+ stdio: silent ? "ignore" : "inherit"
151
+ });
152
+ }
153
+
154
+ const bun = definePackageManager({
155
+ id: "bun",
156
+ name: "Bun",
157
+ command: "bun",
158
+ runner: "bunx",
159
+ meta: {
160
+ lockfile: "bun.lockb"
161
+ },
162
+ args: {
163
+ install: {
164
+ command: "install",
165
+ options: {
166
+ isDevDependency: "-D",
167
+ preferOffline: "--prefer-offline"
168
+ }
169
+ },
170
+ uninstall: {
171
+ command: "uninstall"
172
+ }
173
+ },
174
+ options: {
175
+ version: "--version"
176
+ }
177
+ });
178
+
179
+ const npm = definePackageManager({
180
+ id: "npm",
181
+ name: "NPM",
182
+ command: "npm",
183
+ runner: "npx",
184
+ meta: {
185
+ lockfile: ["package-lock.json", "npm-shrinkwrap.json"]
186
+ },
187
+ args: {
188
+ install: {
189
+ command: "install",
190
+ options: {
191
+ isDevDependency: "-D",
192
+ preferOffline: "--prefer-offline"
193
+ }
194
+ },
195
+ uninstall: {
196
+ command: "uninstall"
197
+ }
198
+ },
199
+ options: {
200
+ version: "--version"
201
+ }
202
+ });
203
+
204
+ const pnpm = definePackageManager({
205
+ id: "pnpm",
206
+ name: "PNPM",
207
+ command: "pnpm",
208
+ runner: "pnpx",
209
+ meta: {
210
+ lockfile: "pnpm-lock.yaml"
211
+ },
212
+ args: {
213
+ install: {
214
+ command: "install",
215
+ options: {
216
+ isDevDependency: "-D",
217
+ preferOffline: "--prefer-offline"
218
+ }
219
+ },
220
+ uninstall: {
221
+ command: "uninstall"
222
+ }
223
+ },
224
+ options: {
225
+ version: "--version"
226
+ }
227
+ });
228
+
229
+ const yarn = definePackageManager({
230
+ id: "yarn",
231
+ name: "Yarn",
232
+ command: "yarn",
233
+ runner: "yarn dlx",
234
+ meta: {
235
+ lockfile: "yarn.lock"
236
+ },
237
+ args: {
238
+ install: {
239
+ command: "add",
240
+ options: {
241
+ isDevDependency: "-D",
242
+ preferOffline: "--prefer-offline"
243
+ }
244
+ },
245
+ uninstall: {
246
+ command: "remove"
247
+ }
248
+ },
249
+ options: {
250
+ version: "--version"
251
+ }
252
+ });
253
+
254
+ const packageManagers = fromEntries(
255
+ [pnpm, yarn, bun, npm].map((e) => [e.id, e])
256
+ );
257
+
258
+ async function findPackageManager(options) {
259
+ const { ...rest } = options ?? {};
260
+ const packageManager = await findPackageManagerSafely(rest);
261
+ invariant(packageManager, "No package manager found");
262
+ return packageManager;
263
+ }
264
+ async function findPackageManagerSafely(options) {
265
+ const lockfilePm = (await detectLockfilePackageManagers(options))[0];
266
+ if (lockfilePm) {
267
+ return lockfilePm;
268
+ }
269
+ const globalPm = (await detectGlobalPackageManagers(options))[0];
270
+ return globalPm;
271
+ }
272
+ async function detectPackageManagers(options) {
273
+ return [
274
+ ...await detectLockfilePackageManagers(options),
275
+ ...await detectGlobalPackageManagers(options)
276
+ ];
277
+ }
278
+ async function detectLockfilePackageManagers(options) {
279
+ ({ cwd: options?.cwd });
280
+ return filterPackageManagers((e) => e.hasLockfile(options), options);
281
+ }
282
+ async function detectGlobalPackageManagers(options) {
283
+ return filterPackageManagers(async (e) => e.globalVersion(options), options);
284
+ }
285
+ async function filterPackageManagers(filterFn, options) {
286
+ const allowedPackageManagers = Object.entries(packageManagers).filter(
287
+ ([key]) => {
288
+ if (!options?.allowed)
289
+ return true;
290
+ return key in options.allowed;
291
+ }
292
+ );
293
+ return (await Promise.all(
294
+ allowedPackageManagers.map(async ([key, pm]) => {
295
+ const valid = await filterFn(pm);
296
+ return valid ? pm : void 0;
297
+ })
298
+ )).filter(notFalsy);
299
+ }
300
+
301
+ export { detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, ensurePackage, filterPackageManagers, findPackageManager, findPackageManagerSafely, importMap, importer, isPackageDependency, packageManagers, resolveModule };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "package-management",
3
+ "description": "Simple type-safe utilities for programatically installing and importing packages",
4
+ "version": "0.0.1",
5
+ "private": false,
6
+ "files": [
7
+ "dist/**"
8
+ ],
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "unbuild",
18
+ "build:watch": "tsc --watch --preserveWatchOutput",
19
+ "dev": "unbuild --stub",
20
+ "lint": "eslint",
21
+ "test": "vitest",
22
+ "typecheck": "tsc --noEmit",
23
+ "clean": "rm -rf .tsbuildinfo .turbo coverage dist node_modules",
24
+ "npm-publish": "pnpm run build && npm publish --access public --no-git-checks"
25
+ },
26
+ "devDependencies": {
27
+ "@types/node": "20.5.9",
28
+ "typescript": "^5.3.2",
29
+ "unbuild": "^2.0.0",
30
+ "vite-tsconfig-paths": "^4.3.1",
31
+ "vitest": "^0.34.6"
32
+ },
33
+ "dependencies": {
34
+ "@antfu/install-pkg": "^0.3.1",
35
+ "async-cache-fn": "^0.0.3",
36
+ "execa": "^8.0.1",
37
+ "find-up": "^7.0.0",
38
+ "local-pkg": "^0.5.0"
39
+ }
40
+ }