@pnpm/lockfile.fs 1100.1.3 → 1100.1.5

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.
@@ -1,17 +1,8 @@
1
1
  import { LOCKFILE_VERSION } from '@pnpm/constants';
2
2
  import { parse, refToRelative, removeSuffix } from '@pnpm/deps.path';
3
+ import { isGitHostedTarballUrl } from '@pnpm/lockfile.utils';
3
4
  import { DEPENDENCIES_FIELDS } from '@pnpm/types';
4
5
  import { isEmpty, map as _mapValues, omit, pick, pickBy } from 'ramda';
5
- // Minimal duplicate of `isGitHostedPkgUrl` from `@pnpm/fetching.pick-fetcher`,
6
- // inlined to avoid pulling the fetcher dep into the lockfile I/O layer. Used
7
- // to enrich entries written by older pnpm versions (which didn't record the
8
- // `gitHosted` field on TarballResolution) so every downstream reader can rely
9
- // on the field directly.
10
- function isGitHostedTarballUrl(url) {
11
- return (url.startsWith('https://codeload.github.com/') ||
12
- url.startsWith('https://bitbucket.org/') ||
13
- url.startsWith('https://gitlab.com/')) && url.includes('tar.gz');
14
- }
15
6
  export function convertToLockfileFile(lockfile) {
16
7
  const packages = {};
17
8
  const snapshots = {};
@@ -6,7 +6,7 @@ export declare const YAML_DOCUMENT_START = "---\n";
6
6
  * Stops reading as soon as the second document separator is found.
7
7
  * Returns null if the file doesn't exist or doesn't start with "---\n".
8
8
  */
9
- export declare function streamReadFirstYamlDocument(filePath: string): Promise<string | null>;
9
+ export declare function streamReadFirstYamlDocument(filePath: string, readBufferSize?: number): Promise<string | null>;
10
10
  /**
11
11
  * Extracts the main lockfile content (second YAML document) from a combined string.
12
12
  * If the file starts with "---\n", returns the content after the separator.
@@ -1,51 +1,54 @@
1
- import { createReadStream } from 'node:fs';
1
+ import { open } from 'node:fs/promises';
2
+ import { StringDecoder } from 'node:string_decoder';
2
3
  import util from 'node:util';
3
4
  import stripBom from 'strip-bom';
4
5
  export const YAML_DOCUMENT_SEPARATOR = '\n---\n';
5
6
  export const YAML_DOCUMENT_START = '---\n';
7
+ const READ_BUFFER_SIZE = 64 * 1024;
6
8
  /**
7
9
  * Reads the first YAML document from a multi-document YAML file using streaming.
8
10
  * The file must start with "---\n" to indicate it contains an env lockfile document.
9
11
  * Stops reading as soon as the second document separator is found.
10
12
  * Returns null if the file doesn't exist or doesn't start with "---\n".
11
13
  */
12
- export async function streamReadFirstYamlDocument(filePath) {
13
- const stream = createReadStream(filePath, { encoding: 'utf8' });
14
- const chunks = stream[Symbol.asyncIterator]();
14
+ export async function streamReadFirstYamlDocument(filePath, readBufferSize = READ_BUFFER_SIZE) {
15
+ let fileHandle;
15
16
  let buffer = '';
17
+ let firstChunk = true;
16
18
  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;
19
+ fileHandle = await open(filePath, 'r');
20
+ const decoder = new StringDecoder('utf8');
21
+ const readBuffer = Buffer.allocUnsafe(normalizeReadBufferSize(readBufferSize));
22
+ let position = 0;
23
+ while (true) {
24
+ const { bytesRead } = await fileHandle.read(readBuffer, 0, readBuffer.length, position); // eslint-disable-line no-await-in-loop
25
+ if (bytesRead === 0)
26
+ break;
27
+ position += bytesRead;
28
+ let chunk = decoder.write(readBuffer.subarray(0, bytesRead));
29
+ if (firstChunk && chunk.length > 0) {
30
+ // Strip BOM from the first chunk. Safe because the decoder uses utf8,
31
+ // so the 3-byte BOM is decoded into a single \uFEFF character.
32
+ chunk = stripBom(chunk);
33
+ firstChunk = false;
26
34
  }
35
+ buffer += chunk;
27
36
  // Normalize CRLF (Windows) to LF so document separator detection works.
28
37
  buffer = buffer.replace(/\r\n/g, '\n');
29
- if (buffer.length >= YAML_DOCUMENT_START.length)
30
- break;
31
- }
32
- if (!buffer.startsWith(YAML_DOCUMENT_START)) {
33
- stream.destroy();
34
- return null;
35
- }
36
- // Phase 2: find the second "---" separator
37
- while (true) {
38
+ if (canRejectDocumentStart(buffer)) {
39
+ return null;
40
+ }
38
41
  const sep = buffer.indexOf(YAML_DOCUMENT_SEPARATOR, YAML_DOCUMENT_START.length);
39
42
  if (sep !== -1) {
40
- stream.destroy();
41
43
  return buffer.slice(YAML_DOCUMENT_START.length, sep);
42
44
  }
43
- const chunk = await chunks.next(); // eslint-disable-line no-await-in-loop
44
- if (chunk.done)
45
- break;
46
- // Normalize CRLF (Windows) to LF so the separator search matches on Windows-checked-out files.
47
- buffer = (buffer + chunk.value).replace(/\r\n/g, '\n');
48
45
  }
46
+ const remainder = decoder.end();
47
+ if (remainder.length > 0) {
48
+ buffer += firstChunk ? stripBom(remainder) : remainder;
49
+ buffer = buffer.replace(/\r\n/g, '\n');
50
+ }
51
+ return null;
49
52
  }
50
53
  catch (err) {
51
54
  if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
@@ -53,8 +56,20 @@ export async function streamReadFirstYamlDocument(filePath) {
53
56
  }
54
57
  throw err;
55
58
  }
56
- stream.destroy();
57
- return null;
59
+ finally {
60
+ await fileHandle?.close().catch(() => { });
61
+ }
62
+ }
63
+ function canRejectDocumentStart(buffer) {
64
+ if (buffer.length < YAML_DOCUMENT_START.length)
65
+ return false;
66
+ if (buffer === '---\r')
67
+ return false;
68
+ return !buffer.startsWith(YAML_DOCUMENT_START);
69
+ }
70
+ function normalizeReadBufferSize(readBufferSize) {
71
+ const size = Number.isFinite(readBufferSize) ? Math.floor(readBufferSize) : READ_BUFFER_SIZE;
72
+ return size > 0 ? size : READ_BUFFER_SIZE;
58
73
  }
59
74
  /**
60
75
  * Extracts the main lockfile content (second YAML document) from a combined string.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/lockfile.fs",
3
- "version": "1100.1.3",
3
+ "version": "1100.1.5",
4
4
  "description": "Read/write pnpm-lock.yaml files",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -34,24 +34,24 @@
34
34
  "js-yaml": "npm:@zkochan/js-yaml@0.0.11",
35
35
  "normalize-path": "^3.0.0",
36
36
  "ramda": "npm:@pnpm/ramda@0.28.1",
37
- "semver": "^7.8.1",
37
+ "semver": "^7.8.4",
38
38
  "strip-bom": "^5.0.0",
39
39
  "write-file-atomic": "^7.0.1",
40
- "@pnpm/constants": "1100.0.0",
41
- "@pnpm/error": "1100.0.0",
42
- "@pnpm/deps.path": "1100.0.6",
43
- "@pnpm/lockfile.types": "1100.0.9",
44
- "@pnpm/lockfile.utils": "1100.0.11",
45
- "@pnpm/types": "1101.3.0",
46
- "@pnpm/object.key-sorting": "1100.0.0",
40
+ "@pnpm/lockfile.utils": "1100.0.13",
41
+ "@pnpm/lockfile.types": "1100.0.11",
42
+ "@pnpm/lockfile.merger": "1100.0.11",
47
43
  "@pnpm/network.git-utils": "1100.0.1",
48
- "@pnpm/lockfile.merger": "1100.0.9"
44
+ "@pnpm/types": "1101.3.2",
45
+ "@pnpm/object.key-sorting": "1100.0.1",
46
+ "@pnpm/deps.path": "1100.0.8",
47
+ "@pnpm/constants": "1100.0.0",
48
+ "@pnpm/error": "1100.0.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "@pnpm/logger": "^1001.0.1"
51
+ "@pnpm/logger": "^1100.0.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@jest/globals": "30.3.0",
54
+ "@jest/globals": "30.4.1",
55
55
  "@types/js-yaml": "^4.0.9",
56
56
  "@types/normalize-path": "^3.0.2",
57
57
  "@types/ramda": "0.31.1",
@@ -61,7 +61,7 @@
61
61
  "write-yaml-file": "^6.0.0",
62
62
  "yaml-tag": "1.1.0",
63
63
  "@pnpm/logger": "1100.0.0",
64
- "@pnpm/lockfile.fs": "1100.1.3"
64
+ "@pnpm/lockfile.fs": "1100.1.5"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=22.13"