npmdata 0.10.3 → 0.12.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.
Files changed (153) hide show
  1. package/README.md +133 -58
  2. package/dist/cli/actions/check.d.ts +6 -0
  3. package/dist/cli/actions/check.d.ts.map +1 -0
  4. package/dist/cli/actions/check.js +57 -0
  5. package/dist/cli/actions/check.js.map +1 -0
  6. package/dist/cli/actions/extract.d.ts +7 -0
  7. package/dist/cli/actions/extract.d.ts.map +1 -0
  8. package/dist/cli/actions/extract.js +84 -0
  9. package/dist/cli/actions/extract.js.map +1 -0
  10. package/dist/cli/actions/init.d.ts +6 -0
  11. package/dist/cli/actions/init.d.ts.map +1 -0
  12. package/dist/cli/actions/init.js +36 -0
  13. package/dist/cli/actions/init.js.map +1 -0
  14. package/dist/cli/actions/list.d.ts +7 -0
  15. package/dist/cli/actions/list.d.ts.map +1 -0
  16. package/dist/cli/actions/list.js +42 -0
  17. package/dist/cli/actions/list.js.map +1 -0
  18. package/dist/cli/actions/purge.d.ts +6 -0
  19. package/dist/cli/actions/purge.d.ts.map +1 -0
  20. package/dist/cli/actions/purge.js +47 -0
  21. package/dist/cli/actions/purge.js.map +1 -0
  22. package/dist/cli/argv.d.ts +35 -0
  23. package/dist/cli/argv.d.ts.map +1 -0
  24. package/dist/cli/argv.js +125 -0
  25. package/dist/cli/argv.js.map +1 -0
  26. package/dist/cli/cli.d.ts +9 -0
  27. package/dist/cli/cli.d.ts.map +1 -0
  28. package/dist/cli/cli.js +69 -0
  29. package/dist/cli/cli.js.map +1 -0
  30. package/dist/cli/runner.d.ts +9 -0
  31. package/dist/cli/runner.d.ts.map +1 -0
  32. package/dist/cli/runner.js +106 -0
  33. package/dist/cli/runner.js.map +1 -0
  34. package/dist/cli/usage.d.ts +6 -0
  35. package/dist/cli/usage.d.ts.map +1 -0
  36. package/dist/cli/usage.js +126 -0
  37. package/dist/cli/usage.js.map +1 -0
  38. package/dist/fileset/check.d.ts +15 -0
  39. package/dist/fileset/check.d.ts.map +1 -0
  40. package/dist/fileset/check.js +68 -0
  41. package/dist/fileset/check.js.map +1 -0
  42. package/dist/fileset/constants.d.ts +13 -0
  43. package/dist/fileset/constants.d.ts.map +1 -0
  44. package/dist/fileset/constants.js +22 -0
  45. package/dist/fileset/constants.js.map +1 -0
  46. package/dist/fileset/diff.d.ts +16 -0
  47. package/dist/fileset/diff.d.ts.map +1 -0
  48. package/dist/fileset/diff.js +116 -0
  49. package/dist/fileset/diff.js.map +1 -0
  50. package/dist/fileset/execute.d.ts +29 -0
  51. package/dist/fileset/execute.d.ts.map +1 -0
  52. package/dist/fileset/execute.js +136 -0
  53. package/dist/fileset/execute.js.map +1 -0
  54. package/dist/fileset/gitignore.d.ts +16 -0
  55. package/dist/fileset/gitignore.d.ts.map +1 -0
  56. package/dist/fileset/gitignore.js +82 -0
  57. package/dist/fileset/gitignore.js.map +1 -0
  58. package/dist/fileset/index.d.ts +5 -0
  59. package/dist/fileset/index.d.ts.map +1 -0
  60. package/dist/fileset/index.js +21 -0
  61. package/dist/fileset/index.js.map +1 -0
  62. package/dist/fileset/list.d.ts +6 -0
  63. package/dist/fileset/list.d.ts.map +1 -0
  64. package/dist/fileset/list.js +11 -0
  65. package/dist/fileset/list.js.map +1 -0
  66. package/dist/fileset/markers.d.ts +22 -0
  67. package/dist/fileset/markers.d.ts.map +1 -0
  68. package/dist/fileset/markers.js +68 -0
  69. package/dist/fileset/markers.js.map +1 -0
  70. package/dist/fileset/package-files.d.ts +14 -0
  71. package/dist/fileset/package-files.d.ts.map +1 -0
  72. package/dist/fileset/package-files.js +81 -0
  73. package/dist/fileset/package-files.js.map +1 -0
  74. package/dist/fileset/purge.d.ts +12 -0
  75. package/dist/fileset/purge.d.ts.map +1 -0
  76. package/dist/fileset/purge.js +95 -0
  77. package/dist/fileset/purge.js.map +1 -0
  78. package/dist/fileset/test-utils.d.ts +12 -0
  79. package/dist/fileset/test-utils.d.ts.map +1 -0
  80. package/dist/fileset/test-utils.js +65 -0
  81. package/dist/fileset/test-utils.js.map +1 -0
  82. package/dist/index.d.ts +10 -7
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +12 -17
  85. package/dist/index.js.map +1 -1
  86. package/dist/main.js +9 -16
  87. package/dist/main.js.map +1 -1
  88. package/dist/npmdata-0.0.1.tgz +0 -0
  89. package/dist/package/action-check.d.ts +20 -0
  90. package/dist/package/action-check.d.ts.map +1 -0
  91. package/dist/package/action-check.js +61 -0
  92. package/dist/package/action-check.js.map +1 -0
  93. package/dist/package/action-extract.d.ts +21 -0
  94. package/dist/package/action-extract.d.ts.map +1 -0
  95. package/dist/package/action-extract.js +186 -0
  96. package/dist/package/action-extract.js.map +1 -0
  97. package/dist/package/action-init.d.ts +13 -0
  98. package/dist/package/action-init.d.ts.map +1 -0
  99. package/dist/package/action-init.js +77 -0
  100. package/dist/package/action-init.js.map +1 -0
  101. package/dist/package/action-list.d.ts +14 -0
  102. package/dist/package/action-list.d.ts.map +1 -0
  103. package/dist/package/action-list.js +46 -0
  104. package/dist/package/action-list.js.map +1 -0
  105. package/dist/package/action-purge.d.ts +21 -0
  106. package/dist/package/action-purge.d.ts.map +1 -0
  107. package/dist/package/action-purge.js +60 -0
  108. package/dist/package/action-purge.js.map +1 -0
  109. package/dist/package/config-merge.d.ts +18 -0
  110. package/dist/package/config-merge.d.ts.map +1 -0
  111. package/dist/package/config-merge.js +48 -0
  112. package/dist/package/config-merge.js.map +1 -0
  113. package/dist/package/config.d.ts +13 -0
  114. package/dist/package/config.d.ts.map +1 -0
  115. package/dist/package/config.js +29 -0
  116. package/dist/package/config.js.map +1 -0
  117. package/dist/package/content-replacements.d.ts +21 -0
  118. package/dist/package/content-replacements.d.ts.map +1 -0
  119. package/dist/package/content-replacements.js +96 -0
  120. package/dist/package/content-replacements.js.map +1 -0
  121. package/dist/package/index.d.ts +10 -0
  122. package/dist/package/index.d.ts.map +1 -0
  123. package/dist/package/index.js +16 -0
  124. package/dist/package/index.js.map +1 -0
  125. package/dist/package/symlinks.d.ts +17 -0
  126. package/dist/package/symlinks.d.ts.map +1 -0
  127. package/dist/package/symlinks.js +125 -0
  128. package/dist/package/symlinks.js.map +1 -0
  129. package/dist/types.d.ts +184 -270
  130. package/dist/types.d.ts.map +1 -1
  131. package/dist/types.js +0 -12
  132. package/dist/types.js.map +1 -1
  133. package/dist/utils.d.ts +25 -55
  134. package/dist/utils.d.ts.map +1 -1
  135. package/dist/utils.js +82 -181
  136. package/dist/utils.js.map +1 -1
  137. package/package.json +5 -2
  138. package/dist/cli.d.ts +0 -6
  139. package/dist/cli.d.ts.map +0 -1
  140. package/dist/cli.js +0 -514
  141. package/dist/cli.js.map +0 -1
  142. package/dist/consumer.d.ts +0 -74
  143. package/dist/consumer.d.ts.map +0 -1
  144. package/dist/consumer.js +0 -820
  145. package/dist/consumer.js.map +0 -1
  146. package/dist/publisher.d.ts +0 -38
  147. package/dist/publisher.d.ts.map +0 -1
  148. package/dist/publisher.js +0 -164
  149. package/dist/publisher.js.map +0 -1
  150. package/dist/runner.d.ts +0 -102
  151. package/dist/runner.d.ts.map +0 -1
  152. package/dist/runner.js +0 -748
  153. package/dist/runner.js.map +0 -1
package/dist/consumer.js DELETED
@@ -1,820 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.compressGitignoreEntries = compressGitignoreEntries;
7
- exports.findNearestMarkerPath = findNearestMarkerPath;
8
- exports.extract = extract;
9
- exports.check = check;
10
- exports.purge = purge;
11
- exports.list = list;
12
- /* eslint-disable functional/no-try-statements */
13
- /* eslint-disable functional/no-let */
14
- /* eslint-disable no-continue */
15
- /* eslint-disable functional/immutable-data */
16
- /* eslint-disable no-restricted-syntax */
17
- /* eslint-disable max-depth */
18
- const node_fs_1 = __importDefault(require("node:fs"));
19
- const node_path_1 = __importDefault(require("node:path"));
20
- const node_child_process_1 = require("node:child_process");
21
- const semver_1 = require("semver");
22
- const ignore_1 = __importDefault(require("ignore"));
23
- const types_1 = require("./types");
24
- const utils_1 = require("./utils");
25
- const MARKER_FILE = '.npmdata';
26
- const GITIGNORE_FILE = '.gitignore';
27
- const GITIGNORE_START = '# npmdata:start';
28
- const GITIGNORE_END = '# npmdata:end';
29
- /**
30
- * Read the .gitignore at dir and return parsed patterns, excluding the npmdata-managed
31
- * section. These are the "external" patterns (e.g. node_modules, dist) that were written
32
- * by the project author rather than by npmdata itself.
33
- */
34
- function readExternalGitignore(dir) {
35
- const ig = (0, ignore_1.default)();
36
- const gitignorePath = node_path_1.default.join(dir, GITIGNORE_FILE);
37
- if (!node_fs_1.default.existsSync(gitignorePath))
38
- return ig;
39
- let content = node_fs_1.default.readFileSync(gitignorePath, 'utf8');
40
- // Strip out the npmdata-managed block so we only act on external entries.
41
- const startIdx = content.indexOf(GITIGNORE_START);
42
- const endIdx = content.indexOf(GITIGNORE_END);
43
- if (startIdx !== -1 && endIdx !== -1 && startIdx < endIdx) {
44
- content = content.slice(0, startIdx) + content.slice(endIdx + GITIGNORE_END.length);
45
- }
46
- ig.add(content);
47
- return ig;
48
- }
49
- /**
50
- * Update (or create) a .gitignore in the given directory so that the managed
51
- * files and the .npmdata marker file are ignored by git.
52
- * If managedFilenames is empty the npmdata section is removed; if the
53
- * resulting file is empty it is deleted.
54
- * When addEntries is false, only existing sections are updated/removed — no new
55
- * section is written if one did not already exist.
56
- */
57
- function updateGitignoreForDir(dir, managedFilenames, addEntries = true) {
58
- const gitignorePath = node_path_1.default.join(dir, GITIGNORE_FILE);
59
- let existingContent = '';
60
- if (node_fs_1.default.existsSync(gitignorePath)) {
61
- existingContent = node_fs_1.default.readFileSync(gitignorePath, 'utf8');
62
- }
63
- const startIdx = existingContent.indexOf(GITIGNORE_START);
64
- const endIdx = existingContent.indexOf(GITIGNORE_END);
65
- const hasExistingSection = startIdx !== -1 && endIdx !== -1 && startIdx < endIdx;
66
- // When not adding entries and there is no existing section, there is nothing to clean up.
67
- if (!addEntries && !hasExistingSection)
68
- return;
69
- let beforeSection = existingContent;
70
- let afterSection = '';
71
- if (hasExistingSection) {
72
- beforeSection = existingContent.slice(0, startIdx).trimEnd();
73
- afterSection = existingContent.slice(endIdx + GITIGNORE_END.length).trimStart();
74
- }
75
- if (managedFilenames.length === 0) {
76
- // Remove the managed section entirely.
77
- const updatedContent = [beforeSection, afterSection].filter(Boolean).join('\n');
78
- if (updatedContent.trim()) {
79
- node_fs_1.default.writeFileSync(gitignorePath, `${updatedContent.trimEnd()}\n`, 'utf8');
80
- }
81
- else if (node_fs_1.default.existsSync(gitignorePath)) {
82
- node_fs_1.default.unlinkSync(gitignorePath);
83
- }
84
- return;
85
- }
86
- // When addEntries is false, only update an existing section (stale entries removed);
87
- // if there is no existing section do not create one (already returned above).
88
- const section = [GITIGNORE_START, MARKER_FILE, ...managedFilenames.sort(), GITIGNORE_END].join('\n');
89
- const parts = [beforeSection, section, afterSection].filter(Boolean);
90
- const updatedContent = `${parts.join('\n')}\n`;
91
- node_fs_1.default.writeFileSync(gitignorePath, updatedContent, 'utf8');
92
- }
93
- /**
94
- * Optimise the list of managed file paths for use in .gitignore.
95
- * When every file inside a directory (recursively, excluding MARKER_FILE, GITIGNORE_FILE, and
96
- * symlinks) is present in managedPaths, the whole directory is represented as "dir/" rather than
97
- * listing each file individually. Root-level files (no slash) are always emitted as-is.
98
- *
99
- * @param managedPaths - Paths relative to outputDir (e.g. ["docs/guide.md", "README.md"])
100
- * @param outputDir - Absolute path to the root used to inspect actual disk contents
101
- */
102
- function compressGitignoreEntries(managedPaths, outputDir) {
103
- const managedSet = new Set(managedPaths);
104
- const gitignore = readExternalGitignore(outputDir);
105
- // Returns true when every non-special, non-symlink file inside absDir (recursively)
106
- // appears in managedSet under its full outputDir-relative path (relDir prefix included).
107
- const isDirFullyManaged = (absDir, relDir) => {
108
- if (!node_fs_1.default.existsSync(absDir))
109
- return false;
110
- for (const entry of node_fs_1.default.readdirSync(absDir)) {
111
- if (entry === MARKER_FILE || entry === GITIGNORE_FILE)
112
- continue;
113
- const absEntry = node_path_1.default.join(absDir, entry);
114
- const relEntry = `${relDir}/${entry}`;
115
- const lstat = node_fs_1.default.lstatSync(absEntry);
116
- if (lstat.isSymbolicLink())
117
- continue;
118
- if (lstat.isDirectory()) {
119
- // Skip gitignored subdirs that have no managed files — they are not our concern
120
- // and traversing them (e.g. node_modules) causes serious performance problems.
121
- if (gitignore.ignores(entry) && !(0, utils_1.hasManagedFilesUnder)(relEntry, managedSet)) {
122
- continue;
123
- }
124
- if (!isDirFullyManaged(absEntry, relEntry))
125
- return false;
126
- }
127
- else if (!managedSet.has(relEntry))
128
- return false;
129
- }
130
- return true;
131
- };
132
- // paths: managed paths relative to the current directory scope
133
- // absRoot: absolute path of the current directory scope
134
- // relRoot: path of the current scope relative to outputDir (empty string at top level)
135
- const compress = (paths, absRoot, relRoot) => {
136
- const result = [];
137
- const subdirNames = new Set();
138
- for (const p of paths) {
139
- const slashIdx = p.indexOf('/');
140
- if (slashIdx === -1) {
141
- // File lives directly in this scope — emit its full outputDir-relative path
142
- result.push(relRoot ? `${relRoot}/${p}` : p);
143
- }
144
- else {
145
- subdirNames.add(p.slice(0, slashIdx));
146
- }
147
- }
148
- for (const dirName of subdirNames) {
149
- const absDir = node_path_1.default.join(absRoot, dirName);
150
- const relDir = relRoot ? `${relRoot}/${dirName}` : dirName;
151
- const prefix = `${dirName}/`;
152
- const subPaths = paths.filter((p) => p.startsWith(prefix)).map((p) => p.slice(prefix.length));
153
- if (isDirFullyManaged(absDir, relDir)) {
154
- result.push(`${relDir}/`);
155
- }
156
- else {
157
- result.push(...compress(subPaths, absDir, relDir));
158
- }
159
- }
160
- return result;
161
- };
162
- return compress(managedPaths, outputDir, '');
163
- }
164
- /**
165
- * Find the nearest .npmdata marker file by walking up from fromDir to outputDir (inclusive).
166
- * Returns the path to the marker file, or null if none found within the outputDir boundary.
167
- */
168
- function findNearestMarkerPath(fromDir, outputDir) {
169
- let dir = fromDir;
170
- const resolvedOutput = node_path_1.default.resolve(outputDir);
171
- // eslint-disable-next-line no-constant-condition
172
- while (true) {
173
- const markerPath = node_path_1.default.join(dir, MARKER_FILE);
174
- if (node_fs_1.default.existsSync(markerPath))
175
- return markerPath;
176
- if (node_path_1.default.resolve(dir) === resolvedOutput)
177
- break;
178
- const parent = node_path_1.default.dirname(dir);
179
- if (parent === dir)
180
- break; // reached filesystem root
181
- dir = parent;
182
- }
183
- // eslint-disable-next-line unicorn/no-null
184
- return null;
185
- }
186
- /**
187
- * Write one .gitignore at outputDir containing all managed file paths (relative to outputDir),
188
- * and remove any npmdata sections from .gitignore files in subdirectories.
189
- * When addEntries is false, existing sections are updated/removed but no new
190
- * sections are created — use this to clean up without opting into gitignore management.
191
- */
192
- function updateGitignores(outputDir, addEntries = true) {
193
- if (!node_fs_1.default.existsSync(outputDir))
194
- return;
195
- // Read managed paths up-front so we can skip gitignored dirs that have no managed files.
196
- const managedPaths = new Set();
197
- const rootMarkerPathForRead = node_path_1.default.join(outputDir, MARKER_FILE);
198
- if (node_fs_1.default.existsSync(rootMarkerPathForRead)) {
199
- // eslint-disable-next-line functional/no-try-statements
200
- try {
201
- for (const m of (0, utils_1.readCsvMarker)(rootMarkerPathForRead)) {
202
- managedPaths.add(m.path);
203
- }
204
- }
205
- catch {
206
- // ignore unreadable marker
207
- }
208
- }
209
- // Read external gitignore patterns once for the whole walk.
210
- const gitignore = readExternalGitignore(outputDir);
211
- // Remove npmdata sections from all subdirectory .gitignore files (migration / cleanup of old format)
212
- const cleanupSubDirGitignores = (dir) => {
213
- for (const item of node_fs_1.default.readdirSync(dir)) {
214
- const fullPath = node_path_1.default.join(dir, item);
215
- const lstat = node_fs_1.default.lstatSync(fullPath);
216
- if (!lstat.isSymbolicLink() && lstat.isDirectory()) {
217
- const relPath = node_path_1.default.relative(outputDir, fullPath);
218
- // Skip gitignored directories that have no managed files under them —
219
- // traversing them (e.g. node_modules) causes serious performance problems.
220
- if (gitignore.ignores(item) && !(0, utils_1.hasManagedFilesUnder)(relPath, managedPaths)) {
221
- continue;
222
- }
223
- const subGitignore = node_path_1.default.join(fullPath, GITIGNORE_FILE);
224
- if (node_fs_1.default.existsSync(subGitignore)) {
225
- updateGitignoreForDir(fullPath, [], false);
226
- }
227
- cleanupSubDirGitignores(fullPath);
228
- }
229
- }
230
- };
231
- cleanupSubDirGitignores(outputDir);
232
- // Update (or remove) the single .gitignore at outputDir
233
- const rootMarkerPath = node_path_1.default.join(outputDir, MARKER_FILE);
234
- if (node_fs_1.default.existsSync(rootMarkerPath)) {
235
- try {
236
- const managedFiles = (0, utils_1.readCsvMarker)(rootMarkerPath);
237
- const rawPaths = managedFiles.map((m) => m.path);
238
- const optimisedPaths = compressGitignoreEntries(rawPaths, outputDir);
239
- updateGitignoreForDir(outputDir, optimisedPaths, addEntries);
240
- }
241
- catch {
242
- // Ignore unreadable marker files
243
- }
244
- }
245
- else {
246
- // Clean up any leftover npmdata section at root
247
- updateGitignoreForDir(outputDir, [], false);
248
- }
249
- }
250
- async function getPackageFiles(packageName, cwd) {
251
- const pkgPath = require.resolve(`${packageName}/package.json`, {
252
- // eslint-disable-next-line no-undefined
253
- paths: cwd ? [cwd] : undefined,
254
- });
255
- const packagePath = node_path_1.default.dirname(pkgPath);
256
- if (!packagePath) {
257
- throw new Error(`Cannot locate installed package: ${packageName}`);
258
- }
259
- const contents = [];
260
- const walkDir = (dir, basePath = '') => {
261
- for (const file of node_fs_1.default.readdirSync(dir)) {
262
- if (file === MARKER_FILE)
263
- continue;
264
- const fullPath = node_path_1.default.join(dir, file);
265
- const relPath = basePath ? `${basePath}/${file}` : file;
266
- const lstat = node_fs_1.default.lstatSync(fullPath);
267
- if (!lstat.isSymbolicLink() && lstat.isDirectory()) {
268
- walkDir(fullPath, relPath);
269
- }
270
- else if (!lstat.isSymbolicLink()) {
271
- contents.push({ relPath, fullPath });
272
- }
273
- }
274
- };
275
- walkDir(packagePath);
276
- return contents;
277
- }
278
- async function installPackage(packageName, version, packageManager, cwd) {
279
- const packageSpec = version ? `${packageName}@${version}` : `${packageName}@latest`;
280
- let cmd;
281
- switch (packageManager) {
282
- case 'pnpm':
283
- cmd = `pnpm add ${packageSpec}`;
284
- break;
285
- case 'yarn':
286
- cmd = `yarn add ${packageSpec}`;
287
- break;
288
- default:
289
- cmd = `npm install ${packageSpec}`;
290
- }
291
- // eslint-disable-next-line functional/no-try-statements
292
- try {
293
- (0, node_child_process_1.execSync)(cmd, { encoding: 'utf8', stdio: 'pipe', cwd });
294
- }
295
- catch (error) {
296
- const e = error;
297
- const detail = (e.stderr ?? e.stdout ?? e.message ?? String(error)).trim();
298
- throw new Error(`Failed to install ${packageSpec}: ${detail}`);
299
- }
300
- }
301
- async function ensurePackageInstalled(packageName, version, packageManager, cwd, upgrade) {
302
- const existingVersion = (0, utils_1.getInstalledPackageVersion)(packageName, cwd);
303
- if (!existingVersion) {
304
- const spec = version ? `${packageName}@${version}` : packageName;
305
- // eslint-disable-next-line no-console
306
- console.log(`Installing missing package ${spec}...`);
307
- await installPackage(packageName, version, packageManager, cwd);
308
- }
309
- else if (upgrade) {
310
- const spec = version ? `${packageName}@${version}` : packageName;
311
- // eslint-disable-next-line no-console
312
- console.log(`Bumping existing package ${spec}...`);
313
- await installPackage(packageName, version, packageManager, cwd);
314
- }
315
- const installedVersion = (0, utils_1.getInstalledPackageVersion)(packageName, cwd);
316
- if (!installedVersion) {
317
- throw new Error(`Couldn't find package ${packageName}`);
318
- }
319
- if (version && !(0, semver_1.satisfies)(installedVersion, version)) {
320
- throw new Error(`Installed version ${installedVersion} of package '${packageName}' does not match constraint ${version}`);
321
- }
322
- return installedVersion;
323
- }
324
- /**
325
- * Load all managed files from the root marker file at outputDir.
326
- * Paths stored in the marker are already relative to outputDir.
327
- * Uses findNearestMarkerPath starting from outputDir itself.
328
- */
329
- function loadAllManagedFiles(outputDir) {
330
- if (!node_fs_1.default.existsSync(outputDir))
331
- return [];
332
- const markerPath = findNearestMarkerPath(outputDir, outputDir);
333
- if (!markerPath)
334
- return [];
335
- try {
336
- return (0, utils_1.readCsvMarker)(markerPath);
337
- }
338
- catch {
339
- console.warn(`Warning: Failed to read marker file at ${markerPath}. Skipping.`); // eslint-disable-line no-console
340
- return [];
341
- }
342
- }
343
- /**
344
- * Load managed files from all marker files under outputDir, keyed by relative path.
345
- * Each value carries the package ownership metadata.
346
- */
347
- function loadManagedFilesMap(outputDir) {
348
- return new Map(loadAllManagedFiles(outputDir).map((m) => [m.path, m]));
349
- }
350
- function cleanupEmptyMarkers(outputDir) {
351
- if (!node_fs_1.default.existsSync(outputDir))
352
- return;
353
- const markerPath = node_path_1.default.join(outputDir, MARKER_FILE);
354
- if (!node_fs_1.default.existsSync(markerPath))
355
- return;
356
- try {
357
- const managedFiles = (0, utils_1.readCsvMarker)(markerPath);
358
- if (managedFiles.length === 0) {
359
- node_fs_1.default.chmodSync(markerPath, 0o644);
360
- node_fs_1.default.unlinkSync(markerPath);
361
- }
362
- }
363
- catch {
364
- // Ignore unreadable marker files
365
- }
366
- }
367
- function cleanupEmptyDirs(outputDir) {
368
- const gitignore = readExternalGitignore(outputDir);
369
- const managedPaths = new Set(loadAllManagedFiles(outputDir).map((m) => m.path));
370
- const walkDir = (dir) => {
371
- if (!node_fs_1.default.existsSync(dir))
372
- return true;
373
- let isEmpty = true;
374
- for (const item of node_fs_1.default.readdirSync(dir)) {
375
- const fullPath = node_path_1.default.join(dir, item);
376
- const lstat = node_fs_1.default.lstatSync(fullPath);
377
- if (!lstat.isSymbolicLink() && lstat.isDirectory()) {
378
- const relPath = node_path_1.default.relative(outputDir, fullPath);
379
- // Skip gitignored directories that have no managed files — they are not our concern
380
- // and traversing them (e.g. node_modules) causes serious performance problems.
381
- if (gitignore.ignores(item) && !(0, utils_1.hasManagedFilesUnder)(relPath, managedPaths)) {
382
- isEmpty = false; // treat as non-empty so we preserve the parent directory
383
- continue;
384
- }
385
- const childEmpty = walkDir(fullPath);
386
- if (!childEmpty)
387
- isEmpty = false;
388
- }
389
- else {
390
- isEmpty = false;
391
- }
392
- }
393
- if (isEmpty && dir !== outputDir) {
394
- node_fs_1.default.rmdirSync(dir);
395
- return true;
396
- }
397
- return isEmpty;
398
- };
399
- walkDir(outputDir);
400
- }
401
- // eslint-disable-next-line complexity
402
- async function extractFiles(config, packageName) {
403
- const changes = {
404
- added: [],
405
- modified: [],
406
- deleted: [],
407
- skipped: [],
408
- };
409
- const dryRun = config.dryRun ?? false;
410
- const emit = config.onProgress;
411
- const installedVersion = (0, utils_1.getInstalledPackageVersion)(packageName, config.cwd);
412
- if (!installedVersion) {
413
- throw new Error(`Failed to determine installed version of package ${packageName}`);
414
- }
415
- emit?.({ type: 'package-start', packageName, packageVersion: installedVersion });
416
- const packageFiles = await getPackageFiles(packageName, config.cwd);
417
- const extractedFiles = [];
418
- const existingManagedMap = loadManagedFilesMap(config.outputDir);
419
- // Tracks full relPaths force-claimed from a different package so the
420
- // marker-file merge can evict the previous owner's entry.
421
- const forceClaimedPaths = new Set();
422
- // eslint-disable-next-line functional/no-let
423
- let wasForced = false;
424
- try {
425
- for (const packageFile of packageFiles) {
426
- if (!(0, utils_1.matchesFilenamePattern)(packageFile.relPath, config.filenamePatterns ?? types_1.DEFAULT_FILENAME_PATTERNS) ||
427
- !(0, utils_1.matchesContentRegex)(packageFile.fullPath, config.contentRegexes)) {
428
- continue;
429
- }
430
- const destPath = node_path_1.default.join(config.outputDir, packageFile.relPath);
431
- if (!dryRun)
432
- (0, utils_1.ensureDir)(node_path_1.default.dirname(destPath));
433
- const existingOwner = existingManagedMap.get(packageFile.relPath);
434
- // In unmanaged mode, skip files that already exist on disk.
435
- if (config.unmanaged && node_fs_1.default.existsSync(destPath)) {
436
- changes.skipped.push(packageFile.relPath);
437
- emit?.({ type: 'file-skipped', packageName, file: packageFile.relPath });
438
- continue;
439
- }
440
- // In keep-existing mode, skip files that already exist on disk but create missing ones normally.
441
- if (config.keepExisting && node_fs_1.default.existsSync(destPath)) {
442
- changes.skipped.push(packageFile.relPath);
443
- emit?.({ type: 'file-skipped', packageName, file: packageFile.relPath });
444
- continue;
445
- }
446
- if (node_fs_1.default.existsSync(destPath)) {
447
- if (existingOwner?.packageName === packageName) {
448
- if ((0, utils_1.calculateFileHash)(packageFile.fullPath) === (0, utils_1.calculateFileHash)(destPath)) {
449
- changes.skipped.push(packageFile.relPath);
450
- emit?.({ type: 'file-skipped', packageName, file: packageFile.relPath });
451
- }
452
- else {
453
- if (!dryRun)
454
- (0, utils_1.copyFile)(packageFile.fullPath, destPath);
455
- changes.modified.push(packageFile.relPath);
456
- emit?.({ type: 'file-modified', packageName, file: packageFile.relPath });
457
- }
458
- wasForced = false;
459
- }
460
- else {
461
- // File exists but is owned by a different package (clash) or is unmanaged (conflict).
462
- // Behaviour is identical in both cases: throw when force is false, overwrite when true.
463
- if (!config.force) {
464
- if (existingOwner) {
465
- throw new Error(`Package clash: ${packageFile.relPath} already managed by ${existingOwner.packageName}@${existingOwner.packageVersion}. Cannot extract from ${packageName}. Use force: true to override.`);
466
- }
467
- throw new Error(`File conflict: ${packageFile.relPath} already exists and is not managed by npmdata. Use force: true to override.`);
468
- }
469
- // force=true: overwrite the existing file and take ownership.
470
- if (!dryRun)
471
- (0, utils_1.copyFile)(packageFile.fullPath, destPath);
472
- changes.modified.push(packageFile.relPath);
473
- emit?.({ type: 'file-modified', packageName, file: packageFile.relPath });
474
- wasForced = true;
475
- if (existingOwner) {
476
- // Evict the previous owner's entry from the root marker file.
477
- forceClaimedPaths.add(packageFile.relPath);
478
- }
479
- }
480
- }
481
- else {
482
- if (!dryRun)
483
- (0, utils_1.copyFile)(packageFile.fullPath, destPath);
484
- changes.added.push(packageFile.relPath);
485
- emit?.({ type: 'file-added', packageName, file: packageFile.relPath });
486
- wasForced = false;
487
- }
488
- if (!dryRun && !config.unmanaged && node_fs_1.default.existsSync(destPath))
489
- node_fs_1.default.chmodSync(destPath, 0o444);
490
- if (!config.unmanaged) {
491
- // eslint-disable-next-line functional/immutable-data
492
- extractedFiles.push({
493
- path: packageFile.relPath,
494
- packageName,
495
- packageVersion: installedVersion,
496
- force: wasForced,
497
- });
498
- }
499
- }
500
- // Delete files that were managed by this package but are no longer in the package.
501
- // Skip this step in unmanaged mode — only extraction is wanted, no sync/deletion.
502
- if (!config.unmanaged) {
503
- for (const [relPath, owner] of existingManagedMap) {
504
- if (owner.packageName !== packageName)
505
- continue;
506
- const stillPresent = extractedFiles.some((m) => m.path === relPath);
507
- if (!stillPresent) {
508
- const fullPath = node_path_1.default.join(config.outputDir, relPath);
509
- if (node_fs_1.default.existsSync(fullPath)) {
510
- if (!dryRun)
511
- (0, utils_1.removeFile)(fullPath);
512
- changes.deleted.push(relPath);
513
- emit?.({ type: 'file-deleted', packageName, file: relPath });
514
- }
515
- }
516
- }
517
- }
518
- if (!dryRun && !config.unmanaged) {
519
- // Write a single root marker at outputDir with all managed file paths (relative to outputDir)
520
- const rootMarkerPath = node_path_1.default.join(config.outputDir, MARKER_FILE);
521
- let existingFiles = [];
522
- if (node_fs_1.default.existsSync(rootMarkerPath)) {
523
- existingFiles = (0, utils_1.readCsvMarker)(rootMarkerPath);
524
- }
525
- // Keep entries from other packages, evict entries from force-claimed paths.
526
- const mergedFiles = [
527
- ...existingFiles.filter((m) => m.packageName !== packageName && !forceClaimedPaths.has(m.path)),
528
- ...extractedFiles,
529
- ];
530
- if (mergedFiles.length === 0) {
531
- if (node_fs_1.default.existsSync(rootMarkerPath)) {
532
- node_fs_1.default.chmodSync(rootMarkerPath, 0o644);
533
- node_fs_1.default.unlinkSync(rootMarkerPath);
534
- }
535
- }
536
- else {
537
- (0, utils_1.writeCsvMarker)(rootMarkerPath, mergedFiles);
538
- }
539
- cleanupEmptyMarkers(config.outputDir);
540
- }
541
- }
542
- catch (error) {
543
- // On error, delete all files that were created during this extraction run
544
- if (!dryRun) {
545
- for (const relPath of changes.added) {
546
- const fullPath = node_path_1.default.join(config.outputDir, relPath);
547
- if (node_fs_1.default.existsSync(fullPath)) {
548
- try {
549
- (0, utils_1.removeFile)(fullPath);
550
- }
551
- catch {
552
- // ignore cleanup errors
553
- }
554
- }
555
- }
556
- cleanupEmptyDirs(config.outputDir);
557
- }
558
- throw error;
559
- }
560
- emit?.({ type: 'package-end', packageName, packageVersion: installedVersion });
561
- return changes;
562
- }
563
- /**
564
- * Extract files from published packages to output directory.
565
- *
566
- * Phase 1 validates and installs every package before touching disk.
567
- * Phase 2 runs file extraction for all packages in parallel.
568
- * When dryRun is true no files are written; the result reflects what would change.
569
- */
570
- async function extract(config) {
571
- const dryRun = config.dryRun ?? false;
572
- if (!dryRun)
573
- (0, utils_1.ensureDir)(config.outputDir);
574
- if (config.force && config.keepExisting) {
575
- throw new Error('force and keepExisting cannot be used together');
576
- }
577
- const packageManager = config.packageManager ?? (0, utils_1.detectPackageManager)(config.cwd);
578
- const sourcePackages = [];
579
- const totalChanges = {
580
- added: [],
581
- modified: [],
582
- deleted: [],
583
- skipped: [],
584
- };
585
- // Phase 1: validate and install every package before touching the disk.
586
- // If any package is missing or at a wrong version, we abort before writing anything.
587
- const resolvedPackages = [];
588
- for (const spec of config.packages) {
589
- const { name, version } = (0, utils_1.parsePackageSpec)(spec);
590
- // eslint-disable-next-line no-await-in-loop
591
- const installedVersion = await ensurePackageInstalled(name, version, packageManager, config.cwd, config.upgrade);
592
- resolvedPackages.push({ name, version, installedVersion });
593
- }
594
- // Phase 2: all packages are verified — extract files serially so progress events are grouped by package.
595
- for (const { name, installedVersion } of resolvedPackages) {
596
- // eslint-disable-next-line no-await-in-loop
597
- const changes = await extractFiles(config, name);
598
- totalChanges.added.push(...changes.added);
599
- totalChanges.modified.push(...changes.modified);
600
- totalChanges.deleted.push(...changes.deleted);
601
- totalChanges.skipped.push(...changes.skipped);
602
- sourcePackages.push({ name, version: installedVersion, changes });
603
- }
604
- if (!dryRun) {
605
- if (!config.unmanaged) {
606
- cleanupEmptyMarkers(config.outputDir);
607
- // Always clean up .gitignore entries for removed files; only add new entries when gitignore: true.
608
- updateGitignores(config.outputDir, config.gitignore ?? true);
609
- }
610
- // Run after gitignore cleanup so dirs kept alive only by a .gitignore get removed.
611
- cleanupEmptyDirs(config.outputDir);
612
- }
613
- return {
614
- ...totalChanges,
615
- sourcePackages,
616
- };
617
- }
618
- /**
619
- * Compute the expected hash of a package source file as it should appear in the
620
- * output directory after all content replacements have been applied.
621
- *
622
- * When no replacement config is provided (or none matches the file), the hash is
623
- * computed directly from the on-disk source content. When one or more replacements
624
- * match, the source content is transformed in memory and the resulting hash is
625
- * returned – this makes check() tolerant of post-extract content replacements.
626
- */
627
- // eslint-disable-next-line complexity
628
- function computeExpectedHash(sourceFullPath, relPath, outputDir, cwd, replacements) {
629
- if (!replacements || replacements.length === 0) {
630
- return (0, utils_1.calculateFileHash)(sourceFullPath);
631
- }
632
- // The workspace path of the extracted file, relative to cwd, is used to
633
- // evaluate the replacement's `files` glob (which is also relative to cwd).
634
- const workspaceRelPath = node_path_1.default.relative(cwd, node_path_1.default.join(outputDir, relPath));
635
- const applicable = replacements.filter((r) => (0, utils_1.matchesFilenamePattern)(workspaceRelPath, [r.files]));
636
- if (applicable.length === 0) {
637
- return (0, utils_1.calculateFileHash)(sourceFullPath);
638
- }
639
- // eslint-disable-next-line functional/no-let
640
- let content = node_fs_1.default.readFileSync(sourceFullPath, 'utf8');
641
- for (const r of applicable) {
642
- content = content.replaceAll(new RegExp(r.match, 'gm'), r.replace);
643
- }
644
- return (0, utils_1.calculateBufferHash)(content);
645
- }
646
- /**
647
- * Check if managed files are in sync with published packages.
648
- *
649
- * Performs a bidirectional comparison:
650
- * - Files in the .npmdata marker that are missing from or modified in the output directory.
651
- * - Files present in the package (matching filters) that have not been extracted yet ("extra").
652
- *
653
- * If a version constraint is specified (e.g. "my-pkg@^1.0.0"), the installed version is
654
- * validated against it so stale installs are caught.
655
- */
656
- async function check(config) {
657
- const sourcePackages = [];
658
- const totalDifferences = {
659
- missing: [],
660
- modified: [],
661
- extra: [],
662
- };
663
- for (const spec of config.packages) {
664
- const { name, version: constraint } = (0, utils_1.parsePackageSpec)(spec);
665
- const installedVersion = (0, utils_1.getInstalledPackageVersion)(name, config.cwd);
666
- if (!installedVersion) {
667
- throw new Error(`Package ${name} is not installed. Run 'extract' first.`);
668
- }
669
- if (constraint && !(0, semver_1.satisfies)(installedVersion, constraint)) {
670
- throw new Error(`Installed version ${installedVersion} of package '${name}' does not satisfy constraint ${constraint}. Run 'extract' to update.`);
671
- }
672
- // Load marker entries for this package and apply the --files filter
673
- const markerFiles = loadAllManagedFiles(config.outputDir)
674
- .filter((m) => m.packageName === name)
675
- .filter((m) => (0, utils_1.matchesFilenamePattern)(m.path, config.filenamePatterns ?? types_1.DEFAULT_FILENAME_PATTERNS));
676
- const markerPaths = new Set(markerFiles.map((m) => m.path));
677
- // Build a hash map of the installed package files (filtered the same way).
678
- // When content replacements are configured, the expected hash for each affected file
679
- // is computed from the source content AFTER applying the replacements, so that files
680
- // modified in-place by a post-extract replacement are not reported as out of sync.
681
- // eslint-disable-next-line no-await-in-loop
682
- const packageFiles = await getPackageFiles(name, config.cwd);
683
- const filteredPackageFiles = packageFiles.filter((f) => (0, utils_1.matchesFilenamePattern)(f.relPath, config.filenamePatterns ?? types_1.DEFAULT_FILENAME_PATTERNS) &&
684
- (0, utils_1.matchesContentRegex)(f.fullPath, config.contentRegexes));
685
- const effectiveCwd = config.cwd ?? process.cwd();
686
- const packageHashMap = new Map(filteredPackageFiles.map((f) => [
687
- f.relPath,
688
- computeExpectedHash(f.fullPath, f.relPath, config.outputDir, effectiveCwd, config.contentReplacements),
689
- ]));
690
- const pkgDiff = {
691
- missing: [],
692
- modified: [],
693
- extra: [],
694
- };
695
- // Check marker entries against local files and package contents
696
- for (const markerFile of markerFiles) {
697
- const localPath = node_path_1.default.join(config.outputDir, markerFile.path);
698
- if (!node_fs_1.default.existsSync(localPath)) {
699
- pkgDiff.missing.push(markerFile.path);
700
- continue;
701
- }
702
- const packageHash = packageHashMap.get(markerFile.path);
703
- // eslint-disable-next-line no-undefined
704
- if (packageHash !== undefined && (0, utils_1.calculateFileHash)(localPath) !== packageHash) {
705
- pkgDiff.modified.push(markerFile.path);
706
- }
707
- }
708
- // Detect package files that were never extracted (not in the marker)
709
- for (const [relPath] of packageHashMap) {
710
- if (!markerPaths.has(relPath)) {
711
- pkgDiff.extra.push(relPath);
712
- }
713
- }
714
- const pkgOk = pkgDiff.missing.length === 0 && pkgDiff.modified.length === 0 && pkgDiff.extra.length === 0;
715
- sourcePackages.push({ name, version: installedVersion, ok: pkgOk, differences: pkgDiff });
716
- totalDifferences.missing.push(...pkgDiff.missing);
717
- totalDifferences.modified.push(...pkgDiff.modified);
718
- totalDifferences.extra.push(...pkgDiff.extra);
719
- }
720
- return {
721
- ok: totalDifferences.missing.length === 0 &&
722
- totalDifferences.modified.length === 0 &&
723
- totalDifferences.extra.length === 0,
724
- differences: totalDifferences,
725
- sourcePackages,
726
- };
727
- }
728
- /**
729
- * Remove all managed files previously extracted by the given packages from outputDir.
730
- * Reads .npmdata marker files to discover which files are owned by each package,
731
- * deletes them from disk, updates the marker files, and cleans up empty directories.
732
- * No package installation is required – only the local marker state is used.
733
- */
734
- async function purge(config) {
735
- const dryRun = config.dryRun ?? false;
736
- const emit = config.onProgress;
737
- const totalChanges = {
738
- added: [],
739
- modified: [],
740
- deleted: [],
741
- skipped: [],
742
- };
743
- const sourcePackages = [];
744
- for (const spec of config.packages) {
745
- const { name: packageName } = (0, utils_1.parsePackageSpec)(spec);
746
- const deleted = [];
747
- emit?.({ type: 'package-start', packageName, packageVersion: 'unknown' });
748
- const allManaged = loadManagedFilesMap(config.outputDir);
749
- for (const [relPath, owner] of allManaged) {
750
- if (owner.packageName !== packageName)
751
- continue;
752
- const fullPath = node_path_1.default.join(config.outputDir, relPath);
753
- if (node_fs_1.default.existsSync(fullPath)) {
754
- if (!dryRun)
755
- (0, utils_1.removeFile)(fullPath);
756
- deleted.push(relPath);
757
- emit?.({ type: 'file-deleted', packageName, file: relPath });
758
- }
759
- }
760
- if (!dryRun) {
761
- // Update root marker: remove entries owned by this package.
762
- const rootMarkerPath = node_path_1.default.join(config.outputDir, MARKER_FILE);
763
- if (node_fs_1.default.existsSync(rootMarkerPath)) {
764
- // eslint-disable-next-line functional/no-try-statements
765
- try {
766
- const existingFiles = (0, utils_1.readCsvMarker)(rootMarkerPath);
767
- const mergedFiles = existingFiles.filter((m) => m.packageName !== packageName);
768
- if (mergedFiles.length === 0) {
769
- node_fs_1.default.chmodSync(rootMarkerPath, 0o644);
770
- node_fs_1.default.unlinkSync(rootMarkerPath);
771
- }
772
- else {
773
- (0, utils_1.writeCsvMarker)(rootMarkerPath, mergedFiles);
774
- }
775
- }
776
- catch {
777
- // Ignore unreadable marker files
778
- }
779
- }
780
- cleanupEmptyMarkers(config.outputDir);
781
- // Clean up any leftover .gitignore sections without adding new ones.
782
- updateGitignores(config.outputDir, false);
783
- cleanupEmptyDirs(config.outputDir);
784
- }
785
- totalChanges.deleted.push(...deleted);
786
- sourcePackages.push({
787
- name: packageName,
788
- version: 'unknown',
789
- changes: { added: [], modified: [], deleted, skipped: [] },
790
- });
791
- emit?.({ type: 'package-end', packageName, packageVersion: 'unknown' });
792
- }
793
- return {
794
- ...totalChanges,
795
- sourcePackages,
796
- };
797
- }
798
- /**
799
- * List all managed files currently extracted in outputDir, grouped by package.
800
- */
801
- function list(outputDir) {
802
- const allManaged = loadAllManagedFiles(outputDir);
803
- const grouped = new Map();
804
- for (const managed of allManaged) {
805
- const key = `${managed.packageName}@${managed.packageVersion}`;
806
- if (!grouped.has(key)) {
807
- grouped.set(key, {
808
- packageName: managed.packageName,
809
- packageVersion: managed.packageVersion,
810
- files: [],
811
- });
812
- }
813
- grouped.get(key).files.push(managed.path);
814
- }
815
- return [...grouped.values()].map((entry) => ({
816
- ...entry,
817
- files: entry.files.sort(),
818
- }));
819
- }
820
- //# sourceMappingURL=consumer.js.map