node-modules-tools 0.0.1 → 0.0.2

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,5 +1,14 @@
1
+ import { relative, dirname } from 'pathe';
1
2
  import { x } from 'tinyexec';
2
3
 
4
+ async function resolveRoot(options) {
5
+ const raw = await x("pnpm", ["root", "-w"], { throwOnError: true, nodeOptions: { cwd: options.cwd } });
6
+ return dirname(raw.stdout.trim());
7
+ }
8
+ async function getPnpmVersion(options) {
9
+ const raw = await x("pnpm", ["--version"], { throwOnError: true, nodeOptions: { cwd: options.cwd } });
10
+ return raw.stdout.trim();
11
+ }
3
12
  async function getDependenciesTree(options) {
4
13
  const args = ["ls", "--json", "--no-optional", "--depth", String(options.depth)];
5
14
  if (options.monorepo)
@@ -9,71 +18,92 @@ async function getDependenciesTree(options) {
9
18
  return tree;
10
19
  }
11
20
  async function listPackageDependencies(options) {
21
+ const root = await resolveRoot(options) || options.cwd;
12
22
  const tree = await getDependenciesTree(options);
13
- const specs = /* @__PURE__ */ new Map();
14
- const map = /* @__PURE__ */ new WeakMap();
23
+ const packages = /* @__PURE__ */ new Map();
24
+ const workspacePackages = tree.map((pkg) => {
25
+ let name = pkg.name;
26
+ if (!name) {
27
+ let path = relative(root, pkg.path);
28
+ if (path === ".")
29
+ path = "";
30
+ const suffix = path.toLowerCase().replace(/[^a-z0-9-]+/g, "_").slice(0, 20);
31
+ name = suffix ? `#workspace-${suffix}` : "#workspace-root";
32
+ }
33
+ const version = pkg.version || "0.0.0";
34
+ const node = {
35
+ spec: `${name}@${version}`,
36
+ name,
37
+ version,
38
+ filepath: pkg.path,
39
+ dependencies: /* @__PURE__ */ new Set(),
40
+ workspace: true
41
+ };
42
+ if (pkg.private)
43
+ node.private = true;
44
+ packages.set(node.spec, node);
45
+ return {
46
+ pkg,
47
+ node
48
+ };
49
+ });
50
+ const mapNormalize = /* @__PURE__ */ new WeakMap();
15
51
  function normalize(raw) {
16
- let node = map.get(raw);
52
+ let node = mapNormalize.get(raw);
17
53
  if (node)
18
54
  return node;
19
- node = {
20
- spec: `${raw.from}@${raw.version}`,
55
+ let version = raw.version;
56
+ if (version.includes(":")) {
57
+ const workspaceMapping = workspacePackages.find((i) => i.pkg.path === raw.path);
58
+ if (workspaceMapping)
59
+ version = workspaceMapping.node.version;
60
+ }
61
+ const spec = `${raw.from}@${version}`;
62
+ node = packages.get(spec) || {
63
+ spec,
21
64
  name: raw.from,
22
- version: raw.version,
23
- path: raw.path,
24
- dependencies: /* @__PURE__ */ new Set(),
25
- dependents: /* @__PURE__ */ new Set(),
26
- flatDependents: /* @__PURE__ */ new Set(),
27
- flatDependencies: /* @__PURE__ */ new Set(),
28
- nestedLevels: /* @__PURE__ */ new Set(),
29
- dev: false,
30
- prod: false,
31
- optional: false
65
+ version,
66
+ filepath: raw.path,
67
+ dependencies: /* @__PURE__ */ new Set()
32
68
  };
33
- map.set(raw, node);
69
+ mapNormalize.set(raw, node);
34
70
  return node;
35
71
  }
36
- function traverse(_node, level, mode, directImporter, nestedImporter) {
37
- const node = normalize(_node);
38
- if (directImporter)
39
- node.dependents.add(directImporter);
40
- for (const im of nestedImporter)
41
- node.flatDependents.add(im);
42
- node.nestedLevels.add(level);
43
- if (mode === "dev")
44
- node.dev = true;
45
- if (mode === "prod")
46
- node.prod = true;
47
- if (mode === "optional")
48
- node.optional = true;
72
+ function traverse(raw, level, mode) {
73
+ const node = normalize(raw);
74
+ if (!node.workspace) {
75
+ if (mode === "dev")
76
+ node.dev = true;
77
+ if (mode === "prod")
78
+ node.prod = true;
79
+ if (mode === "optional")
80
+ node.optional = true;
81
+ }
49
82
  if (options.traverseFilter?.(node) === false)
50
- return;
51
- if (specs.has(node.spec))
52
- return;
53
- specs.set(node.spec, node);
54
- for (const dep of Object.values(_node.dependencies || {})) {
55
- traverse(dep, level + 1, mode, node.spec, [...nestedImporter, node.spec]);
83
+ return node;
84
+ if (packages.has(node.spec))
85
+ return node;
86
+ packages.set(node.spec, node);
87
+ for (const dep of Object.values(raw.dependencies || {})) {
88
+ const resolvedDep = traverse(dep, level + 1, mode);
89
+ node.dependencies.add(resolvedDep.spec);
56
90
  }
91
+ return node;
57
92
  }
58
- for (const pkg of tree) {
93
+ for (const { pkg, node } of workspacePackages) {
59
94
  for (const dep of Object.values(pkg.dependencies || {})) {
60
- traverse(dep, 1, "prod", undefined, []);
95
+ const result = traverse(dep, 1, "prod");
96
+ node.dependencies.add(result.spec);
61
97
  }
62
98
  for (const dep of Object.values(pkg.devDependencies || {})) {
63
- traverse(dep, 1, "dev", undefined, []);
64
- }
65
- }
66
- const packages = [...specs.values()].sort((a, b) => a.spec.localeCompare(b.spec));
67
- for (const pkg of packages) {
68
- for (const dep of pkg.flatDependents) {
69
- const node = specs.get(dep);
70
- if (node)
71
- node.flatDependencies.add(pkg.spec);
99
+ const result = traverse(dep, 1, "dev");
100
+ node.dependencies.add(result.spec);
72
101
  }
73
102
  }
74
103
  return {
75
- cwd: options.cwd,
104
+ root,
76
105
  packageManager: "pnpm",
106
+ packageManagerVersion: await getPnpmVersion(options),
77
107
  packages
78
108
  };
79
109
  }
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  type PackageModuleTypeSimple = 'cjs' | 'esm';
2
- type PackageModuleType = 'cjs' | 'esm' | 'dual' | 'faux' | 'none';
2
+ type PackageModuleType = 'cjs' | 'esm' | 'dual' | 'faux' | 'dts';
3
3
  interface ListPackageDependenciesOptions {
4
4
  /**
5
5
  * Current working directory
@@ -16,14 +16,21 @@ interface ListPackageDependenciesOptions {
16
16
  /**
17
17
  * Filter if a package should be included and continue traversing
18
18
  */
19
- traverseFilter?: (node: PackageNode) => boolean;
19
+ traverseFilter?: (node: PackageNodeRaw) => boolean;
20
20
  }
21
- interface ListPackageDependenciesResult {
22
- cwd: string;
21
+ interface ListPackageDependenciesRawResult {
22
+ root: string;
23
23
  packageManager: string;
24
- packages: PackageNode[];
24
+ packageManagerVersion?: string;
25
+ packages: Map<string, PackageNodeRaw>;
26
+ }
27
+ interface ListPackageDependenciesBaseResult extends ListPackageDependenciesRawResult {
28
+ packages: Map<string, PackageNodeBase>;
25
29
  }
26
- interface PackageNode {
30
+ interface ListPackageDependenciesResult extends ListPackageDependenciesBaseResult {
31
+ packages: Map<string, PackageNode>;
32
+ }
33
+ interface PackageNodeRaw {
27
34
  /** Package Name */
28
35
  name: string;
29
36
  /** Version */
@@ -31,39 +38,49 @@ interface PackageNode {
31
38
  /** Combined name and version using `@` */
32
39
  spec: string;
33
40
  /** Absolute file path of the package */
34
- path: string;
41
+ filepath: string;
35
42
  /** Direct dependencies of this package */
36
43
  dependencies: Set<string>;
44
+ /** Is this package from local workspace */
45
+ workspace?: boolean;
46
+ /** Is this package private */
47
+ private?: boolean;
48
+ /** Is this package part of devDependencies */
49
+ dev?: boolean;
50
+ /** Is this package part of dependencies */
51
+ prod?: boolean;
52
+ /** Is this package part of optionalDependencies */
53
+ optional?: boolean;
54
+ }
55
+ interface PackageNodeBase extends PackageNodeRaw {
37
56
  /** Direct dependents of this package */
38
57
  dependents: Set<string>;
58
+ /** The lowest depth of this package */
59
+ depth: number;
39
60
  /** All nested dependencies of this package */
40
61
  flatDependencies: Set<string>;
41
62
  /** All nested dependents of this package */
42
63
  flatDependents: Set<string>;
43
- /** Nested levels of this package */
44
- nestedLevels: Set<number>;
45
- /** Is this package part of devDependencies */
46
- dev: boolean;
47
- /** Is this package part of dependencies */
48
- prod: boolean;
49
- /** Is this package part of optionalDependencies */
50
- optional: boolean;
51
64
  }
52
- interface ResolvedPackageNode extends PackageNode {
65
+ interface PackageNode extends PackageNodeBase {
53
66
  resolved: {
54
67
  module: PackageModuleType;
55
68
  license?: string;
56
69
  author?: string;
57
70
  repository?: string;
71
+ homepage?: string;
58
72
  engines?: Record<string, string>;
59
73
  };
60
74
  }
61
75
 
76
+ declare const PackageModuleTypes: readonly PackageModuleType[];
77
+
62
78
  /**
63
79
  * List dependencies of packages in the current project.
64
80
  *
65
81
  * This function will automatically detect the package manager in the current project, and list the dependencies of the packages.
66
82
  */
83
+ declare function listPackageDependenciesRaw(options: ListPackageDependenciesOptions): Promise<ListPackageDependenciesRawResult>;
67
84
  declare function listPackageDependencies(options: ListPackageDependenciesOptions): Promise<ListPackageDependenciesResult>;
68
85
 
69
86
  /**
@@ -72,6 +89,8 @@ declare function listPackageDependencies(options: ListPackageDependenciesOptions
72
89
  *
73
90
  * - Set `module` to the resolved module type (cjs, esm, dual, faux, none).
74
91
  */
75
- declare function resolvePackage(pkg: PackageNode): Promise<ResolvedPackageNode>;
92
+ declare function resolvePackage(pkg: PackageNodeBase): Promise<PackageNode>;
93
+
94
+ declare function stripBomTag(content: string): string;
76
95
 
77
- export { type ListPackageDependenciesOptions, type ListPackageDependenciesResult, type PackageModuleType, type PackageModuleTypeSimple, type PackageNode, type ResolvedPackageNode, listPackageDependencies, resolvePackage };
96
+ export { type ListPackageDependenciesBaseResult, type ListPackageDependenciesOptions, type ListPackageDependenciesRawResult, type ListPackageDependenciesResult, type PackageModuleType, type PackageModuleTypeSimple, PackageModuleTypes, type PackageNode, type PackageNodeBase, type PackageNodeRaw, listPackageDependencies, listPackageDependenciesRaw, resolvePackage, stripBomTag };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  type PackageModuleTypeSimple = 'cjs' | 'esm';
2
- type PackageModuleType = 'cjs' | 'esm' | 'dual' | 'faux' | 'none';
2
+ type PackageModuleType = 'cjs' | 'esm' | 'dual' | 'faux' | 'dts';
3
3
  interface ListPackageDependenciesOptions {
4
4
  /**
5
5
  * Current working directory
@@ -16,14 +16,21 @@ interface ListPackageDependenciesOptions {
16
16
  /**
17
17
  * Filter if a package should be included and continue traversing
18
18
  */
19
- traverseFilter?: (node: PackageNode) => boolean;
19
+ traverseFilter?: (node: PackageNodeRaw) => boolean;
20
20
  }
21
- interface ListPackageDependenciesResult {
22
- cwd: string;
21
+ interface ListPackageDependenciesRawResult {
22
+ root: string;
23
23
  packageManager: string;
24
- packages: PackageNode[];
24
+ packageManagerVersion?: string;
25
+ packages: Map<string, PackageNodeRaw>;
26
+ }
27
+ interface ListPackageDependenciesBaseResult extends ListPackageDependenciesRawResult {
28
+ packages: Map<string, PackageNodeBase>;
25
29
  }
26
- interface PackageNode {
30
+ interface ListPackageDependenciesResult extends ListPackageDependenciesBaseResult {
31
+ packages: Map<string, PackageNode>;
32
+ }
33
+ interface PackageNodeRaw {
27
34
  /** Package Name */
28
35
  name: string;
29
36
  /** Version */
@@ -31,39 +38,49 @@ interface PackageNode {
31
38
  /** Combined name and version using `@` */
32
39
  spec: string;
33
40
  /** Absolute file path of the package */
34
- path: string;
41
+ filepath: string;
35
42
  /** Direct dependencies of this package */
36
43
  dependencies: Set<string>;
44
+ /** Is this package from local workspace */
45
+ workspace?: boolean;
46
+ /** Is this package private */
47
+ private?: boolean;
48
+ /** Is this package part of devDependencies */
49
+ dev?: boolean;
50
+ /** Is this package part of dependencies */
51
+ prod?: boolean;
52
+ /** Is this package part of optionalDependencies */
53
+ optional?: boolean;
54
+ }
55
+ interface PackageNodeBase extends PackageNodeRaw {
37
56
  /** Direct dependents of this package */
38
57
  dependents: Set<string>;
58
+ /** The lowest depth of this package */
59
+ depth: number;
39
60
  /** All nested dependencies of this package */
40
61
  flatDependencies: Set<string>;
41
62
  /** All nested dependents of this package */
42
63
  flatDependents: Set<string>;
43
- /** Nested levels of this package */
44
- nestedLevels: Set<number>;
45
- /** Is this package part of devDependencies */
46
- dev: boolean;
47
- /** Is this package part of dependencies */
48
- prod: boolean;
49
- /** Is this package part of optionalDependencies */
50
- optional: boolean;
51
64
  }
52
- interface ResolvedPackageNode extends PackageNode {
65
+ interface PackageNode extends PackageNodeBase {
53
66
  resolved: {
54
67
  module: PackageModuleType;
55
68
  license?: string;
56
69
  author?: string;
57
70
  repository?: string;
71
+ homepage?: string;
58
72
  engines?: Record<string, string>;
59
73
  };
60
74
  }
61
75
 
76
+ declare const PackageModuleTypes: readonly PackageModuleType[];
77
+
62
78
  /**
63
79
  * List dependencies of packages in the current project.
64
80
  *
65
81
  * This function will automatically detect the package manager in the current project, and list the dependencies of the packages.
66
82
  */
83
+ declare function listPackageDependenciesRaw(options: ListPackageDependenciesOptions): Promise<ListPackageDependenciesRawResult>;
67
84
  declare function listPackageDependencies(options: ListPackageDependenciesOptions): Promise<ListPackageDependenciesResult>;
68
85
 
69
86
  /**
@@ -72,6 +89,8 @@ declare function listPackageDependencies(options: ListPackageDependenciesOptions
72
89
  *
73
90
  * - Set `module` to the resolved module type (cjs, esm, dual, faux, none).
74
91
  */
75
- declare function resolvePackage(pkg: PackageNode): Promise<ResolvedPackageNode>;
92
+ declare function resolvePackage(pkg: PackageNodeBase): Promise<PackageNode>;
93
+
94
+ declare function stripBomTag(content: string): string;
76
95
 
77
- export { type ListPackageDependenciesOptions, type ListPackageDependenciesResult, type PackageModuleType, type PackageModuleTypeSimple, type PackageNode, type ResolvedPackageNode, listPackageDependencies, resolvePackage };
96
+ export { type ListPackageDependenciesBaseResult, type ListPackageDependenciesOptions, type ListPackageDependenciesRawResult, type ListPackageDependenciesResult, type PackageModuleType, type PackageModuleTypeSimple, PackageModuleTypes, type PackageNode, type PackageNodeBase, type PackageNodeRaw, listPackageDependencies, listPackageDependenciesRaw, resolvePackage, stripBomTag };
package/dist/index.mjs CHANGED
@@ -1,18 +1,8 @@
1
1
  import { detect } from 'package-manager-detector';
2
2
  import fs from 'node:fs/promises';
3
- import { join } from 'node:path';
3
+ import { join } from 'pathe';
4
4
 
5
- async function listPackageDependencies(options) {
6
- const manager = await detect({
7
- cwd: options.cwd
8
- });
9
- if (!manager)
10
- throw new Error("Cannot detect package manager in the current patch");
11
- if (manager.name === "pnpm")
12
- return await import('./chunks/pnpm.mjs').then((r) => r.listPackageDependencies(options));
13
- else
14
- throw new Error(`Package manager ${manager.name} is not yet supported`);
15
- }
5
+ const PackageModuleTypes = Object.freeze(["cjs", "esm", "dual", "faux", "dts"]);
16
6
 
17
7
  function analyzePackageModuleType(pkgJson) {
18
8
  const { exports, main, type } = pkgJson;
@@ -52,8 +42,8 @@ function analyzePackageModuleType(pkgJson) {
52
42
  return "esm";
53
43
  if (fauxEsm)
54
44
  return "faux";
55
- if (!esm && !cjs)
56
- return "none";
45
+ if (!esm && !cjs && !pkgJson.main && !pkgJson.exports && pkgJson.types)
46
+ return "dts";
57
47
  return "cjs";
58
48
  function analyzeThing(value, path) {
59
49
  if (value && typeof value === "object") {
@@ -123,16 +113,111 @@ async function resolvePackage(pkg) {
123
113
  const _pkg = pkg;
124
114
  if (_pkg.resolved)
125
115
  return _pkg;
126
- const content = await fs.readFile(join(pkg.path, "package.json"), "utf-8");
116
+ const content = await fs.readFile(join(pkg.filepath, "package.json"), "utf-8");
127
117
  const json = JSON.parse(stripBomTag(content));
118
+ let repository = typeof json.repository === "string" ? json.repository : json.repository?.url;
119
+ if (repository?.startsWith("git+"))
120
+ repository = repository.slice(4);
121
+ if (repository?.endsWith(".git"))
122
+ repository = repository.slice(0, -4);
123
+ if (repository?.startsWith("git://"))
124
+ repository = `https://${repository.slice(6)}`;
125
+ if (json.repository && typeof json.repository !== "string" && json.repository.directory)
126
+ repository += `/tree/HEAD/${json.repository.directory}`;
128
127
  _pkg.resolved = {
129
128
  module: analyzePackageModuleType(json),
130
129
  engines: json.engines,
131
130
  license: json.license,
132
131
  author: typeof json.author === "string" ? json.author : json.author?.url,
133
- repository: typeof json.repository === "string" ? json.repository : json.repository?.url
132
+ repository,
133
+ homepage: json.homepage
134
134
  };
135
135
  return _pkg;
136
136
  }
137
137
 
138
- export { listPackageDependencies, resolvePackage };
138
+ async function listPackageDependenciesRaw(options) {
139
+ const manager = await detect({
140
+ cwd: options.cwd
141
+ });
142
+ if (!manager)
143
+ throw new Error("Cannot detect package manager in the current patch");
144
+ let result;
145
+ if (manager.name === "pnpm")
146
+ result = await import('./chunks/pnpm.mjs').then((r) => r.listPackageDependencies(options));
147
+ else
148
+ throw new Error(`Package manager ${manager.name} is not yet supported`);
149
+ return populateRawResult(result);
150
+ }
151
+ function populateRawResult(input) {
152
+ const result = {
153
+ ...input,
154
+ packages: /* @__PURE__ */ new Map()
155
+ };
156
+ for (const [spec, pkg] of input.packages) {
157
+ const node = Object.assign(pkg, {
158
+ dependents: /* @__PURE__ */ new Set(),
159
+ flatDependencies: /* @__PURE__ */ new Set(),
160
+ flatDependents: /* @__PURE__ */ new Set(),
161
+ depth: pkg.workspace ? 0 : Infinity
162
+ });
163
+ result.packages.set(spec, node);
164
+ }
165
+ for (const pkg of result.packages.values()) {
166
+ for (const dep of pkg.dependencies) {
167
+ result.packages.get(dep)?.dependents.add(pkg.spec);
168
+ }
169
+ }
170
+ function resloveFlatDependencies(pkg) {
171
+ const postTasks = [];
172
+ function traverseDependencies(node, seen = /* @__PURE__ */ new Set()) {
173
+ for (const dep of node.dependencies) {
174
+ const level = node.depth + 1;
175
+ const depNode = result.packages.get(dep);
176
+ if (!node.workspace) {
177
+ if (node.dev)
178
+ depNode.dev = true;
179
+ if (node.prod)
180
+ depNode.prod = true;
181
+ if (node.optional)
182
+ depNode.optional = true;
183
+ }
184
+ if (depNode.depth > level)
185
+ depNode.depth = level;
186
+ if (seen.has(depNode))
187
+ continue;
188
+ pkg.flatDependencies.add(dep);
189
+ seen.add(depNode);
190
+ postTasks.push(() => {
191
+ depNode.flatDependents.add(pkg.spec);
192
+ });
193
+ traverseDependencies(depNode, seen);
194
+ }
195
+ }
196
+ function traverseDependents(node) {
197
+ for (const dep of node.dependents) {
198
+ if (pkg.flatDependents.has(dep))
199
+ continue;
200
+ pkg.flatDependents.add(dep);
201
+ const parentNode = result.packages.get(dep);
202
+ postTasks.push(() => {
203
+ parentNode.flatDependencies.add(pkg.spec);
204
+ });
205
+ traverseDependents(parentNode);
206
+ }
207
+ }
208
+ traverseDependencies(pkg);
209
+ traverseDependents(pkg);
210
+ for (const task of postTasks)
211
+ task();
212
+ }
213
+ for (const pkg of result.packages.values())
214
+ resloveFlatDependencies(pkg);
215
+ return result;
216
+ }
217
+ async function listPackageDependencies(options) {
218
+ const result = await listPackageDependenciesRaw(options);
219
+ await Promise.all(Array.from(result.packages.values()).map(async (pkg) => resolvePackage(pkg)));
220
+ return result;
221
+ }
222
+
223
+ export { PackageModuleTypes, listPackageDependencies, listPackageDependenciesRaw, resolvePackage, stripBomTag };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "node-modules-tools",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "description": "Tools for inspecting node_modules",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",
@@ -33,12 +33,13 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
- "package-manager-detector": "^0.2.8",
36
+ "package-manager-detector": "^0.2.9",
37
+ "pathe": "^2.0.2",
37
38
  "tinyexec": "^0.3.2"
38
39
  },
39
40
  "devDependencies": {
40
- "@pnpm/list": "^1000.0.5",
41
- "@pnpm/types": "^1000.1.0",
41
+ "@pnpm/list": "^1000.0.6",
42
+ "@pnpm/types": "^1000.1.1",
42
43
  "pkg-types": "^1.3.1",
43
44
  "unbuild": "^3.3.1"
44
45
  },