@pnpm/hooks.pnpmfile 1002.1.3

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2016 Rico Sta. Cruz and other contributors
4
+ Copyright (c) 2016-2026 Zoltan Kochan and other contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # @pnpm/pnpmfile
2
+
3
+ > Reading a .pnpmfile.cjs
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@pnpm/pnpmfile.svg)](https://www.npmjs.com/package/@pnpm/pnpmfile)
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add @pnpm/pnpmfile
11
+ ```
12
+
13
+ ## License
14
+
15
+ MIT
package/lib/Hooks.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { Log } from '@pnpm/core-loggers';
2
+ import type { PreResolutionHook } from '@pnpm/hooks.types';
3
+ import type { LockfileObject } from '@pnpm/lockfile.types';
4
+ import type { ImportIndexedPackageAsync } from '@pnpm/store.controller-types';
5
+ export interface HookContext {
6
+ log: (message: string) => void;
7
+ }
8
+ export interface Hooks {
9
+ readPackage?: (pkg: any, context: HookContext) => any;
10
+ beforePacking?: (pkg: any, dir: string, context: HookContext) => any;
11
+ preResolution?: PreResolutionHook;
12
+ afterAllResolved?: (lockfile: LockfileObject, context: HookContext) => LockfileObject | Promise<LockfileObject>;
13
+ filterLog?: (log: Log) => boolean;
14
+ importPackage?: ImportIndexedPackageAsync;
15
+ updateConfig?: (config: any) => any;
16
+ }
package/lib/Hooks.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Hooks.js.map
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { CookedHooks } from './requireHooks.js';
2
+ export type { HookContext } from './Hooks.js';
3
+ export { requireHooks } from './requireHooks.js';
4
+ export { BadReadPackageHookError } from './requirePnpmfile.js';
5
+ export type Hooks = CookedHooks;
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { requireHooks } from './requireHooks.js';
2
+ export { BadReadPackageHookError } from './requirePnpmfile.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,30 @@
1
+ import type { CustomFetcher, CustomResolver, PreResolutionHookContext } from '@pnpm/hooks.types';
2
+ import type { LockfileObject } from '@pnpm/lockfile.types';
3
+ import type { ImportIndexedPackageAsync } from '@pnpm/store.controller-types';
4
+ import type { BeforePackingHook, ReadPackageHook } from '@pnpm/types';
5
+ import type { Hooks } from './Hooks.js';
6
+ import { type Finders } from './requirePnpmfile.js';
7
+ type Cook<T extends (...args: any[]) => any> = (arg: Parameters<T>[0], ...otherArgs: any[]) => ReturnType<T>;
8
+ export interface CookedHooks {
9
+ readPackage?: ReadPackageHook[];
10
+ beforePacking?: BeforePackingHook[];
11
+ preResolution?: Array<(ctx: PreResolutionHookContext) => Promise<void>>;
12
+ afterAllResolved?: Array<(lockfile: LockfileObject) => LockfileObject | Promise<LockfileObject>>;
13
+ filterLog?: Array<Cook<Required<Hooks>['filterLog']>>;
14
+ updateConfig?: Array<Cook<Required<Hooks>['updateConfig']>>;
15
+ importPackage?: ImportIndexedPackageAsync;
16
+ customResolvers?: CustomResolver[];
17
+ customFetchers?: CustomFetcher[];
18
+ calculatePnpmfileChecksum?: () => Promise<string>;
19
+ }
20
+ export interface RequireHooksResult {
21
+ hooks: CookedHooks;
22
+ finders: Finders;
23
+ resolvedPnpmfilePaths: string[];
24
+ }
25
+ export declare function requireHooks(prefix: string, opts: {
26
+ globalPnpmfile?: string;
27
+ pnpmfiles?: string[];
28
+ tryLoadDefaultPnpmfile?: boolean;
29
+ }): Promise<RequireHooksResult>;
30
+ export {};
@@ -0,0 +1,189 @@
1
+ import { hookLogger } from '@pnpm/core-loggers';
2
+ import { createHashFromMultipleFiles } from '@pnpm/crypto.hash';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import { pathAbsolute } from 'path-absolute';
5
+ import { requirePnpmfile } from './requirePnpmfile.js';
6
+ export async function requireHooks(prefix, opts) {
7
+ const pnpmfiles = [];
8
+ if (opts.globalPnpmfile) {
9
+ pnpmfiles.push({
10
+ path: opts.globalPnpmfile,
11
+ includeInChecksum: false,
12
+ });
13
+ }
14
+ const entries = [];
15
+ const loadedFiles = [];
16
+ if (opts.tryLoadDefaultPnpmfile) {
17
+ // Prefer .pnpmfile.mjs over .pnpmfile.cjs. Only load one.
18
+ const mjsPath = pathAbsolute('.pnpmfile.mjs', prefix);
19
+ const mjsResult = await requirePnpmfile(mjsPath, prefix);
20
+ if (mjsResult != null) {
21
+ loadedFiles.push(mjsPath);
22
+ entries.push({
23
+ file: mjsPath,
24
+ includeInChecksum: true,
25
+ hooks: mjsResult.pnpmfileModule?.hooks,
26
+ finders: mjsResult.pnpmfileModule?.finders,
27
+ resolvers: mjsResult.pnpmfileModule?.resolvers,
28
+ fetchers: mjsResult.pnpmfileModule?.fetchers,
29
+ });
30
+ }
31
+ else {
32
+ pnpmfiles.push({
33
+ path: '.pnpmfile.cjs',
34
+ includeInChecksum: true,
35
+ optional: true,
36
+ });
37
+ }
38
+ }
39
+ if (opts.pnpmfiles) {
40
+ for (const pnpmfile of opts.pnpmfiles) {
41
+ pnpmfiles.push({
42
+ path: pnpmfile,
43
+ includeInChecksum: true,
44
+ });
45
+ }
46
+ }
47
+ await Promise.all(pnpmfiles.map(async ({ path, includeInChecksum, optional }) => {
48
+ const file = pathAbsolute(path, prefix);
49
+ if (!loadedFiles.includes(file)) {
50
+ loadedFiles.push(file);
51
+ const requirePnpmfileResult = await requirePnpmfile(file, prefix);
52
+ if (requirePnpmfileResult != null) {
53
+ entries.push({
54
+ file,
55
+ includeInChecksum,
56
+ hooks: requirePnpmfileResult.pnpmfileModule?.hooks,
57
+ finders: requirePnpmfileResult.pnpmfileModule?.finders,
58
+ resolvers: requirePnpmfileResult.pnpmfileModule?.resolvers,
59
+ fetchers: requirePnpmfileResult.pnpmfileModule?.fetchers,
60
+ });
61
+ }
62
+ else if (!optional) {
63
+ throw new PnpmError('PNPMFILE_NOT_FOUND', `pnpmfile at "${file}" is not found`);
64
+ }
65
+ }
66
+ }));
67
+ const mergedFinders = {};
68
+ const cookedHooks = {
69
+ readPackage: [],
70
+ beforePacking: [],
71
+ preResolution: [],
72
+ afterAllResolved: [],
73
+ filterLog: [],
74
+ updateConfig: [],
75
+ };
76
+ // calculate combined checksum for all included files
77
+ if (entries.some((entry) => entry.hooks != null)) {
78
+ cookedHooks.calculatePnpmfileChecksum = async () => {
79
+ const filesToIncludeInHash = [];
80
+ for (const { includeInChecksum, file } of entries) {
81
+ if (includeInChecksum) {
82
+ filesToIncludeInHash.push(file);
83
+ }
84
+ }
85
+ filesToIncludeInHash.sort();
86
+ return createHashFromMultipleFiles(filesToIncludeInHash);
87
+ };
88
+ }
89
+ let importProvider;
90
+ const finderProviders = {};
91
+ // process hooks in order
92
+ for (const { hooks, file, finders } of entries) {
93
+ if (finders != null) {
94
+ for (const [finderName, finder] of Object.entries(finders)) {
95
+ if (mergedFinders[finderName] != null) {
96
+ const firstDefinedIn = finderProviders[finderName];
97
+ throw new PnpmError('DUPLICATE_FINDER', `Finder "${finderName}" defined in both ${firstDefinedIn} and ${file}`);
98
+ }
99
+ mergedFinders[finderName] = finder;
100
+ finderProviders[finderName] = file;
101
+ }
102
+ }
103
+ const fileHooks = hooks ?? {};
104
+ // readPackage
105
+ if (fileHooks.readPackage) {
106
+ const fn = fileHooks.readPackage;
107
+ const context = createReadPackageHookContext(file, prefix, 'readPackage');
108
+ cookedHooks.readPackage.push((pkg, _dir) => fn(pkg, context));
109
+ }
110
+ // beforePacking
111
+ if (fileHooks.beforePacking) {
112
+ const fn = fileHooks.beforePacking;
113
+ const context = createReadPackageHookContext(file, prefix, 'beforePacking');
114
+ cookedHooks.beforePacking.push((pkg, dir) => fn(pkg, dir, context));
115
+ }
116
+ // afterAllResolved
117
+ if (fileHooks.afterAllResolved) {
118
+ const fn = fileHooks.afterAllResolved;
119
+ const context = createReadPackageHookContext(file, prefix, 'afterAllResolved');
120
+ cookedHooks.afterAllResolved.push((lockfile) => fn(lockfile, context));
121
+ }
122
+ // filterLog
123
+ if (fileHooks.filterLog) {
124
+ cookedHooks.filterLog.push(fileHooks.filterLog);
125
+ }
126
+ // updateConfig
127
+ if (fileHooks.updateConfig) {
128
+ const updateConfig = fileHooks.updateConfig;
129
+ cookedHooks.updateConfig.push((config) => {
130
+ const updated = updateConfig(config);
131
+ if (updated == null) {
132
+ throw new PnpmError('CONFIG_IS_UNDEFINED', 'The updateConfig hook returned undefined');
133
+ }
134
+ return updated;
135
+ });
136
+ }
137
+ // preResolution
138
+ if (fileHooks.preResolution) {
139
+ const preRes = fileHooks.preResolution;
140
+ cookedHooks.preResolution.push((ctx) => preRes(ctx, createPreResolutionHookLogger(prefix)));
141
+ }
142
+ // importPackage: only one allowed
143
+ if (fileHooks.importPackage) {
144
+ if (importProvider) {
145
+ throw new PnpmError('MULTIPLE_IMPORT_PACKAGE', `importPackage hook defined in both ${importProvider} and ${file}`);
146
+ }
147
+ importProvider = file;
148
+ cookedHooks.importPackage = fileHooks.importPackage;
149
+ }
150
+ }
151
+ // Process top-level resolvers and fetchers exports
152
+ for (const { resolvers, fetchers } of entries) {
153
+ // Custom resolvers: merge all
154
+ if (resolvers) {
155
+ cookedHooks.customResolvers = cookedHooks.customResolvers ?? [];
156
+ cookedHooks.customResolvers.push(...resolvers);
157
+ }
158
+ // Custom fetchers: merge all
159
+ if (fetchers) {
160
+ cookedHooks.customFetchers = cookedHooks.customFetchers ?? [];
161
+ cookedHooks.customFetchers.push(...fetchers);
162
+ }
163
+ }
164
+ return {
165
+ hooks: cookedHooks,
166
+ finders: mergedFinders,
167
+ resolvedPnpmfilePaths: entries.map(({ file }) => file),
168
+ };
169
+ }
170
+ function createReadPackageHookContext(calledFrom, prefix, hook) {
171
+ return {
172
+ log: (message) => {
173
+ hookLogger.debug({ from: calledFrom, hook, message, prefix });
174
+ },
175
+ };
176
+ }
177
+ function createPreResolutionHookLogger(prefix) {
178
+ const hook = 'preResolution';
179
+ const from = 'pnpmfile';
180
+ return {
181
+ info: (message) => {
182
+ hookLogger.info({ message, prefix, hook, from }); // eslint-disable-line @typescript-eslint/no-explicit-any
183
+ },
184
+ warn: (message) => {
185
+ hookLogger.warn({ message, prefix, hook, from }); // eslint-disable-line @typescript-eslint/no-explicit-any
186
+ },
187
+ };
188
+ }
189
+ //# sourceMappingURL=requireHooks.js.map
@@ -0,0 +1,18 @@
1
+ import { PnpmError } from '@pnpm/error';
2
+ import type { CustomFetcher, CustomResolver } from '@pnpm/hooks.types';
3
+ import type { Finder } from '@pnpm/types';
4
+ import type { Hooks } from './Hooks.js';
5
+ export declare class BadReadPackageHookError extends PnpmError {
6
+ readonly pnpmfile: string;
7
+ constructor(pnpmfile: string, message: string);
8
+ }
9
+ export type Finders = Record<string, Finder>;
10
+ export interface Pnpmfile {
11
+ hooks?: Hooks;
12
+ finders?: Finders;
13
+ resolvers?: CustomResolver[];
14
+ fetchers?: CustomFetcher[];
15
+ }
16
+ export declare function requirePnpmfile(pnpmFilePath: string, prefix: string): Promise<{
17
+ pnpmfileModule: Pnpmfile | undefined;
18
+ } | undefined>;
@@ -0,0 +1,94 @@
1
+ import assert from 'node:assert';
2
+ import fs from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import util from 'node:util';
7
+ import { PnpmError } from '@pnpm/error';
8
+ import { logger } from '@pnpm/logger';
9
+ import chalk from 'chalk';
10
+ const require = createRequire(import.meta.url);
11
+ export class BadReadPackageHookError extends PnpmError {
12
+ pnpmfile;
13
+ constructor(pnpmfile, message) {
14
+ super('BAD_READ_PACKAGE_HOOK_RESULT', `${message} Hook imported via ${pnpmfile}`);
15
+ this.pnpmfile = pnpmfile;
16
+ }
17
+ }
18
+ class PnpmFileFailError extends PnpmError {
19
+ pnpmfile;
20
+ originalError;
21
+ constructor(pnpmfile, originalError) {
22
+ super('PNPMFILE_FAIL', `Error during pnpmfile execution. pnpmfile: "${pnpmfile}". Error: "${originalError.message}".`);
23
+ this.pnpmfile = pnpmfile;
24
+ this.originalError = originalError;
25
+ }
26
+ }
27
+ export async function requirePnpmfile(pnpmFilePath, prefix) {
28
+ try {
29
+ let pnpmfile;
30
+ // Check if it's an ESM module (ends with .mjs)
31
+ if (pnpmFilePath.endsWith('.mjs')) {
32
+ const url = pathToFileURL(path.resolve(pnpmFilePath)).href;
33
+ pnpmfile = await import(url);
34
+ }
35
+ else {
36
+ // Use require for CommonJS modules
37
+ pnpmfile = require(pnpmFilePath);
38
+ }
39
+ if (typeof pnpmfile === 'undefined') {
40
+ logger.warn({
41
+ message: `Ignoring the pnpmfile at "${pnpmFilePath}". It exports "undefined".`,
42
+ prefix,
43
+ });
44
+ return { pnpmfileModule: undefined };
45
+ }
46
+ if (pnpmfile?.hooks?.readPackage && typeof pnpmfile.hooks.readPackage !== 'function') {
47
+ throw new TypeError('hooks.readPackage should be a function');
48
+ }
49
+ if (pnpmfile?.hooks?.readPackage) {
50
+ const readPackage = pnpmfile.hooks.readPackage; // eslint-disable-line
51
+ pnpmfile.hooks.readPackage = async function (pkg, ...args) {
52
+ pkg.dependencies = pkg.dependencies ?? {};
53
+ pkg.devDependencies = pkg.devDependencies ?? {};
54
+ pkg.optionalDependencies = pkg.optionalDependencies ?? {};
55
+ pkg.peerDependencies = pkg.peerDependencies ?? {};
56
+ const newPkg = await readPackage(pkg, ...args);
57
+ if (!newPkg) {
58
+ throw new BadReadPackageHookError(pnpmFilePath, 'readPackage hook did not return a package manifest object.');
59
+ }
60
+ const dependencies = ['dependencies', 'optionalDependencies', 'peerDependencies'];
61
+ for (const dep of dependencies) {
62
+ if (newPkg[dep] && typeof newPkg[dep] !== 'object') {
63
+ throw new BadReadPackageHookError(pnpmFilePath, `readPackage hook returned package manifest object's property '${dep}' must be an object.`);
64
+ }
65
+ }
66
+ return newPkg;
67
+ };
68
+ if (pnpmfile?.hooks?.beforePacking && typeof pnpmfile.hooks.beforePacking !== 'function') {
69
+ throw new TypeError('hooks.beforePacking should be a function');
70
+ }
71
+ }
72
+ return { pnpmfileModule: pnpmfile };
73
+ }
74
+ catch (err) {
75
+ if (err instanceof SyntaxError) {
76
+ console.error(chalk.red(`A syntax error in the "${pnpmFilePath}"\n`));
77
+ console.error(err);
78
+ process.exit(1);
79
+ }
80
+ assert(util.types.isNativeError(err));
81
+ if (!('code' in err && (err.code === 'MODULE_NOT_FOUND' || err.code === 'ERR_MODULE_NOT_FOUND')) ||
82
+ pnpmFileExistsSync(pnpmFilePath)) {
83
+ throw new PnpmFileFailError(pnpmFilePath, err);
84
+ }
85
+ return undefined;
86
+ }
87
+ }
88
+ function pnpmFileExistsSync(pnpmFilePath) {
89
+ const pnpmFileRealName = pnpmFilePath.endsWith('.cjs') || pnpmFilePath.endsWith('.mjs')
90
+ ? pnpmFilePath
91
+ : `${pnpmFilePath}.cjs`;
92
+ return fs.existsSync(pnpmFileRealName);
93
+ }
94
+ //# sourceMappingURL=requirePnpmfile.js.map
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@pnpm/hooks.pnpmfile",
3
+ "version": "1002.1.3",
4
+ "description": "Reading a .pnpmfile.cjs",
5
+ "keywords": [
6
+ "pnpm",
7
+ "pnpm11"
8
+ ],
9
+ "license": "MIT",
10
+ "funding": "https://opencollective.com/pnpm",
11
+ "repository": "https://github.com/pnpm/pnpm/tree/main/hooks/pnpmfile",
12
+ "homepage": "https://github.com/pnpm/pnpm/tree/main/hooks/pnpmfile#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/pnpm/pnpm/issues"
15
+ },
16
+ "type": "module",
17
+ "main": "lib/index.js",
18
+ "types": "lib/index.d.ts",
19
+ "exports": {
20
+ ".": "./lib/index.js"
21
+ },
22
+ "files": [
23
+ "lib",
24
+ "!*.map"
25
+ ],
26
+ "dependencies": {
27
+ "chalk": "^5.6.0",
28
+ "path-absolute": "^2.0.0",
29
+ "@pnpm/crypto.hash": "1000.2.1",
30
+ "@pnpm/error": "1000.0.5",
31
+ "@pnpm/hooks.types": "1001.0.12",
32
+ "@pnpm/lockfile.types": "1002.0.2",
33
+ "@pnpm/types": "1000.9.0",
34
+ "@pnpm/store.controller-types": "1004.1.0",
35
+ "@pnpm/core-loggers": "1001.0.4"
36
+ },
37
+ "peerDependencies": {
38
+ "@pnpm/logger": ">=1001.0.0 <1002.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@pnpm/fetching.fetcher-base": "1001.0.2",
42
+ "@pnpm/hooks.pnpmfile": "1002.1.3",
43
+ "@pnpm/logger": "1001.0.1",
44
+ "@pnpm/test-fixtures": "1000.0.0"
45
+ },
46
+ "engines": {
47
+ "node": ">=22.13"
48
+ },
49
+ "jest": {
50
+ "preset": "@pnpm/jest-config"
51
+ },
52
+ "scripts": {
53
+ "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
54
+ "_test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest",
55
+ "test": "pnpm run compile && pnpm run _test",
56
+ "compile": "tsgo --build && pnpm run lint --fix"
57
+ }
58
+ }