@pnpm/fetching.directory-fetcher 1100.0.34 → 1100.0.36

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,36 @@
1
1
  # @pnpm/directory-fetcher
2
2
 
3
+ ## 1100.0.36
4
+
5
+ ### Patch Changes
6
+
7
+ - `pnpm deploy` with a shared lockfile now copies workspace dependencies into the deploy directory, even when `packageImportMethod` is set to `hardlink`. Previously, their files were hard-linked to the workspace sources, so editing a source file also changed the deployed copy [pnpm/pnpm#12176](https://github.com/pnpm/pnpm/issues/12176).
8
+
9
+ - `pnpm install` no longer fails for an injected workspace dependency whose package publishes from a `publishConfig.directory` that its own `prepare` script builds. The injected copy now picks up that directory once `prepare` finishes building it. `pnpm install --frozen-lockfile` no longer reports the dependency as outdated while the directory has not been built yet. [pnpm/pnpm#7811](https://github.com/pnpm/pnpm/issues/7811)
10
+
11
+ - `pnpm install` now works in StackBlitz WebContainers on projects without a lockfile. It used to fail there with `ENOENT ... pnpm-lock.yaml`, because pnpm did not recognize the errors that WebContainers return from asynchronous file system calls [#15649](https://github.com/pnpm/pnpm/issues/15649).
12
+
13
+ - Updated dependencies:
14
+ - @pnpm/building.pkg-requires-build@1100.0.19
15
+ - @pnpm/error@1100.2.1
16
+ - @pnpm/fetching.fetcher-base@1100.2.12
17
+ - @pnpm/fs.packlist@1100.0.6
18
+ - @pnpm/resolving.resolver-base@1101.3.2
19
+ - @pnpm/store.cafs-types@1100.1.1
20
+ - @pnpm/workspace.project-manifest-reader@1100.1.1
21
+
22
+ ## 1100.0.35
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies:
27
+ - @pnpm/building.pkg-requires-build@1100.0.18
28
+ - @pnpm/fetching.fetcher-base@1100.2.11
29
+ - @pnpm/fs.packlist@1100.0.5
30
+ - @pnpm/resolving.resolver-base@1101.3.1
31
+ - @pnpm/types@1102.1.1
32
+ - @pnpm/workspace.project-manifest-reader@1100.1.0
33
+
3
34
  ## 1100.0.34
4
35
 
5
36
  ### Patch Changes
package/lib/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { type Stats } from 'node:fs';
2
- import type { DirectoryFetcher, DirectoryFetcherOptions } from '@pnpm/fetching.fetcher-base';
2
+ import type { DirectoryFetcher, DirectoryFetcherOptions, LocalDirPackageImportMethod } from '@pnpm/fetching.fetcher-base';
3
3
  import type { FilesMap } from '@pnpm/store.cafs-types';
4
4
  import type { DependencyManifest } from '@pnpm/types';
5
5
  export interface CreateDirectoryFetcherOptions {
6
6
  includeOnlyPackageFiles?: boolean;
7
+ localDirPackageImportMethod?: LocalDirPackageImportMethod;
7
8
  resolveSymlinks?: boolean;
8
9
  }
9
10
  export declare function createDirectoryFetcher(opts?: CreateDirectoryFetcherOptions): {
@@ -14,7 +15,7 @@ export interface FetchResult {
14
15
  local: true;
15
16
  filesMap: FilesMap;
16
17
  filesStats?: Record<string, Stats | null>;
17
- packageImportMethod: 'hardlink';
18
+ packageImportMethod: LocalDirPackageImportMethod;
18
19
  manifest: DependencyManifest;
19
20
  requiresBuild: boolean;
20
21
  }
package/lib/index.js CHANGED
@@ -1,33 +1,70 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
- import util from 'node:util';
4
3
  import { pkgRequiresBuild } from '@pnpm/building.pkg-requires-build';
4
+ import { isError } from '@pnpm/error';
5
5
  import { packlist } from '@pnpm/fs.packlist';
6
6
  import { logger } from '@pnpm/logger';
7
- import { safeReadProjectManifestOnly } from '@pnpm/workspace.project-manifest-reader';
7
+ import { safeReadParentPublishManifest, safeReadProjectManifestOnly } from '@pnpm/workspace.project-manifest-reader';
8
8
  const directoryFetcherLogger = logger('directory-fetcher');
9
9
  export function createDirectoryFetcher(opts) {
10
10
  const readFileStat = opts?.resolveSymlinks === true ? realFileStat : fileStat;
11
- const fetchFromDir = opts?.includeOnlyPackageFiles ? fetchPackageFilesFromDir : fetchAllFilesFromDir.bind(null, readFileStat);
12
- const directoryFetcher = (cafs, resolution, opts) => {
11
+ const packageImportMethod = opts?.localDirPackageImportMethod ?? 'hardlink';
12
+ const fetchFromDir = opts?.includeOnlyPackageFiles
13
+ ? fetchPackageFilesFromDir.bind(null, packageImportMethod)
14
+ : fetchAllFilesFromDir.bind(null, readFileStat, packageImportMethod);
15
+ const directoryFetcher = async (cafs, resolution, opts) => {
13
16
  // Use path.resolve so absolute directories (e.g. cross-drive Windows paths
14
17
  // stored by `file:` deps) are respected instead of being concatenated
15
18
  // onto lockfileDir.
16
19
  const dir = path.resolve(opts.lockfileDir, resolution.directory);
17
- return fetchFromDir(dir);
20
+ // An injected dependency whose packed content is the output of its own
21
+ // lifecycle scripts (a project with `publishConfig.directory` built by
22
+ // `prepare`) has no source directory on a fresh install: the scripts run
23
+ // after linking, and the built output is imported afterwards. Inject an
24
+ // empty copy so the install can proceed instead of failing on the
25
+ // not-yet-built directory. Any other missing directory still fails.
26
+ if (!await dirExists(dir)) {
27
+ const manifest = await safeReadParentPublishManifest(dir);
28
+ if (manifest != null) {
29
+ return {
30
+ local: true,
31
+ filesMap: new Map(),
32
+ packageImportMethod,
33
+ manifest,
34
+ requiresBuild: false,
35
+ sourceExists: false,
36
+ };
37
+ }
38
+ }
39
+ return {
40
+ ...await fetchFromDir(dir),
41
+ sourceExists: true,
42
+ };
18
43
  };
19
44
  return {
20
45
  directory: directoryFetcher,
21
46
  };
22
47
  }
48
+ async function dirExists(dir) {
49
+ try {
50
+ await fs.stat(dir);
51
+ return true;
52
+ }
53
+ catch (err) {
54
+ if (isError(err) && 'code' in err && err.code === 'ENOENT')
55
+ return false;
56
+ throw err;
57
+ }
58
+ }
23
59
  export async function fetchFromDir(dir, opts) {
60
+ const packageImportMethod = opts.localDirPackageImportMethod ?? 'hardlink';
24
61
  if (opts.includeOnlyPackageFiles) {
25
- return fetchPackageFilesFromDir(dir);
62
+ return fetchPackageFilesFromDir(packageImportMethod, dir);
26
63
  }
27
64
  const readFileStat = opts?.resolveSymlinks === true ? realFileStat : fileStat;
28
- return fetchAllFilesFromDir(readFileStat, dir);
65
+ return fetchAllFilesFromDir(readFileStat, packageImportMethod, dir);
29
66
  }
30
- async function fetchAllFilesFromDir(readFileStat, dir) {
67
+ async function fetchAllFilesFromDir(readFileStat, packageImportMethod, dir) {
31
68
  const { filesMap, filesStats } = await _fetchAllFilesFromDir(readFileStat, dir);
32
69
  // In a regular pnpm workspace it will probably never happen that a dependency has no package.json file.
33
70
  // Safe read was added to support the Bit workspace in which the components have no package.json files.
@@ -38,7 +75,7 @@ async function fetchAllFilesFromDir(readFileStat, dir) {
38
75
  local: true,
39
76
  filesMap,
40
77
  filesStats,
41
- packageImportMethod: 'hardlink',
78
+ packageImportMethod,
42
79
  manifest,
43
80
  requiresBuild,
44
81
  };
@@ -81,7 +118,7 @@ async function realFileStat(filePath) {
81
118
  }
82
119
  catch (err) {
83
120
  // Broken symlinks are skipped
84
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
121
+ if (isError(err) && 'code' in err && err.code === 'ENOENT') {
85
122
  directoryFetcherLogger.debug({ brokenSymlink: filePath });
86
123
  return null;
87
124
  }
@@ -97,14 +134,14 @@ async function fileStat(filePath) {
97
134
  }
98
135
  catch (err) {
99
136
  // Broken symlinks are skipped
100
- if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
137
+ if (isError(err) && 'code' in err && err.code === 'ENOENT') {
101
138
  directoryFetcherLogger.debug({ brokenSymlink: filePath });
102
139
  return null;
103
140
  }
104
141
  throw err;
105
142
  }
106
143
  }
107
- async function fetchPackageFilesFromDir(dir) {
144
+ async function fetchPackageFilesFromDir(packageImportMethod, dir) {
108
145
  const files = await packlist(dir);
109
146
  const filesMap = new Map(files.map((file) => [file, path.join(dir, file)]));
110
147
  // In a regular pnpm workspace it will probably never happen that a dependency has no package.json file.
@@ -115,7 +152,7 @@ async function fetchPackageFilesFromDir(dir) {
115
152
  return {
116
153
  local: true,
117
154
  filesMap,
118
- packageImportMethod: 'hardlink',
155
+ packageImportMethod,
119
156
  manifest,
120
157
  requiresBuild,
121
158
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/fetching.directory-fetcher",
3
- "version": "1100.0.34",
3
+ "version": "1100.0.36",
4
4
  "description": "A fetcher for local directory packages",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -28,20 +28,21 @@
28
28
  "!*.map"
29
29
  ],
30
30
  "dependencies": {
31
- "@pnpm/building.pkg-requires-build": "1100.0.17",
32
- "@pnpm/fetching.fetcher-base": "1100.2.10",
33
- "@pnpm/fs.packlist": "1100.0.4",
34
- "@pnpm/resolving.resolver-base": "1101.3.0",
35
- "@pnpm/store.cafs-types": "1100.1.0",
36
- "@pnpm/types": "1102.1.0",
37
- "@pnpm/workspace.project-manifest-reader": "1100.0.29"
31
+ "@pnpm/building.pkg-requires-build": "1100.0.19",
32
+ "@pnpm/error": "1100.2.1",
33
+ "@pnpm/fetching.fetcher-base": "1100.2.12",
34
+ "@pnpm/fs.packlist": "1100.0.6",
35
+ "@pnpm/resolving.resolver-base": "1101.3.2",
36
+ "@pnpm/store.cafs-types": "1100.1.1",
37
+ "@pnpm/types": "1102.1.1",
38
+ "@pnpm/workspace.project-manifest-reader": "1100.1.1"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "@pnpm/logger": "^1100.0.0"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@jest/globals": "30.4.1",
44
- "@pnpm/fetching.directory-fetcher": "1100.0.34",
45
+ "@pnpm/fetching.directory-fetcher": "1100.0.36",
45
46
  "@pnpm/logger": "1100.0.0",
46
47
  "@pnpm/test-fixtures": "1100.0.1",
47
48
  "@pnpm/text.ordinal-comparator": "1100.0.0",