@pnpm/lockfile.fs 1100.1.11 → 1100.1.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @pnpm/lockfile-file
2
2
 
3
+ ## 1100.1.12
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed `pnpm install` failing with `ERR_PNPM_LOCKFILE_IS_SYMLINK` when `pnpm-lock.yaml` is a symlink, as build sandboxes such as Bazel and Nix stage it [#13073](https://github.com/pnpm/pnpm/issues/13073). Reading a lockfile through a symlink is allowed again, and an install that leaves the lockfile unchanged no longer rewrites it, so `--frozen-lockfile` no longer needs to write at all. Writing a *changed* lockfile through a symlink is still refused, as that would redirect the write onto the symlink's target.
8
+
9
+ - Prevent broken-lockfile errors from including snippets of the lockfile's contents.
10
+
3
11
  ## 1100.1.11
4
12
 
5
13
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/lockfile.fs",
3
- "version": "1100.1.11",
3
+ "version": "1100.1.12",
4
4
  "description": "Read/write pnpm-lock.yaml files",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@jest/globals": "30.4.1",
55
- "@pnpm/lockfile.fs": "1100.1.11",
55
+ "@pnpm/lockfile.fs": "1100.1.12",
56
56
  "@pnpm/logger": "1100.0.0",
57
57
  "@types/js-yaml": "^4.0.9",
58
58
  "@types/normalize-path": "^3.0.2",
@@ -1,4 +0,0 @@
1
- import type { EnvLockfile } from '@pnpm/lockfile.types';
2
- export declare function createEnvLockfile(): EnvLockfile;
3
- export declare function readEnvLockfile(rootDir: string): Promise<EnvLockfile | null>;
4
- export declare function writeEnvLockfile(rootDir: string, lockfile: EnvLockfile): Promise<void>;
@@ -1,61 +0,0 @@
1
- import path from 'node:path';
2
- import { LOCKFILE_VERSION, WANTED_LOCKFILE } from '@pnpm/constants';
3
- import yaml from 'js-yaml';
4
- import writeFileAtomic from 'write-file-atomic';
5
- import { sortLockfileKeys } from './sortLockfileKeys.js';
6
- import { lockfileYamlDump } from './write.js';
7
- import { extractMainDocument, readLockfileToStringNoFollow, streamReadFirstYamlDocument } from './yamlDocuments.js';
8
- export function createEnvLockfile() {
9
- return {
10
- lockfileVersion: LOCKFILE_VERSION,
11
- importers: {
12
- '.': {
13
- configDependencies: {},
14
- },
15
- },
16
- packages: {},
17
- snapshots: {},
18
- };
19
- }
20
- export async function readEnvLockfile(rootDir) {
21
- const lockfilePath = path.join(rootDir, WANTED_LOCKFILE);
22
- const rawContent = await streamReadFirstYamlDocument(lockfilePath);
23
- if (rawContent == null) {
24
- return null;
25
- }
26
- const parsed = yaml.load(rawContent);
27
- if (parsed == null || typeof parsed !== 'object') {
28
- return null;
29
- }
30
- const lockfile = parsed;
31
- if (typeof lockfile.lockfileVersion !== 'string') {
32
- return null;
33
- }
34
- if (lockfile.importers == null || typeof lockfile.importers !== 'object') {
35
- return null;
36
- }
37
- if (lockfile.packages == null || typeof lockfile.packages !== 'object') {
38
- return null;
39
- }
40
- if (lockfile.snapshots == null || typeof lockfile.snapshots !== 'object') {
41
- return null;
42
- }
43
- const envLockfile = parsed;
44
- if (!envLockfile.importers['.']) {
45
- envLockfile.importers['.'] = { configDependencies: {} };
46
- }
47
- else if (!envLockfile.importers['.'].configDependencies) {
48
- envLockfile.importers['.'].configDependencies = {};
49
- }
50
- return envLockfile;
51
- }
52
- export async function writeEnvLockfile(rootDir, lockfile) {
53
- const lockfilePath = path.join(rootDir, WANTED_LOCKFILE);
54
- const sorted = sortLockfileKeys(lockfile);
55
- const envYaml = lockfileYamlDump(sorted);
56
- const existing = await readLockfileToStringNoFollow(lockfilePath);
57
- const mainDoc = existing == null ? '' : extractMainDocument(existing);
58
- const combined = `---\n${envYaml}\n---\n${mainDoc}`;
59
- return writeFileAtomic(lockfilePath, combined);
60
- }
61
- //# sourceMappingURL=envLockfile.js.map
@@ -1,5 +0,0 @@
1
- import { PnpmError } from '@pnpm/error';
2
- export declare class LockfileBreakingChangeError extends PnpmError {
3
- filename: string;
4
- constructor(filename: string);
5
- }
@@ -1,9 +0,0 @@
1
- import { PnpmError } from '@pnpm/error';
2
- export class LockfileBreakingChangeError extends PnpmError {
3
- filename;
4
- constructor(filename) {
5
- super('LOCKFILE_BREAKING_CHANGE', `Lockfile ${filename} not compatible with current pnpm`);
6
- this.filename = filename;
7
- }
8
- }
9
- //# sourceMappingURL=LockfileBreakingChangeError.js.map
@@ -1 +0,0 @@
1
- export { LockfileBreakingChangeError } from './LockfileBreakingChangeError.js';
@@ -1,2 +0,0 @@
1
- export { LockfileBreakingChangeError } from './LockfileBreakingChangeError.js';
2
- //# sourceMappingURL=index.js.map
@@ -1,6 +0,0 @@
1
- interface ExistsNonEmptyWantedLockfileOptions {
2
- useGitBranchLockfile?: boolean;
3
- mergeGitBranchLockfiles?: boolean;
4
- }
5
- export declare function existsNonEmptyWantedLockfile(pkgPath: string, opts?: ExistsNonEmptyWantedLockfileOptions): Promise<boolean>;
6
- export {};
@@ -1,23 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { getWantedLockfileName } from './lockfileName.js';
4
- export async function existsNonEmptyWantedLockfile(pkgPath, opts = {
5
- useGitBranchLockfile: false,
6
- mergeGitBranchLockfiles: false,
7
- }) {
8
- const wantedLockfile = await getWantedLockfileName(opts);
9
- return new Promise((resolve, reject) => {
10
- fs.access(path.join(pkgPath, wantedLockfile), (err) => {
11
- if (err == null) {
12
- resolve(true);
13
- return;
14
- }
15
- if (err.code === 'ENOENT') {
16
- resolve(false);
17
- return;
18
- }
19
- reject(err);
20
- });
21
- });
22
- }
23
- //# sourceMappingURL=existsWantedLockfile.js.map
@@ -1,2 +0,0 @@
1
- import type { ProjectId } from '@pnpm/types';
2
- export declare function getLockfileImporterId(lockfileDir: string, prefix: string): ProjectId;
@@ -1,6 +0,0 @@
1
- import path from 'node:path';
2
- import normalize from 'normalize-path';
3
- export function getLockfileImporterId(lockfileDir, prefix) {
4
- return (normalize(path.relative(lockfileDir, prefix)) || '.');
5
- }
6
- //# sourceMappingURL=getLockfileImporterId.js.map
@@ -1,3 +0,0 @@
1
- export declare function getGitBranchLockfileNames(lockfileDir: string): Promise<string[]>;
2
- export declare function getGitBranchLockfileNamesSync(lockfileDir: string): string[];
3
- export declare function cleanGitBranchLockfiles(lockfileDir: string): Promise<void>;
@@ -1,22 +0,0 @@
1
- import fs, { promises as fsp } from 'node:fs';
2
- import path from 'node:path';
3
- // Branch lockfiles are written as `pnpm-lock.<branch>.yaml` with literal
4
- // dots and a non-empty branch segment. Escaping the dots keeps unrelated
5
- // files out of the matches that feed scanning and `cleanGitBranchLockfiles`.
6
- const GIT_BRANCH_LOCKFILE_NAME = /^pnpm-lock\..+\.yaml$/;
7
- export async function getGitBranchLockfileNames(lockfileDir) {
8
- const files = await fsp.readdir(lockfileDir);
9
- return files.filter(file => GIT_BRANCH_LOCKFILE_NAME.test(file));
10
- }
11
- export function getGitBranchLockfileNamesSync(lockfileDir) {
12
- const files = fs.readdirSync(lockfileDir);
13
- return files.filter(file => GIT_BRANCH_LOCKFILE_NAME.test(file));
14
- }
15
- export async function cleanGitBranchLockfiles(lockfileDir) {
16
- const gitBranchLockfiles = await getGitBranchLockfileNames(lockfileDir);
17
- await Promise.all(gitBranchLockfiles.map(async (file) => {
18
- const filepath = path.join(lockfileDir, file);
19
- await fsp.unlink(filepath);
20
- }));
21
- }
22
- //# sourceMappingURL=gitBranchLockfile.js.map
@@ -1,3 +0,0 @@
1
- import type { LockfileObject } from '@pnpm/lockfile.types';
2
- export declare function autofixMergeConflicts(fileContent: string): LockfileObject;
3
- export declare function isDiff(fileContent: string): boolean;
@@ -1,47 +0,0 @@
1
- import { mergeLockfileChanges } from '@pnpm/lockfile.merger';
2
- import yaml from 'js-yaml';
3
- import { convertToLockfileObject } from './lockfileFormatConverters.js';
4
- const MERGE_CONFLICT_PARENT = '|||||||';
5
- const MERGE_CONFLICT_END = '>>>>>>>';
6
- const MERGE_CONFLICT_THEIRS = '=======';
7
- const MERGE_CONFLICT_OURS = '<<<<<<<';
8
- export function autofixMergeConflicts(fileContent) {
9
- const { ours, theirs } = parseMergeFile(fileContent);
10
- return mergeLockfileChanges(convertToLockfileObject(yaml.load(ours)), convertToLockfileObject(yaml.load(theirs)));
11
- }
12
- function parseMergeFile(fileContent) {
13
- const lines = fileContent.split(/[\n\r]+/);
14
- let state = 'top';
15
- const ours = [];
16
- const theirs = [];
17
- while (lines.length > 0) {
18
- const line = lines.shift();
19
- if (line.startsWith(MERGE_CONFLICT_PARENT)) {
20
- state = 'parent';
21
- continue;
22
- }
23
- if (line.startsWith(MERGE_CONFLICT_OURS)) {
24
- state = 'ours';
25
- continue;
26
- }
27
- if (line === MERGE_CONFLICT_THEIRS) {
28
- state = 'theirs';
29
- continue;
30
- }
31
- if (line.startsWith(MERGE_CONFLICT_END)) {
32
- state = 'top';
33
- continue;
34
- }
35
- if (state === 'top' || state === 'ours')
36
- ours.push(line);
37
- if (state === 'top' || state === 'theirs')
38
- theirs.push(line);
39
- }
40
- return { ours: ours.join('\n'), theirs: theirs.join('\n') };
41
- }
42
- export function isDiff(fileContent) {
43
- return fileContent.includes(MERGE_CONFLICT_OURS) &&
44
- fileContent.includes(MERGE_CONFLICT_THEIRS) &&
45
- fileContent.includes(MERGE_CONFLICT_END);
46
- }
47
- //# sourceMappingURL=gitMergeFile.js.map
package/lib/index.d.ts DELETED
@@ -1,10 +0,0 @@
1
- export { createEnvLockfile, readEnvLockfile, writeEnvLockfile } from './envLockfile.js';
2
- export { existsNonEmptyWantedLockfile } from './existsWantedLockfile.js';
3
- export { getLockfileImporterId } from './getLockfileImporterId.js';
4
- export { cleanGitBranchLockfiles, getGitBranchLockfileNamesSync } from './gitBranchLockfile.js';
5
- export { convertToLockfileFile, convertToLockfileObject } from './lockfileFormatConverters.js';
6
- export { getWantedLockfileName } from './lockfileName.js';
7
- export * from './read.js';
8
- export { isEmptyLockfile, writeCurrentLockfile, writeLockfileFile, writeLockfiles, type WriteLockfilesResult, writeWantedLockfile, } from './write.js';
9
- export { extractMainDocument } from './yamlDocuments.js';
10
- export * from '@pnpm/lockfile.types';
@@ -1,3 +0,0 @@
1
- import type { LockfileFile, LockfileObject } from '@pnpm/lockfile.types';
2
- export declare function convertToLockfileFile(lockfile: LockfileObject): LockfileFile;
3
- export declare function convertToLockfileObject(lockfile: LockfileFile): LockfileObject;
@@ -1,227 +0,0 @@
1
- import { LOCKFILE_VERSION } from '@pnpm/constants';
2
- import { parse, refToRelative, removeSuffix } from '@pnpm/deps.path';
3
- import { isGitHostedTarballUrl } from '@pnpm/lockfile.utils';
4
- import { DEPENDENCIES_FIELDS } from '@pnpm/types';
5
- import { isEmpty, map as _mapValues, omit, pick, pickBy } from 'ramda';
6
- export function convertToLockfileFile(lockfile) {
7
- const packages = {};
8
- const snapshots = {};
9
- for (const [depPath, pkg] of Object.entries(lockfile.packages ?? {})) {
10
- snapshots[depPath] = pick([
11
- 'dependencies',
12
- 'optionalDependencies',
13
- 'transitivePeerDependencies',
14
- 'optional',
15
- 'id',
16
- ], pkg);
17
- const pkgId = removeSuffix(depPath);
18
- if (!packages[pkgId]) {
19
- packages[pkgId] = pick([
20
- 'bundledDependencies',
21
- 'cpu',
22
- 'deprecated',
23
- 'engines',
24
- 'hasBin',
25
- 'libc',
26
- 'name',
27
- 'os',
28
- 'peerDependencies',
29
- 'peerDependenciesMeta',
30
- 'resolution',
31
- 'version',
32
- ], pkg);
33
- }
34
- }
35
- const newLockfile = {
36
- ...lockfile,
37
- snapshots,
38
- packages,
39
- lockfileVersion: LOCKFILE_VERSION,
40
- importers: mapValues(lockfile.importers, convertProjectSnapshotToInlineSpecifiersFormat),
41
- };
42
- if (newLockfile.settings?.peersSuffixMaxLength === 1000) {
43
- newLockfile.settings = omit(['peersSuffixMaxLength'], newLockfile.settings);
44
- }
45
- if (newLockfile.settings?.injectWorkspacePackages === false) {
46
- delete newLockfile.settings.injectWorkspacePackages;
47
- }
48
- return normalizeLockfile(newLockfile);
49
- }
50
- function normalizeLockfile(lockfile) {
51
- const lockfileToSave = {
52
- ...lockfile,
53
- importers: _mapValues((importer) => {
54
- const normalizedImporter = {};
55
- if (importer.dependenciesMeta != null && !isEmpty(importer.dependenciesMeta)) {
56
- normalizedImporter.dependenciesMeta = importer.dependenciesMeta;
57
- }
58
- for (const depType of DEPENDENCIES_FIELDS) {
59
- if (!isEmpty(importer[depType] ?? {})) {
60
- normalizedImporter[depType] = importer[depType];
61
- }
62
- }
63
- if (importer.publishDirectory) {
64
- normalizedImporter.publishDirectory = importer.publishDirectory;
65
- }
66
- return normalizedImporter;
67
- }, lockfile.importers ?? {}),
68
- };
69
- if (isEmpty(lockfileToSave.packages) || (lockfileToSave.packages == null)) {
70
- delete lockfileToSave.packages;
71
- }
72
- if (isEmpty(lockfileToSave.snapshots) || (lockfileToSave.snapshots == null)) {
73
- delete lockfileToSave.snapshots;
74
- }
75
- if (lockfileToSave.time) {
76
- lockfileToSave.time = pruneTimeInLockfile(lockfileToSave.time, lockfile.importers ?? {});
77
- }
78
- if ((lockfileToSave.catalogs != null) && isEmpty(lockfileToSave.catalogs)) {
79
- delete lockfileToSave.catalogs;
80
- }
81
- if ((lockfileToSave.overrides != null) && isEmpty(lockfileToSave.overrides)) {
82
- delete lockfileToSave.overrides;
83
- }
84
- if ((lockfileToSave.patchedDependencies != null) && isEmpty(lockfileToSave.patchedDependencies)) {
85
- delete lockfileToSave.patchedDependencies;
86
- }
87
- if (!lockfileToSave.packageExtensionsChecksum) {
88
- delete lockfileToSave.packageExtensionsChecksum;
89
- }
90
- if (!lockfileToSave.ignoredOptionalDependencies?.length) {
91
- delete lockfileToSave.ignoredOptionalDependencies;
92
- }
93
- if (!lockfileToSave.pnpmfileChecksum) {
94
- delete lockfileToSave.pnpmfileChecksum;
95
- }
96
- return lockfileToSave;
97
- }
98
- function pruneTimeInLockfile(time, importers) {
99
- const rootDepPaths = new Set();
100
- for (const importer of Object.values(importers)) {
101
- for (const depType of DEPENDENCIES_FIELDS) {
102
- for (const [depName, ref] of Object.entries(importer[depType] ?? {})) {
103
- const suffixStart = ref.version.indexOf('(');
104
- const refWithoutPeerDepGraphHash = suffixStart === -1 ? ref.version : ref.version.slice(0, suffixStart);
105
- const depPath = refToRelative(refWithoutPeerDepGraphHash, depName);
106
- if (!depPath)
107
- continue;
108
- rootDepPaths.add(depPath);
109
- }
110
- }
111
- }
112
- return pickBy((_, depPath) => rootDepPaths.has(depPath), time);
113
- }
114
- // Mirrors `isFilename` in `resolving/local-resolver/src/parseBareSpecifier.ts`
115
- // so the directory-vs-tarball boundary applied at lockfile load time
116
- // matches the resolver's at resolve time.
117
- const LOCAL_TARBALL_RE = /\.(?:tgz|tar\.gz|tar)$/i;
118
- export function convertToLockfileObject(lockfile) {
119
- const { importers, ...rest } = lockfile;
120
- const packages = {};
121
- for (const [depPath, pkg] of Object.entries(lockfile.snapshots ?? {})) {
122
- const pkgId = removeSuffix(depPath);
123
- const snapshot = Object.assign(pkg, lockfile.packages?.[pkgId]);
124
- // Defense-in-depth for pruned lockfiles (older `turbo prune --docker`,
125
- // pre vercel/turborepo#12825): a peer-variant injected workspace
126
- // snapshot whose base `packages:` entry was dropped now has a null
127
- // `resolution`. Reconstruct it from the `file:` depPath — same value
128
- // pnpm's writer emits — so every downstream reader sees a complete
129
- // snapshot without per-reader guards.
130
- if (snapshot.resolution == null) {
131
- const ref = parse(depPath).nonSemverVersion;
132
- if (ref != null && ref.startsWith('file:') && !LOCAL_TARBALL_RE.test(ref)) {
133
- snapshot.resolution = { directory: ref.slice('file:'.length), type: 'directory' };
134
- }
135
- }
136
- packages[depPath] = snapshot;
137
- enrichGitHostedFlag(packages[depPath]?.resolution);
138
- }
139
- return {
140
- ...omit(['snapshots'], rest),
141
- patchedDependencies: migratePatchedDependencies(rest.patchedDependencies),
142
- packages,
143
- importers: mapValues(importers ?? {}, revertProjectSnapshot),
144
- };
145
- }
146
- // Backfill the `gitHosted` flag for tarball resolutions written by older
147
- // pnpm versions. Doing it once at load time lets every downstream reader
148
- // rely on the typed field instead of repeating URL prefix matches.
149
- function enrichGitHostedFlag(resolution) {
150
- if (resolution == null)
151
- return;
152
- if (resolution.type !== undefined)
153
- return;
154
- if (resolution.gitHosted != null)
155
- return;
156
- if (resolution.tarball != null && isGitHostedTarballUrl(resolution.tarball)) {
157
- resolution.gitHosted = true;
158
- }
159
- }
160
- function migratePatchedDependencies(patchedDependencies) {
161
- if (!patchedDependencies)
162
- return undefined;
163
- const result = {};
164
- for (const [key, value] of Object.entries(patchedDependencies)) {
165
- result[key] = typeof value === 'string' ? value : value.hash;
166
- }
167
- return result;
168
- }
169
- function convertProjectSnapshotToInlineSpecifiersFormat(projectSnapshot) {
170
- const { specifiers, ...rest } = projectSnapshot;
171
- if (specifiers == null)
172
- return projectSnapshot;
173
- const convertBlock = (block) => block != null
174
- ? convertResolvedDependenciesToInlineSpecifiersFormat(block, { specifiers })
175
- : block;
176
- return {
177
- ...rest,
178
- dependencies: convertBlock(projectSnapshot.dependencies ?? {}),
179
- optionalDependencies: convertBlock(projectSnapshot.optionalDependencies ?? {}),
180
- devDependencies: convertBlock(projectSnapshot.devDependencies ?? {}),
181
- };
182
- }
183
- function convertResolvedDependenciesToInlineSpecifiersFormat(resolvedDependencies, { specifiers }) {
184
- return mapValues(resolvedDependencies, (version, depName) => ({
185
- specifier: specifiers[depName],
186
- version,
187
- }));
188
- }
189
- function revertProjectSnapshot(from) {
190
- const specifiers = {};
191
- function moveSpecifiers(from) {
192
- const resolvedDependencies = {};
193
- for (const [depName, { specifier, version }] of Object.entries(from)) {
194
- const existingValue = specifiers[depName];
195
- if (existingValue != null && existingValue !== specifier) {
196
- throw new Error(`Project snapshot lists the same dependency more than once with conflicting versions: ${depName}`);
197
- }
198
- specifiers[depName] = specifier;
199
- resolvedDependencies[depName] = version;
200
- }
201
- return resolvedDependencies;
202
- }
203
- const dependencies = from.dependencies == null
204
- ? from.dependencies
205
- : moveSpecifiers(from.dependencies);
206
- const devDependencies = from.devDependencies == null
207
- ? from.devDependencies
208
- : moveSpecifiers(from.devDependencies);
209
- const optionalDependencies = from.optionalDependencies == null
210
- ? from.optionalDependencies
211
- : moveSpecifiers(from.optionalDependencies);
212
- return {
213
- ...from,
214
- specifiers,
215
- dependencies,
216
- devDependencies,
217
- optionalDependencies,
218
- };
219
- }
220
- function mapValues(obj, mapper) {
221
- const result = {};
222
- for (const [key, value] of Object.entries(obj)) {
223
- result[key] = mapper(value, key);
224
- }
225
- return result;
226
- }
227
- //# sourceMappingURL=lockfileFormatConverters.js.map
@@ -1,6 +0,0 @@
1
- export interface GetWantedLockfileNameOptions {
2
- useGitBranchLockfile?: boolean;
3
- mergeGitBranchLockfiles?: boolean;
4
- cwd?: string;
5
- }
6
- export declare function getWantedLockfileName(opts?: GetWantedLockfileNameOptions): Promise<string>;
@@ -1,19 +0,0 @@
1
- import { WANTED_LOCKFILE } from '@pnpm/constants';
2
- import { getCurrentBranch } from '@pnpm/network.git-utils';
3
- export async function getWantedLockfileName(opts = {}) {
4
- if (opts.useGitBranchLockfile && !opts.mergeGitBranchLockfiles) {
5
- const currentBranchName = await getCurrentBranch({ cwd: opts.cwd });
6
- if (currentBranchName) {
7
- return WANTED_LOCKFILE.replace('.yaml', `.${stringifyBranchName(currentBranchName)}.yaml`);
8
- }
9
- }
10
- return WANTED_LOCKFILE;
11
- }
12
- /**
13
- * 1. Git branch name may contains slashes, which is not allowed in filenames
14
- * 2. Filesystem may be case-insensitive, so we need to convert branch name to lowercase
15
- */
16
- function stringifyBranchName(branchName = '') {
17
- return branchName.replace(/[^\w.-]/g, '!').toLowerCase();
18
- }
19
- //# sourceMappingURL=lockfileName.js.map
package/lib/logger.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const lockfileLogger: import("@pnpm/logger").Logger<unknown>;
package/lib/logger.js DELETED
@@ -1,3 +0,0 @@
1
- import { logger } from '@pnpm/logger';
2
- export const lockfileLogger = logger('lockfile');
3
- //# sourceMappingURL=logger.js.map
package/lib/read.d.ts DELETED
@@ -1,42 +0,0 @@
1
- import type { LockfileFile, LockfileObject } from '@pnpm/lockfile.types';
2
- import type { ProjectId } from '@pnpm/types';
3
- export declare function readCurrentLockfile(pnpmInternalDir: string, opts: {
4
- wantedVersions?: string[];
5
- ignoreIncompatible: boolean;
6
- }): Promise<LockfileObject | null>;
7
- export declare function readWantedLockfileAndAutofixConflicts(pkgPath: string, opts: {
8
- wantedVersions?: string[];
9
- ignoreIncompatible: boolean;
10
- useGitBranchLockfile?: boolean;
11
- mergeGitBranchLockfiles?: boolean;
12
- }): Promise<{
13
- lockfile: LockfileObject | null;
14
- hadConflicts: boolean;
15
- }>;
16
- export declare function readWantedLockfile(pkgPath: string, opts: {
17
- wantedVersions?: string[];
18
- ignoreIncompatible: boolean;
19
- useGitBranchLockfile?: boolean;
20
- mergeGitBranchLockfiles?: boolean;
21
- }): Promise<LockfileObject | null>;
22
- /**
23
- * Read the wanted lockfile in its on-disk shape ({@link LockfileFile}),
24
- * skipping the conversion to the in-memory {@link LockfileObject}.
25
- *
26
- * Use this when the caller needs the exact serialized form — e.g. to
27
- * forward the lockfile to a server that speaks the on-disk format —
28
- * rather than the in-process representation.
29
- */
30
- export declare function readWantedLockfileFile(pkgPath: string, opts: {
31
- wantedVersions?: string[];
32
- ignoreIncompatible: boolean;
33
- useGitBranchLockfile?: boolean;
34
- mergeGitBranchLockfiles?: boolean;
35
- }): Promise<LockfileFile | null>;
36
- export declare function wantedLockfileHasMergeConflictsSync(pkgPath: string, lockfileName?: string): boolean;
37
- export declare function createLockfileObject(importerIds: ProjectId[], opts: {
38
- lockfileVersion: string;
39
- autoInstallPeers: boolean;
40
- excludeLinksFromLockfile: boolean;
41
- peersSuffixMaxLength: number;
42
- }): LockfileObject;
package/lib/read.js DELETED
@@ -1,186 +0,0 @@
1
- import fs, { promises as fsp } from 'node:fs';
2
- import path from 'node:path';
3
- import util from 'node:util';
4
- import { LOCKFILE_VERSION, WANTED_LOCKFILE, } from '@pnpm/constants';
5
- import { PnpmError } from '@pnpm/error';
6
- import { mergeLockfileChanges } from '@pnpm/lockfile.merger';
7
- import { comverToSemver } from 'comver-to-semver';
8
- import yaml from 'js-yaml';
9
- import semver from 'semver';
10
- import stripBom from 'strip-bom';
11
- import { LockfileBreakingChangeError } from './errors/index.js';
12
- import { getGitBranchLockfileNames } from './gitBranchLockfile.js';
13
- import { autofixMergeConflicts, isDiff } from './gitMergeFile.js';
14
- import { convertToLockfileFile, convertToLockfileObject } from './lockfileFormatConverters.js';
15
- import { getWantedLockfileName } from './lockfileName.js';
16
- import { lockfileLogger as logger } from './logger.js';
17
- import { extractMainDocument } from './yamlDocuments.js';
18
- export async function readCurrentLockfile(pnpmInternalDir, opts) {
19
- const lockfilePath = path.join(pnpmInternalDir, 'lock.yaml');
20
- return (await _read(lockfilePath, pnpmInternalDir, opts)).lockfile;
21
- }
22
- export async function readWantedLockfileAndAutofixConflicts(pkgPath, opts) {
23
- return _readWantedLockfile(pkgPath, {
24
- ...opts,
25
- autofixMergeConflicts: true,
26
- });
27
- }
28
- export async function readWantedLockfile(pkgPath, opts) {
29
- return (await _readWantedLockfile(pkgPath, opts)).lockfile;
30
- }
31
- /**
32
- * Read the wanted lockfile in its on-disk shape ({@link LockfileFile}),
33
- * skipping the conversion to the in-memory {@link LockfileObject}.
34
- *
35
- * Use this when the caller needs the exact serialized form — e.g. to
36
- * forward the lockfile to a server that speaks the on-disk format —
37
- * rather than the in-process representation.
38
- */
39
- export async function readWantedLockfileFile(pkgPath, opts) {
40
- return (await _readWantedLockfile(pkgPath, opts)).lockfileFile;
41
- }
42
- export function wantedLockfileHasMergeConflictsSync(pkgPath, lockfileName = WANTED_LOCKFILE) {
43
- try {
44
- const lockfileRawContent = stripBom(fs.readFileSync(path.join(pkgPath, lockfileName), 'utf8'));
45
- return isDiff(extractMainDocument(lockfileRawContent));
46
- }
47
- catch (err) {
48
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
49
- return false;
50
- }
51
- throw err;
52
- }
53
- }
54
- async function _read(lockfilePath, prefix, // only for logging
55
- opts) {
56
- let lockfileRawContent;
57
- try {
58
- lockfileRawContent = stripBom(await fsp.readFile(lockfilePath, 'utf8'));
59
- }
60
- catch (err) {
61
- if (!(util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT')) {
62
- throw err;
63
- }
64
- return {
65
- lockfile: null,
66
- lockfileFile: null,
67
- hadConflicts: false,
68
- };
69
- }
70
- // Skip the env lockfile document if present (first document in combined format)
71
- lockfileRawContent = extractMainDocument(lockfileRawContent);
72
- if (!lockfileRawContent.trim()) {
73
- return {
74
- lockfile: null,
75
- lockfileFile: null,
76
- hadConflicts: false,
77
- };
78
- }
79
- let lockfile;
80
- let lockfileFile;
81
- let hadConflicts;
82
- try {
83
- lockfileFile = yaml.load(lockfileRawContent);
84
- lockfile = convertToLockfileObject(lockfileFile);
85
- hadConflicts = false;
86
- }
87
- catch (err) {
88
- if (!opts.autofixMergeConflicts || !isDiff(lockfileRawContent)) {
89
- throw new PnpmError('BROKEN_LOCKFILE', `The lockfile at "${lockfilePath}" is broken: ${err.message}`);
90
- }
91
- hadConflicts = true;
92
- lockfile = autofixMergeConflicts(lockfileRawContent);
93
- lockfileFile = convertToLockfileFile(lockfile);
94
- logger.info({
95
- message: `Merge conflict detected in ${WANTED_LOCKFILE} and successfully merged`,
96
- prefix,
97
- });
98
- }
99
- if (lockfile) {
100
- const lockfileSemver = comverToSemver((lockfile.lockfileVersion ?? 0).toString());
101
- if (!opts.wantedVersions ||
102
- opts.wantedVersions.length === 0 ||
103
- opts.wantedVersions.some((wantedVersion) => {
104
- if (semver.major(lockfileSemver) !== semver.major(comverToSemver(wantedVersion)))
105
- return false;
106
- if (lockfile.lockfileVersion !== '6.1' && semver.gt(lockfileSemver, comverToSemver(wantedVersion))) {
107
- logger.warn({
108
- message: `Your ${WANTED_LOCKFILE} was generated by a newer version of pnpm. ` +
109
- `It is a compatible version but it might get downgraded to version ${wantedVersion}`,
110
- prefix,
111
- });
112
- }
113
- return true;
114
- })) {
115
- return { lockfile, lockfileFile, hadConflicts };
116
- }
117
- }
118
- if (opts.ignoreIncompatible) {
119
- logger.warn({
120
- message: `Ignoring not compatible lockfile at ${lockfilePath}`,
121
- prefix,
122
- });
123
- return { lockfile: null, lockfileFile: null, hadConflicts: false };
124
- }
125
- throw new LockfileBreakingChangeError(lockfilePath);
126
- }
127
- export function createLockfileObject(importerIds, opts) {
128
- const importers = {};
129
- for (const importerId of importerIds) {
130
- importers[importerId] = {
131
- dependencies: {},
132
- specifiers: {},
133
- };
134
- }
135
- return {
136
- importers,
137
- lockfileVersion: opts.lockfileVersion || LOCKFILE_VERSION,
138
- settings: {
139
- autoInstallPeers: opts.autoInstallPeers,
140
- excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
141
- peersSuffixMaxLength: opts.peersSuffixMaxLength,
142
- },
143
- };
144
- }
145
- async function _readWantedLockfile(pkgPath, opts) {
146
- const lockfileNames = [WANTED_LOCKFILE];
147
- if (opts.useGitBranchLockfile) {
148
- const gitBranchLockfileName = await getWantedLockfileName(opts);
149
- if (gitBranchLockfileName !== WANTED_LOCKFILE) {
150
- lockfileNames.unshift(gitBranchLockfileName);
151
- }
152
- }
153
- let result = { lockfile: null, lockfileFile: null, hadConflicts: false };
154
- /* eslint-disable no-await-in-loop */
155
- for (const lockfileName of lockfileNames) {
156
- result = await _read(path.join(pkgPath, lockfileName), pkgPath, { ...opts, autofixMergeConflicts: true });
157
- if (result.lockfile) {
158
- if (opts.mergeGitBranchLockfiles) {
159
- result.lockfile = await _mergeGitBranchLockfiles(result.lockfile, pkgPath, pkgPath, opts);
160
- result.lockfileFile = result.lockfile ? convertToLockfileFile(result.lockfile) : null;
161
- }
162
- break;
163
- }
164
- }
165
- /* eslint-enable no-await-in-loop */
166
- return result;
167
- }
168
- async function _mergeGitBranchLockfiles(lockfile, lockfileDir, prefix, opts) {
169
- if (!lockfile) {
170
- return lockfile;
171
- }
172
- const gitBranchLockfiles = (await _readGitBranchLockfiles(lockfileDir, prefix, opts)).map(({ lockfile }) => lockfile);
173
- let mergedLockfile = lockfile;
174
- for (const gitBranchLockfile of gitBranchLockfiles) {
175
- if (!gitBranchLockfile) {
176
- continue;
177
- }
178
- mergedLockfile = mergeLockfileChanges(mergedLockfile, gitBranchLockfile);
179
- }
180
- return mergedLockfile;
181
- }
182
- async function _readGitBranchLockfiles(lockfileDir, prefix, opts) {
183
- const files = await getGitBranchLockfileNames(lockfileDir);
184
- return Promise.all(files.map((file) => _read(path.join(lockfileDir, file), prefix, opts)));
185
- }
186
- //# sourceMappingURL=read.js.map
@@ -1,3 +0,0 @@
1
- import type { EnvLockfile, LockfileFile } from '@pnpm/lockfile.types';
2
- export declare function sortLockfileKeys(lockfile: LockfileFile): LockfileFile;
3
- export declare function sortLockfileKeys(lockfile: EnvLockfile): EnvLockfile;
@@ -1,78 +0,0 @@
1
- import { sortDeepKeys, sortDirectKeys, sortKeysByPriority } from '@pnpm/object.key-sorting';
2
- const ORDERED_KEYS = {
3
- resolution: 1,
4
- id: 2,
5
- name: 3,
6
- version: 4,
7
- engines: 5,
8
- cpu: 6,
9
- os: 7,
10
- libc: 8,
11
- deprecated: 9,
12
- hasBin: 10,
13
- prepare: 11,
14
- requiresBuild: 12,
15
- bundleDependencies: 13,
16
- peerDependencies: 14,
17
- peerDependenciesMeta: 15,
18
- dependencies: 16,
19
- optionalDependencies: 17,
20
- transitivePeerDependencies: 18,
21
- dev: 19,
22
- optional: 20,
23
- };
24
- const ROOT_KEYS = [
25
- 'lockfileVersion',
26
- 'settings',
27
- 'catalogs',
28
- 'overrides',
29
- 'packageExtensionsChecksum',
30
- 'pnpmfileChecksum',
31
- 'patchedDependencies',
32
- 'importers',
33
- 'packages',
34
- ];
35
- const ROOT_KEYS_ORDER = Object.fromEntries(ROOT_KEYS.map((key, index) => [key, index]));
36
- export function sortLockfileKeys(lockfile) {
37
- if (lockfile.importers != null) {
38
- lockfile.importers = sortDirectKeys(lockfile.importers);
39
- for (const [importerId, importer] of Object.entries(lockfile.importers)) {
40
- lockfile.importers[importerId] = sortKeysByPriority({
41
- priority: ROOT_KEYS_ORDER,
42
- deep: true,
43
- }, importer);
44
- }
45
- }
46
- if (lockfile.packages != null) {
47
- lockfile.packages = sortDirectKeys(lockfile.packages);
48
- for (const [pkgId, pkg] of Object.entries(lockfile.packages)) {
49
- lockfile.packages[pkgId] = sortKeysByPriority({
50
- priority: ORDERED_KEYS,
51
- deep: true,
52
- }, pkg);
53
- }
54
- }
55
- if (lockfile.snapshots != null) {
56
- lockfile.snapshots = sortDirectKeys(lockfile.snapshots);
57
- for (const [pkgId, pkg] of Object.entries(lockfile.snapshots)) {
58
- lockfile.snapshots[pkgId] = sortKeysByPriority({
59
- priority: ORDERED_KEYS,
60
- deep: true,
61
- }, pkg);
62
- }
63
- }
64
- if ('catalogs' in lockfile && lockfile.catalogs != null) {
65
- lockfile.catalogs = sortDirectKeys(lockfile.catalogs);
66
- for (const [catalogName, catalog] of Object.entries(lockfile.catalogs)) {
67
- lockfile.catalogs[catalogName] = sortDeepKeys(catalog);
68
- }
69
- }
70
- if ('time' in lockfile && lockfile.time != null) {
71
- lockfile.time = sortDirectKeys(lockfile.time);
72
- }
73
- if ('patchedDependencies' in lockfile && lockfile.patchedDependencies != null) {
74
- lockfile.patchedDependencies = sortDirectKeys(lockfile.patchedDependencies);
75
- }
76
- return sortKeysByPriority({ priority: ROOT_KEYS_ORDER }, lockfile);
77
- }
78
- //# sourceMappingURL=sortLockfileKeys.js.map
package/lib/write.d.ts DELETED
@@ -1,39 +0,0 @@
1
- import type { LockfileFile, LockfileObject } from '@pnpm/lockfile.types';
2
- export declare function lockfileYamlDump(obj: object): string;
3
- /**
4
- * Returns the canonical post-write lockfile — structurally identical
5
- * to what `readWantedLockfile` would parse back. Lets callers like
6
- * the verification cache hash the as-saved form without re-reading.
7
- */
8
- export declare function writeWantedLockfile(pkgPath: string, wantedLockfile: LockfileObject, opts?: {
9
- useGitBranchLockfile?: boolean;
10
- mergeGitBranchLockfiles?: boolean;
11
- /** Pre-resolved filename; skips the `getWantedLockfileName` (and
12
- * its `getCurrentBranch`) call when supplied. */
13
- lockfileName?: string;
14
- }): Promise<LockfileObject>;
15
- export declare function writeCurrentLockfile(virtualStoreDir: string, currentLockfile: LockfileObject): Promise<LockfileObject | undefined>;
16
- export declare function writeLockfileFile(lockfilePath: string, wantedLockfile: LockfileFile): Promise<void>;
17
- export declare function isEmptyLockfile(lockfile: LockfileObject): boolean;
18
- export interface WriteLockfilesResult {
19
- /**
20
- * The canonical "as-saved" wanted lockfile — the inverse converter
21
- * applied to the same object that was serialized to YAML. Hashing
22
- * this is equivalent to hashing the lockfile the next install will
23
- * load from disk (modulo undefined values that YAML drops, which any
24
- * sensible canonicalization-then-hash routine should strip).
25
- */
26
- wantedLockfile: LockfileObject;
27
- /** Same as above for the current lockfile, or undefined when it was skipped because empty. */
28
- currentLockfile: LockfileObject | undefined;
29
- }
30
- export declare function writeLockfiles(opts: {
31
- wantedLockfile: LockfileObject;
32
- wantedLockfileDir: string;
33
- currentLockfile: LockfileObject;
34
- currentLockfileDir: string;
35
- useGitBranchLockfile?: boolean;
36
- mergeGitBranchLockfiles?: boolean;
37
- /** See {@link writeWantedLockfile}'s `lockfileName` option. */
38
- wantedLockfileName?: string;
39
- }): Promise<WriteLockfilesResult>;
package/lib/write.js DELETED
@@ -1,155 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import { WANTED_LOCKFILE } from '@pnpm/constants';
4
- import { rimraf } from '@zkochan/rimraf';
5
- import yaml from 'js-yaml';
6
- import { isEmpty } from 'ramda';
7
- import writeFileAtomic from 'write-file-atomic';
8
- import { convertToLockfileFile, convertToLockfileObject } from './lockfileFormatConverters.js';
9
- import { getWantedLockfileName } from './lockfileName.js';
10
- import { lockfileLogger as logger } from './logger.js';
11
- import { sortLockfileKeys } from './sortLockfileKeys.js';
12
- import { streamReadFirstYamlDocument, YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START } from './yamlDocuments.js';
13
- const LOCKFILE_YAML_FORMAT = {
14
- blankLines: true,
15
- lineWidth: -1,
16
- noCompatMode: true,
17
- noRefs: true,
18
- sortKeys: false,
19
- };
20
- export function lockfileYamlDump(obj) {
21
- return yaml.dump(obj, LOCKFILE_YAML_FORMAT);
22
- }
23
- /**
24
- * Returns the canonical post-write lockfile — structurally identical
25
- * to what `readWantedLockfile` would parse back. Lets callers like
26
- * the verification cache hash the as-saved form without re-reading.
27
- */
28
- export async function writeWantedLockfile(pkgPath, wantedLockfile, opts) {
29
- const wantedLockfileName = opts?.lockfileName ?? await getWantedLockfileName(opts);
30
- return writeLockfile(wantedLockfileName, pkgPath, wantedLockfile);
31
- }
32
- export async function writeCurrentLockfile(virtualStoreDir, currentLockfile) {
33
- // empty lockfile is not saved
34
- if (isEmptyLockfile(currentLockfile)) {
35
- await rimraf(path.join(virtualStoreDir, 'lock.yaml'));
36
- return undefined;
37
- }
38
- await fs.mkdir(virtualStoreDir, { recursive: true });
39
- return writeLockfile('lock.yaml', virtualStoreDir, currentLockfile);
40
- }
41
- async function writeLockfile(lockfileFilename, pkgPath, wantedLockfile) {
42
- const lockfilePath = path.join(pkgPath, lockfileFilename);
43
- const lockfileToStringify = convertToLockfileFile(wantedLockfile);
44
- const yamlDoc = yamlStringify(lockfileToStringify);
45
- if (lockfileFilename === WANTED_LOCKFILE) {
46
- // Re-read the env document from the existing lockfile to preserve it.
47
- // Ideally the env document would be captured during the initial lockfile read
48
- // and passed through to the write functions, but that would require threading it
49
- // through 25+ call sites. Re-reading is cheap since the file is likely still
50
- // in the OS page cache and streaming stops at the first separator.
51
- const envDoc = await streamReadFirstYamlDocument(lockfilePath);
52
- const envPrefix = envDoc != null ? `${YAML_DOCUMENT_START}${envDoc}${YAML_DOCUMENT_SEPARATOR}` : '';
53
- await writeFileAtomic(lockfilePath, `${envPrefix}${yamlDoc}`);
54
- }
55
- else {
56
- await writeFileAtomic(lockfilePath, yamlDoc);
57
- }
58
- // YAML drops undefined on serialize, so the in-memory LockfileFile
59
- // can carry fields (like an unset settings.dedupePeers) that won't
60
- // survive a round-trip; strip them to mirror what the next reader
61
- // will parse back.
62
- return convertToLockfileObject(stripUndefinedDeep(lockfileToStringify));
63
- }
64
- function stripUndefinedDeep(value) {
65
- if (value === null || typeof value !== 'object')
66
- return value;
67
- if (Array.isArray(value))
68
- return value.map(stripUndefinedDeep);
69
- const out = {};
70
- for (const [k, v] of Object.entries(value)) {
71
- if (v === undefined)
72
- continue;
73
- out[k] = stripUndefinedDeep(v);
74
- }
75
- return out;
76
- }
77
- export function writeLockfileFile(lockfilePath, wantedLockfile) {
78
- const yamlDoc = yamlStringify(wantedLockfile);
79
- return writeFileAtomic(lockfilePath, yamlDoc);
80
- }
81
- function yamlStringify(lockfile) {
82
- const sortedLockfile = sortLockfileKeys(lockfile);
83
- return lockfileYamlDump(sortedLockfile);
84
- }
85
- export function isEmptyLockfile(lockfile) {
86
- return Object.values(lockfile.importers).every((importer) => isEmpty(importer.specifiers ?? {}) && isEmpty(importer.dependencies ?? {}));
87
- }
88
- export async function writeLockfiles(opts) {
89
- const wantedLockfileName = opts.wantedLockfileName ?? await getWantedLockfileName(opts);
90
- const wantedLockfilePath = path.join(opts.wantedLockfileDir, wantedLockfileName);
91
- const currentLockfilePath = path.join(opts.currentLockfileDir, 'lock.yaml');
92
- const wantedLockfileToStringify = convertToLockfileFile(opts.wantedLockfile);
93
- const yamlDoc = yamlStringify(wantedLockfileToStringify);
94
- // Preserve the env lockfile document at the top of pnpm-lock.yaml
95
- let envPrefix = '';
96
- if (wantedLockfileName === WANTED_LOCKFILE) {
97
- const envDoc = await streamReadFirstYamlDocument(wantedLockfilePath);
98
- if (envDoc != null) {
99
- envPrefix = `${YAML_DOCUMENT_START}${envDoc}${YAML_DOCUMENT_SEPARATOR}`;
100
- }
101
- }
102
- const wantedYamlDoc = `${envPrefix}${yamlDoc}`;
103
- // in most cases the `pnpm-lock.yaml` and `node_modules/.pnpm-lock.yaml` are equal
104
- // in those cases the YAML document can be stringified only once for both files
105
- // which is more efficient
106
- if (opts.wantedLockfile === opts.currentLockfile) {
107
- await Promise.all([
108
- writeFileAtomic(wantedLockfilePath, wantedYamlDoc),
109
- (async () => {
110
- if (isEmptyLockfile(opts.wantedLockfile)) {
111
- await rimraf(currentLockfilePath);
112
- }
113
- else {
114
- await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
115
- // Current lockfile (node_modules/.pnpm/lock.yaml) does not include the env document
116
- await writeFileAtomic(currentLockfilePath, yamlDoc);
117
- }
118
- })(),
119
- ]);
120
- // Both files share the same source object; strip once and reuse.
121
- const normalized = convertToLockfileObject(stripUndefinedDeep(wantedLockfileToStringify));
122
- return {
123
- wantedLockfile: normalized,
124
- currentLockfile: isEmptyLockfile(opts.wantedLockfile) ? undefined : normalized,
125
- };
126
- }
127
- logger.debug({
128
- message: `\`${WANTED_LOCKFILE}\` differs from \`${path.relative(opts.wantedLockfileDir, currentLockfilePath)}\``,
129
- prefix: opts.wantedLockfileDir,
130
- });
131
- const currentLockfileToStringify = convertToLockfileFile(opts.currentLockfile);
132
- const currentYamlDoc = yamlStringify(currentLockfileToStringify);
133
- // Filtered-current callers (deps-restorer) can pass an empty
134
- // current against a non-empty wanted; key off the current.
135
- const currentIsEmpty = isEmptyLockfile(opts.currentLockfile);
136
- await Promise.all([
137
- writeFileAtomic(wantedLockfilePath, wantedYamlDoc),
138
- (async () => {
139
- if (currentIsEmpty) {
140
- await rimraf(currentLockfilePath);
141
- }
142
- else {
143
- await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
144
- await writeFileAtomic(currentLockfilePath, currentYamlDoc);
145
- }
146
- })(),
147
- ]);
148
- return {
149
- wantedLockfile: convertToLockfileObject(stripUndefinedDeep(wantedLockfileToStringify)),
150
- currentLockfile: currentIsEmpty
151
- ? undefined
152
- : convertToLockfileObject(stripUndefinedDeep(currentLockfileToStringify)),
153
- };
154
- }
155
- //# sourceMappingURL=write.js.map
@@ -1,17 +0,0 @@
1
- export declare const YAML_DOCUMENT_SEPARATOR = "\n---\n";
2
- export declare const YAML_DOCUMENT_START = "---\n";
3
- /**
4
- * Reads the first YAML document from a multi-document YAML file using streaming.
5
- * The file must start with "---\n" to indicate it contains an env lockfile document.
6
- * Stops reading as soon as the second document separator is found.
7
- * Returns null if the file doesn't exist or doesn't start with "---\n".
8
- */
9
- export declare function streamReadFirstYamlDocument(filePath: string, readBufferSize?: number): Promise<string | null>;
10
- export declare function readLockfileToStringNoFollow(filePath: string): Promise<string | null>;
11
- /**
12
- * Extracts the main lockfile content (second YAML document) from a combined string.
13
- * If the file starts with "---\n", returns the content after the separator.
14
- * If there is no separator, returns empty string (file is env-only).
15
- * Otherwise returns the entire content (no env document present).
16
- */
17
- export declare function extractMainDocument(content: string): string;
@@ -1,138 +0,0 @@
1
- import { constants } from 'node:fs';
2
- import { lstat, open } from 'node:fs/promises';
3
- import { StringDecoder } from 'node:string_decoder';
4
- import util from 'node:util';
5
- import { PnpmError } from '@pnpm/error';
6
- import stripBom from 'strip-bom';
7
- export const YAML_DOCUMENT_SEPARATOR = '\n---\n';
8
- export const YAML_DOCUMENT_START = '---\n';
9
- const READ_BUFFER_SIZE = 64 * 1024;
10
- const LOCKFILE_READ_FLAGS = constants.O_RDONLY | (process.platform === 'win32' ? 0 : constants.O_NOFOLLOW);
11
- /**
12
- * Reads the first YAML document from a multi-document YAML file using streaming.
13
- * The file must start with "---\n" to indicate it contains an env lockfile document.
14
- * Stops reading as soon as the second document separator is found.
15
- * Returns null if the file doesn't exist or doesn't start with "---\n".
16
- */
17
- export async function streamReadFirstYamlDocument(filePath, readBufferSize = READ_BUFFER_SIZE) {
18
- let fileHandle;
19
- let buffer = '';
20
- let firstChunk = true;
21
- try {
22
- fileHandle = await openLockfileNoFollow(filePath);
23
- const decoder = new StringDecoder('utf8');
24
- const readBuffer = Buffer.allocUnsafe(normalizeReadBufferSize(readBufferSize));
25
- let position = 0;
26
- while (true) {
27
- const { bytesRead } = await fileHandle.read(readBuffer, 0, readBuffer.length, position); // eslint-disable-line no-await-in-loop
28
- if (bytesRead === 0)
29
- break;
30
- position += bytesRead;
31
- let chunk = decoder.write(readBuffer.subarray(0, bytesRead));
32
- if (firstChunk && chunk.length > 0) {
33
- // Strip BOM from the first chunk. Safe because the decoder uses utf8,
34
- // so the 3-byte BOM is decoded into a single \uFEFF character.
35
- chunk = stripBom(chunk);
36
- firstChunk = false;
37
- }
38
- buffer += chunk;
39
- // Normalize CRLF (Windows) to LF so document separator detection works.
40
- buffer = buffer.replace(/\r\n/g, '\n');
41
- if (canRejectDocumentStart(buffer)) {
42
- return null;
43
- }
44
- const sep = buffer.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length);
45
- if (sep !== -1) {
46
- return buffer.slice(YAML_DOCUMENT_START.length, sep);
47
- }
48
- }
49
- const remainder = decoder.end();
50
- if (remainder.length > 0) {
51
- buffer += firstChunk ? stripBom(remainder) : remainder;
52
- buffer = buffer.replace(/\r\n/g, '\n');
53
- }
54
- return null;
55
- }
56
- catch (err) {
57
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
58
- return null;
59
- }
60
- throw err;
61
- }
62
- finally {
63
- await fileHandle?.close().catch(() => { });
64
- }
65
- }
66
- export async function readLockfileToStringNoFollow(filePath) {
67
- let fileHandle;
68
- try {
69
- fileHandle = await openLockfileNoFollow(filePath);
70
- return await fileHandle.readFile('utf8');
71
- }
72
- catch (err) {
73
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
74
- return null;
75
- }
76
- throw err;
77
- }
78
- finally {
79
- await fileHandle?.close().catch(() => { });
80
- }
81
- }
82
- async function openLockfileNoFollow(filePath) {
83
- await ensureLockfileIsNotSymlink(filePath);
84
- try {
85
- return await open(filePath, LOCKFILE_READ_FLAGS);
86
- }
87
- catch (err) {
88
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ELOOP') {
89
- throw symlinkedLockfileError(filePath);
90
- }
91
- throw err;
92
- }
93
- }
94
- async function ensureLockfileIsNotSymlink(filePath) {
95
- let stat;
96
- try {
97
- stat = await lstat(filePath);
98
- }
99
- catch (err) {
100
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
101
- return;
102
- }
103
- throw err;
104
- }
105
- if (stat.isSymbolicLink()) {
106
- throw symlinkedLockfileError(filePath);
107
- }
108
- }
109
- function symlinkedLockfileError(filePath) {
110
- return new PnpmError('LOCKFILE_IS_SYMLINK', `Refusing to read or write symlinked lockfile at ${filePath}`);
111
- }
112
- function canRejectDocumentStart(buffer) {
113
- if (buffer.length < YAML_DOCUMENT_START.length)
114
- return false;
115
- if (buffer === '---\r')
116
- return false;
117
- return !buffer.startsWith(YAML_DOCUMENT_START);
118
- }
119
- function normalizeReadBufferSize(readBufferSize) {
120
- const size = Number.isFinite(readBufferSize) ? Math.floor(readBufferSize) : READ_BUFFER_SIZE;
121
- return size > 0 ? size : READ_BUFFER_SIZE;
122
- }
123
- /**
124
- * Extracts the main lockfile content (second YAML document) from a combined string.
125
- * If the file starts with "---\n", returns the content after the separator.
126
- * If there is no separator, returns empty string (file is env-only).
127
- * Otherwise returns the entire content (no env document present).
128
- */
129
- export function extractMainDocument(content) {
130
- content = content.replace(/\r\n/g, '\n');
131
- if (!content.startsWith(YAML_DOCUMENT_START))
132
- return content;
133
- const sep = content.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length);
134
- if (sep === -1)
135
- return '';
136
- return content.slice(sep + YAML_DOCUMENT_SEPARATOR.length);
137
- }
138
- //# sourceMappingURL=yamlDocuments.js.map