@pnpm/lockfile.fs 1001.1.32 → 1100.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/envLockfile.d.ts +4 -0
- package/lib/envLockfile.js +72 -0
- package/lib/errors/LockfileBreakingChangeError.js +2 -6
- package/lib/errors/index.js +1 -5
- package/lib/existsWantedLockfile.d.ts +2 -2
- package/lib/existsWantedLockfile.js +6 -12
- package/lib/getLockfileImporterId.d.ts +1 -1
- package/lib/getLockfileImporterId.js +4 -10
- package/lib/gitBranchLockfile.js +8 -15
- package/lib/gitMergeFile.d.ts +1 -1
- package/lib/gitMergeFile.js +6 -13
- package/lib/index.d.ts +6 -4
- package/lib/index.js +9 -33
- package/lib/lockfileFormatConverters.d.ts +1 -1
- package/lib/lockfileFormatConverters.js +34 -47
- package/lib/lockfileName.js +6 -9
- package/lib/logger.js +2 -5
- package/lib/read.d.ts +2 -2
- package/lib/read.js +52 -63
- package/lib/sortLockfileKeys.d.ts +2 -1
- package/lib/sortLockfileKeys.js +17 -19
- package/lib/write.d.ts +2 -1
- package/lib/write.js +64 -56
- package/lib/yamlDocuments.d.ts +16 -0
- package/lib/yamlDocuments.js +70 -0
- package/package.json +30 -30
- package/lib/errors/LockfileBreakingChangeError.js.map +0 -1
- package/lib/errors/index.js.map +0 -1
- package/lib/existsWantedLockfile.js.map +0 -1
- package/lib/getLockfileImporterId.js.map +0 -1
- package/lib/gitBranchLockfile.js.map +0 -1
- package/lib/gitMergeFile.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/lockfileFormatConverters.js.map +0 -1
- package/lib/lockfileName.js.map +0 -1
- package/lib/logger.js.map +0 -1
- package/lib/read.js.map +0 -1
- package/lib/sortLockfileKeys.js.map +0 -1
- package/lib/write.js.map +0 -1
package/lib/read.js
CHANGED
|
@@ -1,49 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const index_js_1 = require("./errors/index.js");
|
|
21
|
-
const gitMergeFile_js_1 = require("./gitMergeFile.js");
|
|
22
|
-
const logger_js_1 = require("./logger.js");
|
|
23
|
-
const lockfileName_js_1 = require("./lockfileName.js");
|
|
24
|
-
const gitBranchLockfile_js_1 = require("./gitBranchLockfile.js");
|
|
25
|
-
const lockfileFormatConverters_js_1 = require("./lockfileFormatConverters.js");
|
|
26
|
-
async function readCurrentLockfile(pnpmInternalDir, opts) {
|
|
27
|
-
const lockfilePath = path_1.default.join(pnpmInternalDir, 'lock.yaml');
|
|
1
|
+
import { promises as fs } 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 { 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');
|
|
28
20
|
return (await _read(lockfilePath, pnpmInternalDir, opts)).lockfile;
|
|
29
21
|
}
|
|
30
|
-
async function readWantedLockfileAndAutofixConflicts(pkgPath, opts) {
|
|
22
|
+
export async function readWantedLockfileAndAutofixConflicts(pkgPath, opts) {
|
|
31
23
|
return _readWantedLockfile(pkgPath, {
|
|
32
24
|
...opts,
|
|
33
25
|
autofixMergeConflicts: true,
|
|
34
26
|
});
|
|
35
27
|
}
|
|
36
|
-
async function readWantedLockfile(pkgPath, opts) {
|
|
28
|
+
export async function readWantedLockfile(pkgPath, opts) {
|
|
37
29
|
return (await _readWantedLockfile(pkgPath, opts)).lockfile;
|
|
38
30
|
}
|
|
39
31
|
async function _read(lockfilePath, prefix, // only for logging
|
|
40
32
|
opts) {
|
|
41
33
|
let lockfileRawContent;
|
|
42
34
|
try {
|
|
43
|
-
lockfileRawContent = (
|
|
35
|
+
lockfileRawContent = stripBom(await fs.readFile(lockfilePath, 'utf8'));
|
|
44
36
|
}
|
|
45
37
|
catch (err) {
|
|
46
|
-
if (!(
|
|
38
|
+
if (!(util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT')) {
|
|
47
39
|
throw err;
|
|
48
40
|
}
|
|
49
41
|
return {
|
|
@@ -51,36 +43,41 @@ opts) {
|
|
|
51
43
|
hadConflicts: false,
|
|
52
44
|
};
|
|
53
45
|
}
|
|
54
|
-
// Skip the
|
|
55
|
-
// The first document is the env lockfile; the second is the actual lockfile.
|
|
46
|
+
// Skip the env lockfile document if present (first document in combined format)
|
|
56
47
|
lockfileRawContent = extractMainDocument(lockfileRawContent);
|
|
48
|
+
if (!lockfileRawContent.trim()) {
|
|
49
|
+
return {
|
|
50
|
+
lockfile: null,
|
|
51
|
+
hadConflicts: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
57
54
|
let lockfile;
|
|
58
55
|
let hadConflicts;
|
|
59
56
|
try {
|
|
60
|
-
lockfile =
|
|
57
|
+
lockfile = convertToLockfileObject(yaml.load(lockfileRawContent)); // eslint-disable-line
|
|
61
58
|
hadConflicts = false;
|
|
62
59
|
}
|
|
63
60
|
catch (err) {
|
|
64
|
-
if (!opts.autofixMergeConflicts || !
|
|
65
|
-
throw new
|
|
61
|
+
if (!opts.autofixMergeConflicts || !isDiff(lockfileRawContent)) {
|
|
62
|
+
throw new PnpmError('BROKEN_LOCKFILE', `The lockfile at "${lockfilePath}" is broken: ${err.message}`);
|
|
66
63
|
}
|
|
67
64
|
hadConflicts = true;
|
|
68
|
-
lockfile =
|
|
69
|
-
|
|
70
|
-
message: `Merge conflict detected in ${
|
|
65
|
+
lockfile = autofixMergeConflicts(lockfileRawContent);
|
|
66
|
+
logger.info({
|
|
67
|
+
message: `Merge conflict detected in ${WANTED_LOCKFILE} and successfully merged`,
|
|
71
68
|
prefix,
|
|
72
69
|
});
|
|
73
70
|
}
|
|
74
71
|
if (lockfile) {
|
|
75
|
-
const lockfileSemver = (
|
|
72
|
+
const lockfileSemver = comverToSemver((lockfile.lockfileVersion ?? 0).toString());
|
|
76
73
|
if (!opts.wantedVersions ||
|
|
77
74
|
opts.wantedVersions.length === 0 ||
|
|
78
75
|
opts.wantedVersions.some((wantedVersion) => {
|
|
79
|
-
if (
|
|
76
|
+
if (semver.major(lockfileSemver) !== semver.major(comverToSemver(wantedVersion)))
|
|
80
77
|
return false;
|
|
81
|
-
if (lockfile.lockfileVersion !== '6.1' &&
|
|
82
|
-
|
|
83
|
-
message: `Your ${
|
|
78
|
+
if (lockfile.lockfileVersion !== '6.1' && semver.gt(lockfileSemver, comverToSemver(wantedVersion))) {
|
|
79
|
+
logger.warn({
|
|
80
|
+
message: `Your ${WANTED_LOCKFILE} was generated by a newer version of pnpm. ` +
|
|
84
81
|
`It is a compatible version but it might get downgraded to version ${wantedVersion}`,
|
|
85
82
|
prefix,
|
|
86
83
|
});
|
|
@@ -91,15 +88,15 @@ opts) {
|
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
if (opts.ignoreIncompatible) {
|
|
94
|
-
|
|
91
|
+
logger.warn({
|
|
95
92
|
message: `Ignoring not compatible lockfile at ${lockfilePath}`,
|
|
96
93
|
prefix,
|
|
97
94
|
});
|
|
98
95
|
return { lockfile: null, hadConflicts: false };
|
|
99
96
|
}
|
|
100
|
-
throw new
|
|
97
|
+
throw new LockfileBreakingChangeError(lockfilePath);
|
|
101
98
|
}
|
|
102
|
-
function createLockfileObject(importerIds, opts) {
|
|
99
|
+
export function createLockfileObject(importerIds, opts) {
|
|
103
100
|
const importers = {};
|
|
104
101
|
for (const importerId of importerIds) {
|
|
105
102
|
importers[importerId] = {
|
|
@@ -109,7 +106,7 @@ function createLockfileObject(importerIds, opts) {
|
|
|
109
106
|
}
|
|
110
107
|
return {
|
|
111
108
|
importers,
|
|
112
|
-
lockfileVersion: opts.lockfileVersion ||
|
|
109
|
+
lockfileVersion: opts.lockfileVersion || LOCKFILE_VERSION,
|
|
113
110
|
settings: {
|
|
114
111
|
autoInstallPeers: opts.autoInstallPeers,
|
|
115
112
|
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
|
@@ -118,17 +115,17 @@ function createLockfileObject(importerIds, opts) {
|
|
|
118
115
|
};
|
|
119
116
|
}
|
|
120
117
|
async function _readWantedLockfile(pkgPath, opts) {
|
|
121
|
-
const lockfileNames = [
|
|
118
|
+
const lockfileNames = [WANTED_LOCKFILE];
|
|
122
119
|
if (opts.useGitBranchLockfile) {
|
|
123
|
-
const gitBranchLockfileName = await
|
|
124
|
-
if (gitBranchLockfileName !==
|
|
120
|
+
const gitBranchLockfileName = await getWantedLockfileName(opts);
|
|
121
|
+
if (gitBranchLockfileName !== WANTED_LOCKFILE) {
|
|
125
122
|
lockfileNames.unshift(gitBranchLockfileName);
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
let result = { lockfile: null, hadConflicts: false };
|
|
129
126
|
/* eslint-disable no-await-in-loop */
|
|
130
127
|
for (const lockfileName of lockfileNames) {
|
|
131
|
-
result = await _read(
|
|
128
|
+
result = await _read(path.join(pkgPath, lockfileName), pkgPath, { ...opts, autofixMergeConflicts: true });
|
|
132
129
|
if (result.lockfile) {
|
|
133
130
|
if (opts.mergeGitBranchLockfiles) {
|
|
134
131
|
result.lockfile = await _mergeGitBranchLockfiles(result.lockfile, pkgPath, pkgPath, opts);
|
|
@@ -149,20 +146,12 @@ async function _mergeGitBranchLockfiles(lockfile, lockfileDir, prefix, opts) {
|
|
|
149
146
|
if (!gitBranchLockfile) {
|
|
150
147
|
continue;
|
|
151
148
|
}
|
|
152
|
-
mergedLockfile =
|
|
149
|
+
mergedLockfile = mergeLockfileChanges(mergedLockfile, gitBranchLockfile);
|
|
153
150
|
}
|
|
154
151
|
return mergedLockfile;
|
|
155
152
|
}
|
|
156
153
|
async function _readGitBranchLockfiles(lockfileDir, prefix, opts) {
|
|
157
|
-
const files = await
|
|
158
|
-
return Promise.all(files.map((file) => _read(
|
|
159
|
-
}
|
|
160
|
-
function extractMainDocument(content) {
|
|
161
|
-
if (!content.startsWith('---\n'))
|
|
162
|
-
return content;
|
|
163
|
-
const sep = content.indexOf('\n---\n', '---\n'.length);
|
|
164
|
-
if (sep === -1)
|
|
165
|
-
return content;
|
|
166
|
-
return content.slice(sep + '\n---\n'.length);
|
|
154
|
+
const files = await getGitBranchLockfileNames(lockfileDir);
|
|
155
|
+
return Promise.all(files.map((file) => _read(path.join(lockfileDir, file), prefix, opts)));
|
|
167
156
|
}
|
|
168
157
|
//# sourceMappingURL=read.js.map
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { EnvLockfile, LockfileFile } from '@pnpm/lockfile.types';
|
|
2
2
|
export declare function sortLockfileKeys(lockfile: LockfileFile): LockfileFile;
|
|
3
|
+
export declare function sortLockfileKeys(lockfile: EnvLockfile): EnvLockfile;
|
package/lib/sortLockfileKeys.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sortLockfileKeys = sortLockfileKeys;
|
|
4
|
-
const object_key_sorting_1 = require("@pnpm/object.key-sorting");
|
|
1
|
+
import { sortDeepKeys, sortDirectKeys, sortKeysByPriority } from '@pnpm/object.key-sorting';
|
|
5
2
|
const ORDERED_KEYS = {
|
|
6
3
|
resolution: 1,
|
|
7
4
|
id: 2,
|
|
@@ -36,45 +33,46 @@ const ROOT_KEYS = [
|
|
|
36
33
|
'packages',
|
|
37
34
|
];
|
|
38
35
|
const ROOT_KEYS_ORDER = Object.fromEntries(ROOT_KEYS.map((key, index) => [key, index]));
|
|
39
|
-
function sortLockfileKeys(lockfile) {
|
|
36
|
+
export function sortLockfileKeys(lockfile) {
|
|
40
37
|
if (lockfile.importers != null) {
|
|
41
|
-
lockfile.importers =
|
|
38
|
+
lockfile.importers = sortDirectKeys(lockfile.importers);
|
|
42
39
|
for (const [importerId, importer] of Object.entries(lockfile.importers)) {
|
|
43
|
-
lockfile.importers[importerId] =
|
|
40
|
+
lockfile.importers[importerId] = sortKeysByPriority({
|
|
44
41
|
priority: ROOT_KEYS_ORDER,
|
|
45
42
|
deep: true,
|
|
46
43
|
}, importer);
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
if (lockfile.packages != null) {
|
|
50
|
-
lockfile.packages =
|
|
47
|
+
lockfile.packages = sortDirectKeys(lockfile.packages);
|
|
51
48
|
for (const [pkgId, pkg] of Object.entries(lockfile.packages)) {
|
|
52
|
-
lockfile.packages[pkgId] =
|
|
49
|
+
lockfile.packages[pkgId] = sortKeysByPriority({
|
|
53
50
|
priority: ORDERED_KEYS,
|
|
54
51
|
deep: true,
|
|
55
52
|
}, pkg);
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
if (lockfile.snapshots != null) {
|
|
59
|
-
lockfile.snapshots =
|
|
56
|
+
lockfile.snapshots = sortDirectKeys(lockfile.snapshots);
|
|
60
57
|
for (const [pkgId, pkg] of Object.entries(lockfile.snapshots)) {
|
|
61
|
-
lockfile.snapshots[pkgId] =
|
|
58
|
+
lockfile.snapshots[pkgId] = sortKeysByPriority({
|
|
62
59
|
priority: ORDERED_KEYS,
|
|
63
60
|
deep: true,
|
|
64
61
|
}, pkg);
|
|
65
62
|
}
|
|
66
63
|
}
|
|
67
|
-
if (lockfile.catalogs != null) {
|
|
68
|
-
lockfile.catalogs =
|
|
64
|
+
if ('catalogs' in lockfile && lockfile.catalogs != null) {
|
|
65
|
+
lockfile.catalogs = sortDirectKeys(lockfile.catalogs);
|
|
69
66
|
for (const [catalogName, catalog] of Object.entries(lockfile.catalogs)) {
|
|
70
|
-
lockfile.catalogs[catalogName] =
|
|
67
|
+
lockfile.catalogs[catalogName] = sortDeepKeys(catalog);
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
continue;
|
|
76
|
-
lockfile[key] = (0, object_key_sorting_1.sortDirectKeys)(lockfile[key]); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
70
|
+
if ('time' in lockfile && lockfile.time != null) {
|
|
71
|
+
lockfile.time = sortDirectKeys(lockfile.time);
|
|
77
72
|
}
|
|
78
|
-
|
|
73
|
+
if ('patchedDependencies' in lockfile && lockfile.patchedDependencies != null) {
|
|
74
|
+
lockfile.patchedDependencies = sortDirectKeys(lockfile.patchedDependencies);
|
|
75
|
+
}
|
|
76
|
+
return sortKeysByPriority({ priority: ROOT_KEYS_ORDER }, lockfile);
|
|
79
77
|
}
|
|
80
78
|
//# sourceMappingURL=sortLockfileKeys.js.map
|
package/lib/write.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { LockfileFile, LockfileObject } from '@pnpm/lockfile.types';
|
|
2
|
+
export declare function lockfileYamlDump(obj: object): string;
|
|
2
3
|
export declare function writeWantedLockfile(pkgPath: string, wantedLockfile: LockfileObject, opts?: {
|
|
3
4
|
useGitBranchLockfile?: boolean;
|
|
4
5
|
mergeGitBranchLockfiles?: boolean;
|
package/lib/write.js
CHANGED
|
@@ -1,105 +1,113 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const constants_1 = require("@pnpm/constants");
|
|
14
|
-
const rimraf_1 = __importDefault(require("@zkochan/rimraf"));
|
|
15
|
-
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
16
|
-
const isEmpty_1 = __importDefault(require("ramda/src/isEmpty"));
|
|
17
|
-
const write_file_atomic_1 = __importDefault(require("write-file-atomic"));
|
|
18
|
-
const logger_js_1 = require("./logger.js");
|
|
19
|
-
const sortLockfileKeys_js_1 = require("./sortLockfileKeys.js");
|
|
20
|
-
const lockfileName_js_1 = require("./lockfileName.js");
|
|
21
|
-
const lockfileFormatConverters_js_1 = require("./lockfileFormatConverters.js");
|
|
22
|
-
async function writeFileAtomic(filename, data) {
|
|
23
|
-
return new Promise((resolve, reject) => {
|
|
24
|
-
(0, write_file_atomic_1.default)(filename, data, {}, (err) => {
|
|
25
|
-
(err != null) ? reject(err) : resolve();
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
}
|
|
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 } 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';
|
|
29
13
|
const LOCKFILE_YAML_FORMAT = {
|
|
30
14
|
blankLines: true,
|
|
31
|
-
lineWidth: -1,
|
|
15
|
+
lineWidth: -1,
|
|
32
16
|
noCompatMode: true,
|
|
33
17
|
noRefs: true,
|
|
34
18
|
sortKeys: false,
|
|
35
19
|
};
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
export function lockfileYamlDump(obj) {
|
|
21
|
+
return yaml.dump(obj, LOCKFILE_YAML_FORMAT);
|
|
22
|
+
}
|
|
23
|
+
export async function writeWantedLockfile(pkgPath, wantedLockfile, opts) {
|
|
24
|
+
const wantedLockfileName = await getWantedLockfileName(opts);
|
|
38
25
|
return writeLockfile(wantedLockfileName, pkgPath, wantedLockfile);
|
|
39
26
|
}
|
|
40
|
-
async function writeCurrentLockfile(virtualStoreDir, currentLockfile) {
|
|
27
|
+
export async function writeCurrentLockfile(virtualStoreDir, currentLockfile) {
|
|
41
28
|
// empty lockfile is not saved
|
|
42
29
|
if (isEmptyLockfile(currentLockfile)) {
|
|
43
|
-
await (
|
|
30
|
+
await rimraf(path.join(virtualStoreDir, 'lock.yaml'));
|
|
44
31
|
return;
|
|
45
32
|
}
|
|
46
|
-
await
|
|
33
|
+
await fs.mkdir(virtualStoreDir, { recursive: true });
|
|
47
34
|
return writeLockfile('lock.yaml', virtualStoreDir, currentLockfile);
|
|
48
35
|
}
|
|
49
36
|
async function writeLockfile(lockfileFilename, pkgPath, wantedLockfile) {
|
|
50
|
-
const lockfilePath =
|
|
51
|
-
const lockfileToStringify =
|
|
52
|
-
|
|
37
|
+
const lockfilePath = path.join(pkgPath, lockfileFilename);
|
|
38
|
+
const lockfileToStringify = convertToLockfileFile(wantedLockfile);
|
|
39
|
+
const yamlDoc = yamlStringify(lockfileToStringify);
|
|
40
|
+
if (lockfileFilename === WANTED_LOCKFILE) {
|
|
41
|
+
// Re-read the env document from the existing lockfile to preserve it.
|
|
42
|
+
// Ideally the env document would be captured during the initial lockfile read
|
|
43
|
+
// and passed through to the write functions, but that would require threading it
|
|
44
|
+
// through 25+ call sites. Re-reading is cheap since the file is likely still
|
|
45
|
+
// in the OS page cache and streaming stops at the first separator.
|
|
46
|
+
const envDoc = await streamReadFirstYamlDocument(lockfilePath);
|
|
47
|
+
const envPrefix = envDoc != null ? `${YAML_DOCUMENT_START}${envDoc}${YAML_DOCUMENT_SEPARATOR}` : '';
|
|
48
|
+
return writeFileAtomic(lockfilePath, `${envPrefix}${yamlDoc}`);
|
|
49
|
+
}
|
|
50
|
+
return writeFileAtomic(lockfilePath, yamlDoc);
|
|
53
51
|
}
|
|
54
|
-
function writeLockfileFile(lockfilePath, wantedLockfile) {
|
|
52
|
+
export function writeLockfileFile(lockfilePath, wantedLockfile) {
|
|
55
53
|
const yamlDoc = yamlStringify(wantedLockfile);
|
|
56
54
|
return writeFileAtomic(lockfilePath, yamlDoc);
|
|
57
55
|
}
|
|
58
56
|
function yamlStringify(lockfile) {
|
|
59
|
-
const sortedLockfile =
|
|
60
|
-
return
|
|
57
|
+
const sortedLockfile = sortLockfileKeys(lockfile);
|
|
58
|
+
return lockfileYamlDump(sortedLockfile);
|
|
61
59
|
}
|
|
62
|
-
function isEmptyLockfile(lockfile) {
|
|
63
|
-
return Object.values(lockfile.importers).every((importer) => (
|
|
60
|
+
export function isEmptyLockfile(lockfile) {
|
|
61
|
+
return Object.values(lockfile.importers).every((importer) => isEmpty(importer.specifiers ?? {}) && isEmpty(importer.dependencies ?? {}));
|
|
64
62
|
}
|
|
65
|
-
async function writeLockfiles(opts) {
|
|
66
|
-
const wantedLockfileName = await
|
|
67
|
-
const wantedLockfilePath =
|
|
68
|
-
const currentLockfilePath =
|
|
69
|
-
const wantedLockfileToStringify =
|
|
63
|
+
export async function writeLockfiles(opts) {
|
|
64
|
+
const wantedLockfileName = await getWantedLockfileName(opts);
|
|
65
|
+
const wantedLockfilePath = path.join(opts.wantedLockfileDir, wantedLockfileName);
|
|
66
|
+
const currentLockfilePath = path.join(opts.currentLockfileDir, 'lock.yaml');
|
|
67
|
+
const wantedLockfileToStringify = convertToLockfileFile(opts.wantedLockfile);
|
|
70
68
|
const yamlDoc = yamlStringify(wantedLockfileToStringify);
|
|
69
|
+
// Preserve the env lockfile document at the top of pnpm-lock.yaml
|
|
70
|
+
let envPrefix = '';
|
|
71
|
+
if (wantedLockfileName === WANTED_LOCKFILE) {
|
|
72
|
+
const envDoc = await streamReadFirstYamlDocument(wantedLockfilePath);
|
|
73
|
+
if (envDoc != null) {
|
|
74
|
+
envPrefix = `${YAML_DOCUMENT_START}${envDoc}${YAML_DOCUMENT_SEPARATOR}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const wantedYamlDoc = `${envPrefix}${yamlDoc}`;
|
|
71
78
|
// in most cases the `pnpm-lock.yaml` and `node_modules/.pnpm-lock.yaml` are equal
|
|
72
79
|
// in those cases the YAML document can be stringified only once for both files
|
|
73
80
|
// which is more efficient
|
|
74
81
|
if (opts.wantedLockfile === opts.currentLockfile) {
|
|
75
82
|
await Promise.all([
|
|
76
|
-
writeFileAtomic(wantedLockfilePath,
|
|
83
|
+
writeFileAtomic(wantedLockfilePath, wantedYamlDoc),
|
|
77
84
|
(async () => {
|
|
78
85
|
if (isEmptyLockfile(opts.wantedLockfile)) {
|
|
79
|
-
await (
|
|
86
|
+
await rimraf(currentLockfilePath);
|
|
80
87
|
}
|
|
81
88
|
else {
|
|
82
|
-
await
|
|
89
|
+
await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
|
|
90
|
+
// Current lockfile (node_modules/.pnpm/lock.yaml) does not include the env document
|
|
83
91
|
await writeFileAtomic(currentLockfilePath, yamlDoc);
|
|
84
92
|
}
|
|
85
93
|
})(),
|
|
86
94
|
]);
|
|
87
95
|
return;
|
|
88
96
|
}
|
|
89
|
-
|
|
90
|
-
message: `\`${
|
|
97
|
+
logger.debug({
|
|
98
|
+
message: `\`${WANTED_LOCKFILE}\` differs from \`${path.relative(opts.wantedLockfileDir, currentLockfilePath)}\``,
|
|
91
99
|
prefix: opts.wantedLockfileDir,
|
|
92
100
|
});
|
|
93
|
-
const currentLockfileToStringify =
|
|
101
|
+
const currentLockfileToStringify = convertToLockfileFile(opts.currentLockfile);
|
|
94
102
|
const currentYamlDoc = yamlStringify(currentLockfileToStringify);
|
|
95
103
|
await Promise.all([
|
|
96
|
-
writeFileAtomic(wantedLockfilePath,
|
|
104
|
+
writeFileAtomic(wantedLockfilePath, wantedYamlDoc),
|
|
97
105
|
(async () => {
|
|
98
106
|
if (isEmptyLockfile(opts.wantedLockfile)) {
|
|
99
|
-
await (
|
|
107
|
+
await rimraf(currentLockfilePath);
|
|
100
108
|
}
|
|
101
109
|
else {
|
|
102
|
-
await
|
|
110
|
+
await fs.mkdir(path.dirname(currentLockfilePath), { recursive: true });
|
|
103
111
|
await writeFileAtomic(currentLockfilePath, currentYamlDoc);
|
|
104
112
|
}
|
|
105
113
|
})(),
|
|
@@ -0,0 +1,16 @@
|
|
|
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): Promise<string | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the main lockfile content (second YAML document) from a combined string.
|
|
12
|
+
* If the file starts with "---\n", returns the content after the separator.
|
|
13
|
+
* If there is no separator, returns empty string (file is env-only).
|
|
14
|
+
* Otherwise returns the entire content (no env document present).
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractMainDocument(content: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import util from 'node:util';
|
|
3
|
+
import stripBom from 'strip-bom';
|
|
4
|
+
export const YAML_DOCUMENT_SEPARATOR = '\n---\n';
|
|
5
|
+
export const YAML_DOCUMENT_START = '---\n';
|
|
6
|
+
/**
|
|
7
|
+
* Reads the first YAML document from a multi-document YAML file using streaming.
|
|
8
|
+
* The file must start with "---\n" to indicate it contains an env lockfile document.
|
|
9
|
+
* Stops reading as soon as the second document separator is found.
|
|
10
|
+
* Returns null if the file doesn't exist or doesn't start with "---\n".
|
|
11
|
+
*/
|
|
12
|
+
export async function streamReadFirstYamlDocument(filePath) {
|
|
13
|
+
const stream = createReadStream(filePath, { encoding: 'utf8' });
|
|
14
|
+
const chunks = stream[Symbol.asyncIterator]();
|
|
15
|
+
let buffer = '';
|
|
16
|
+
try {
|
|
17
|
+
// Phase 1: verify the file starts with "---\n"
|
|
18
|
+
for (let chunk = await chunks.next(); !chunk.done; chunk = await chunks.next()) { // eslint-disable-line no-await-in-loop
|
|
19
|
+
if (buffer.length === 0) {
|
|
20
|
+
// Strip BOM from the first chunk. Safe because the stream uses utf8 encoding,
|
|
21
|
+
// so the 3-byte BOM is decoded into a single \uFEFF character in the first chunk.
|
|
22
|
+
buffer = stripBom(chunk.value);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
buffer += chunk.value;
|
|
26
|
+
}
|
|
27
|
+
if (buffer.length >= YAML_DOCUMENT_START.length)
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
if (!buffer.startsWith(YAML_DOCUMENT_START)) {
|
|
31
|
+
stream.destroy();
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Phase 2: find the second "---" separator
|
|
35
|
+
while (true) {
|
|
36
|
+
const sep = buffer.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length);
|
|
37
|
+
if (sep !== -1) {
|
|
38
|
+
stream.destroy();
|
|
39
|
+
return buffer.slice(YAML_DOCUMENT_START.length, sep);
|
|
40
|
+
}
|
|
41
|
+
const chunk = await chunks.next(); // eslint-disable-line no-await-in-loop
|
|
42
|
+
if (chunk.done)
|
|
43
|
+
break;
|
|
44
|
+
buffer += chunk.value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
stream.destroy();
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Extracts the main lockfile content (second YAML document) from a combined string.
|
|
58
|
+
* If the file starts with "---\n", returns the content after the separator.
|
|
59
|
+
* If there is no separator, returns empty string (file is env-only).
|
|
60
|
+
* Otherwise returns the entire content (no env document present).
|
|
61
|
+
*/
|
|
62
|
+
export function extractMainDocument(content) {
|
|
63
|
+
if (!content.startsWith(YAML_DOCUMENT_START))
|
|
64
|
+
return content;
|
|
65
|
+
const sep = content.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length);
|
|
66
|
+
if (sep === -1)
|
|
67
|
+
return '';
|
|
68
|
+
return content.slice(sep + YAML_DOCUMENT_SEPARATOR.length);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=yamlDocuments.js.map
|