@decohere/forge 0.0.1-dev.120 → 0.0.1-dev.122
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/LICENSE +15 -0
- package/README.md +9 -0
- package/commonjs/src/actions/copy.action.js +19 -0
- package/commonjs/src/services/dependency-resolver.service.js +69 -23
- package/es6/src/actions/copy.action.js +19 -0
- package/es6/src/services/dependency-resolver.service.js +69 -23
- package/package.json +6 -6
- package/types/src/actions/copy.action.d.ts +5 -0
- package/types/src/services/dependency-resolver.service.d.ts +7 -0
- package/types/src/types/dependency.types.d.ts +7 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Typesmiths
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -136,6 +136,15 @@ export default {
|
|
|
136
136
|
|
|
137
137
|
Design record: [ADR 0006](../../docs/adr/0006-forge-package-deployables.md).
|
|
138
138
|
|
|
139
|
+
## `forge dependencies`: package manifests from imports
|
|
140
|
+
|
|
141
|
+
There is one install, from the root `package.json`, and it owns every version. `forge dependencies sync` writes each package's `dependencies` and `devDependencies` from what its code imports, using the root's ranges. `forge dependencies report --fail-on-drift` fails when a manifest differs from what sync would write; CI runs it.
|
|
142
|
+
|
|
143
|
+
- **Peers** are declared by name in the package's own `peerDependencies`. Sync sets the root's range and keeps them out of the other two lists, even when no code imports them. Mark optional ones in `peerDependenciesMeta` by hand.
|
|
144
|
+
- **Under the release configuration**, which scans only shipped code, importing a package the root lists only as a devDependency is an error unless the package declares it a peer.
|
|
145
|
+
|
|
146
|
+
Design record: [ADR 0017](../../docs/adr/0017-dependency-manifests-from-imports.md).
|
|
147
|
+
|
|
139
148
|
## `forge link` clears `dist`
|
|
140
149
|
|
|
141
150
|
`forge link` is a development tool: it symlinks build output into a consumer's `node_modules`. Like `forge build`, it cleans `dist` first. Without `--build` it then links an output folder holding only `package.json` files, so run it with `--build` (or build again afterwards). It is not a way to make a release build runnable; use `forge package` for that.
|
|
@@ -51,11 +51,30 @@ class Copy {
|
|
|
51
51
|
cwd
|
|
52
52
|
});
|
|
53
53
|
await Promise.all(rootFiles.map((file)=>this.copyFileToOut(file, root)));
|
|
54
|
+
await this.copyWorkspaceLicense(proj, rootFiles, root);
|
|
54
55
|
// Configuration-only globs → each pack target outDir (next to emitted JS).
|
|
55
56
|
await this.copyColocatedPackAssets(proj, cwd);
|
|
56
57
|
}));
|
|
57
58
|
return _cli.ActionResponse.Success(Copy.name);
|
|
58
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* A package without a LICENSE of its own ships the workspace's, so a monorepo
|
|
62
|
+
* licensed once at the root still puts the license text in every tarball.
|
|
63
|
+
*/ async copyWorkspaceLicense(proj, rootFiles, outDir) {
|
|
64
|
+
if (!proj.configuration.pack) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (rootFiles.some((file)=>/^LICEN[CS]E/i.test(file.relativePath))) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const licenses = await (0, _io1.findFiles)([
|
|
71
|
+
"LICENSE*",
|
|
72
|
+
"LICENCE*"
|
|
73
|
+
], {
|
|
74
|
+
cwd: this.workspace.rootDir
|
|
75
|
+
});
|
|
76
|
+
await Promise.all(licenses.map((file)=>this.copyFileToOut(file, outDir)));
|
|
77
|
+
}
|
|
59
78
|
/**
|
|
60
79
|
* Packed builds emit JS under `<out>/<target>/` (e.g. `es6/`). Assets listed on
|
|
61
80
|
* the *configuration* `copyFiles` (not the workspace defaults) are also copied
|
|
@@ -21,24 +21,22 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
21
21
|
}
|
|
22
22
|
class DependencyResolver {
|
|
23
23
|
computeDrift(current, computed) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const lists = (pkg)=>{
|
|
25
|
+
const sortKeys = (obj)=>Object.fromEntries(Object.entries(obj ?? {}).sort());
|
|
26
|
+
return {
|
|
27
|
+
dependencies: sortKeys(pkg.dependencies),
|
|
28
|
+
devDependencies: sortKeys(pkg.devDependencies),
|
|
29
|
+
peerDependencies: sortKeys(pkg.peerDependencies)
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const original = lists(current);
|
|
33
|
+
const updated = lists(computed);
|
|
34
|
+
if (JSON.stringify(original) === JSON.stringify(updated)) {
|
|
31
35
|
return null;
|
|
32
36
|
}
|
|
33
37
|
return {
|
|
34
|
-
original
|
|
35
|
-
|
|
36
|
-
devDependencies: currentDevDeps
|
|
37
|
-
},
|
|
38
|
-
updated: {
|
|
39
|
-
dependencies: computedDeps,
|
|
40
|
-
devDependencies: computedDevDeps
|
|
41
|
-
}
|
|
38
|
+
original,
|
|
39
|
+
updated
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
async getWorkspacePackages(workspace, projects) {
|
|
@@ -78,29 +76,77 @@ class DependencyResolver {
|
|
|
78
76
|
]);
|
|
79
77
|
}
|
|
80
78
|
async computeProjectDependencies(project, workspacePackages) {
|
|
81
|
-
const { dependencies, devDependencies, warnings } = await this.getDependencies(project, workspacePackages);
|
|
82
79
|
const pkg = await project.getPackageJson();
|
|
83
|
-
const
|
|
80
|
+
const peerNames = Object.keys(pkg.peerDependencies ?? {});
|
|
81
|
+
const peerDependencies = this.getPeerDependencies(project, peerNames, workspacePackages);
|
|
82
|
+
const { dependencies, devDependencies, warnings } = await this.getDependencies(project, workspacePackages, new Set(peerNames));
|
|
83
|
+
let updatedPackage = pkg.withDependencies(dependencies).withDevDependencies(devDependencies);
|
|
84
|
+
if (peerNames.length > 0) {
|
|
85
|
+
updatedPackage = updatedPackage.withPeerDependencies(peerDependencies);
|
|
86
|
+
}
|
|
84
87
|
return {
|
|
85
88
|
project,
|
|
86
89
|
updatedPackage,
|
|
87
90
|
warnings
|
|
88
91
|
};
|
|
89
92
|
}
|
|
90
|
-
|
|
93
|
+
/**
|
|
94
|
+
* A package declares its peers by name in its own `peerDependencies` (and marks optional ones in
|
|
95
|
+
* `peerDependenciesMeta`, which sync leaves as written). The root owns every version, so each
|
|
96
|
+
* peer gets the root's range; a peer the root doesn't list is an error. A declared peer stays
|
|
97
|
+
* even when no code imports it: a pino transport, say, is loaded by name.
|
|
98
|
+
*/ getPeerDependencies(project, peerNames, workspacePackages) {
|
|
99
|
+
const peers = {};
|
|
100
|
+
for (const name of [
|
|
101
|
+
...peerNames
|
|
102
|
+
].sort()){
|
|
103
|
+
const version = workspacePackages.get(name)?.version;
|
|
104
|
+
if (version == null) {
|
|
105
|
+
throw new _errors.ForgeError(`"${name}" is declared a peer of ${project.name}, but the root package.json doesn't list it. ` + "Add it there: the root owns every version.");
|
|
106
|
+
}
|
|
107
|
+
peers[name] = version;
|
|
108
|
+
}
|
|
109
|
+
return peers;
|
|
110
|
+
}
|
|
111
|
+
async getDependencies(project, workspacePackages, peers) {
|
|
91
112
|
const files = await project.getAllFiles();
|
|
92
|
-
const results = await Promise.all(files.map((file)=>
|
|
93
|
-
|
|
113
|
+
const results = await Promise.all(files.map(async (file)=>({
|
|
114
|
+
file,
|
|
115
|
+
...await this.getFileImports(file)
|
|
116
|
+
})));
|
|
94
117
|
const warnings = results.flatMap((r)=>r.warnings);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
118
|
+
/** Which files import each package, for errors that point at the code. */ const importedBy = new Map();
|
|
119
|
+
const allDependencies = [];
|
|
120
|
+
for (const { file, imports } of results){
|
|
121
|
+
for (const fileImport of imports){
|
|
122
|
+
const dep = this.getDependencyFrom(fileImport, workspacePackages);
|
|
123
|
+
if (dep == null || peers.has(dep.name)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!importedBy.has(dep.name)) {
|
|
127
|
+
importedBy.set(dep.name, new Set());
|
|
128
|
+
allDependencies.push(dep);
|
|
129
|
+
}
|
|
130
|
+
importedBy.get(dep.name)?.add(file.relativePath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
98
133
|
const missing = allDependencies.filter((dep)=>dep.missing);
|
|
99
134
|
if (missing.length > 0) {
|
|
100
135
|
const names = missing.map((dep)=>dep.name).join(", ");
|
|
101
136
|
throw new _errors.ForgeError(`Missing packages in project "${project.name}": ${names}`);
|
|
102
137
|
}
|
|
103
138
|
const resolved = allDependencies.filter((dep)=>!dep.missing);
|
|
139
|
+
// A configuration that packs is the published shape: its files ship, so a package they
|
|
140
|
+
// import that the root keeps for development would be missing for the consumer.
|
|
141
|
+
if (project.configuration.pack === true) {
|
|
142
|
+
const devOnly = resolved.filter((dep)=>dep.isDev && !dep.internal);
|
|
143
|
+
if (devOnly.length > 0) {
|
|
144
|
+
const lines = devOnly.map((dep)=>` - ${dep.name}, imported by ${[
|
|
145
|
+
...importedBy.get(dep.name) ?? []
|
|
146
|
+
].sort().join(", ")}`);
|
|
147
|
+
throw new _errors.ForgeError(`${project.name} ships code that imports packages the root lists only as a devDependency:\n` + `${lines.join("\n")}\n` + "Move each to the root's dependencies, or, if the consumer provides it, " + `declare it in ${project.name}'s peerDependencies.`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
104
150
|
const dependencies = Object.fromEntries(resolved.filter((dep)=>!dep.isDev).map((dep)=>[
|
|
105
151
|
dep.name,
|
|
106
152
|
dep.version
|
|
@@ -36,11 +36,30 @@ export class Copy {
|
|
|
36
36
|
cwd
|
|
37
37
|
});
|
|
38
38
|
await Promise.all(rootFiles.map((file)=>this.copyFileToOut(file, root)));
|
|
39
|
+
await this.copyWorkspaceLicense(proj, rootFiles, root);
|
|
39
40
|
// Configuration-only globs → each pack target outDir (next to emitted JS).
|
|
40
41
|
await this.copyColocatedPackAssets(proj, cwd);
|
|
41
42
|
}));
|
|
42
43
|
return ActionResponse.Success(Copy.name);
|
|
43
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* A package without a LICENSE of its own ships the workspace's, so a monorepo
|
|
47
|
+
* licensed once at the root still puts the license text in every tarball.
|
|
48
|
+
*/ async copyWorkspaceLicense(proj, rootFiles, outDir) {
|
|
49
|
+
if (!proj.configuration.pack) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (rootFiles.some((file)=>/^LICEN[CS]E/i.test(file.relativePath))) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const licenses = await findFiles([
|
|
56
|
+
"LICENSE*",
|
|
57
|
+
"LICENCE*"
|
|
58
|
+
], {
|
|
59
|
+
cwd: this.workspace.rootDir
|
|
60
|
+
});
|
|
61
|
+
await Promise.all(licenses.map((file)=>this.copyFileToOut(file, outDir)));
|
|
62
|
+
}
|
|
44
63
|
/**
|
|
45
64
|
* Packed builds emit JS under `<out>/<target>/` (e.g. `es6/`). Assets listed on
|
|
46
65
|
* the *configuration* `copyFiles` (not the workspace defaults) are also copied
|
|
@@ -11,24 +11,22 @@ import { BUILT_IN_MODULES } from "../const.js";
|
|
|
11
11
|
import { ForgeError } from "../errors.js";
|
|
12
12
|
export class DependencyResolver {
|
|
13
13
|
computeDrift(current, computed) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
const lists = (pkg)=>{
|
|
15
|
+
const sortKeys = (obj)=>Object.fromEntries(Object.entries(obj ?? {}).sort());
|
|
16
|
+
return {
|
|
17
|
+
dependencies: sortKeys(pkg.dependencies),
|
|
18
|
+
devDependencies: sortKeys(pkg.devDependencies),
|
|
19
|
+
peerDependencies: sortKeys(pkg.peerDependencies)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const original = lists(current);
|
|
23
|
+
const updated = lists(computed);
|
|
24
|
+
if (JSON.stringify(original) === JSON.stringify(updated)) {
|
|
21
25
|
return null;
|
|
22
26
|
}
|
|
23
27
|
return {
|
|
24
|
-
original
|
|
25
|
-
|
|
26
|
-
devDependencies: currentDevDeps
|
|
27
|
-
},
|
|
28
|
-
updated: {
|
|
29
|
-
dependencies: computedDeps,
|
|
30
|
-
devDependencies: computedDevDeps
|
|
31
|
-
}
|
|
28
|
+
original,
|
|
29
|
+
updated
|
|
32
30
|
};
|
|
33
31
|
}
|
|
34
32
|
async getWorkspacePackages(workspace, projects) {
|
|
@@ -68,29 +66,77 @@ export class DependencyResolver {
|
|
|
68
66
|
]);
|
|
69
67
|
}
|
|
70
68
|
async computeProjectDependencies(project, workspacePackages) {
|
|
71
|
-
const { dependencies, devDependencies, warnings } = await this.getDependencies(project, workspacePackages);
|
|
72
69
|
const pkg = await project.getPackageJson();
|
|
73
|
-
const
|
|
70
|
+
const peerNames = Object.keys(pkg.peerDependencies ?? {});
|
|
71
|
+
const peerDependencies = this.getPeerDependencies(project, peerNames, workspacePackages);
|
|
72
|
+
const { dependencies, devDependencies, warnings } = await this.getDependencies(project, workspacePackages, new Set(peerNames));
|
|
73
|
+
let updatedPackage = pkg.withDependencies(dependencies).withDevDependencies(devDependencies);
|
|
74
|
+
if (peerNames.length > 0) {
|
|
75
|
+
updatedPackage = updatedPackage.withPeerDependencies(peerDependencies);
|
|
76
|
+
}
|
|
74
77
|
return {
|
|
75
78
|
project,
|
|
76
79
|
updatedPackage,
|
|
77
80
|
warnings
|
|
78
81
|
};
|
|
79
82
|
}
|
|
80
|
-
|
|
83
|
+
/**
|
|
84
|
+
* A package declares its peers by name in its own `peerDependencies` (and marks optional ones in
|
|
85
|
+
* `peerDependenciesMeta`, which sync leaves as written). The root owns every version, so each
|
|
86
|
+
* peer gets the root's range; a peer the root doesn't list is an error. A declared peer stays
|
|
87
|
+
* even when no code imports it: a pino transport, say, is loaded by name.
|
|
88
|
+
*/ getPeerDependencies(project, peerNames, workspacePackages) {
|
|
89
|
+
const peers = {};
|
|
90
|
+
for (const name of [
|
|
91
|
+
...peerNames
|
|
92
|
+
].sort()){
|
|
93
|
+
const version = workspacePackages.get(name)?.version;
|
|
94
|
+
if (version == null) {
|
|
95
|
+
throw new ForgeError(`"${name}" is declared a peer of ${project.name}, but the root package.json doesn't list it. ` + "Add it there: the root owns every version.");
|
|
96
|
+
}
|
|
97
|
+
peers[name] = version;
|
|
98
|
+
}
|
|
99
|
+
return peers;
|
|
100
|
+
}
|
|
101
|
+
async getDependencies(project, workspacePackages, peers) {
|
|
81
102
|
const files = await project.getAllFiles();
|
|
82
|
-
const results = await Promise.all(files.map((file)=>
|
|
83
|
-
|
|
103
|
+
const results = await Promise.all(files.map(async (file)=>({
|
|
104
|
+
file,
|
|
105
|
+
...await this.getFileImports(file)
|
|
106
|
+
})));
|
|
84
107
|
const warnings = results.flatMap((r)=>r.warnings);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
108
|
+
/** Which files import each package, for errors that point at the code. */ const importedBy = new Map();
|
|
109
|
+
const allDependencies = [];
|
|
110
|
+
for (const { file, imports } of results){
|
|
111
|
+
for (const fileImport of imports){
|
|
112
|
+
const dep = this.getDependencyFrom(fileImport, workspacePackages);
|
|
113
|
+
if (dep == null || peers.has(dep.name)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!importedBy.has(dep.name)) {
|
|
117
|
+
importedBy.set(dep.name, new Set());
|
|
118
|
+
allDependencies.push(dep);
|
|
119
|
+
}
|
|
120
|
+
importedBy.get(dep.name)?.add(file.relativePath);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
88
123
|
const missing = allDependencies.filter((dep)=>dep.missing);
|
|
89
124
|
if (missing.length > 0) {
|
|
90
125
|
const names = missing.map((dep)=>dep.name).join(", ");
|
|
91
126
|
throw new ForgeError(`Missing packages in project "${project.name}": ${names}`);
|
|
92
127
|
}
|
|
93
128
|
const resolved = allDependencies.filter((dep)=>!dep.missing);
|
|
129
|
+
// A configuration that packs is the published shape: its files ship, so a package they
|
|
130
|
+
// import that the root keeps for development would be missing for the consumer.
|
|
131
|
+
if (project.configuration.pack === true) {
|
|
132
|
+
const devOnly = resolved.filter((dep)=>dep.isDev && !dep.internal);
|
|
133
|
+
if (devOnly.length > 0) {
|
|
134
|
+
const lines = devOnly.map((dep)=>` - ${dep.name}, imported by ${[
|
|
135
|
+
...importedBy.get(dep.name) ?? []
|
|
136
|
+
].sort().join(", ")}`);
|
|
137
|
+
throw new ForgeError(`${project.name} ships code that imports packages the root lists only as a devDependency:\n` + `${lines.join("\n")}\n` + "Move each to the root's dependencies, or, if the consumer provides it, " + `declare it in ${project.name}'s peerDependencies.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
94
140
|
const dependencies = Object.fromEntries(resolved.filter((dep)=>!dep.isDev).map((dep)=>[
|
|
95
141
|
dep.name,
|
|
96
142
|
dep.version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decohere/forge",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.122",
|
|
4
4
|
"description": "TypeScript monorepo build system — zero config, fast compilation, streamlined developer workflow",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@decohere/core": "0.0.1-dev.
|
|
33
|
-
"@decohere/crucible": "0.0.1-dev.
|
|
34
|
-
"@decohere/devkit": "0.0.1-dev.
|
|
35
|
-
"@decohere/scribe": "0.0.1-dev.
|
|
36
|
-
"@decohere/workspace": "0.0.1-dev.
|
|
32
|
+
"@decohere/core": "0.0.1-dev.122",
|
|
33
|
+
"@decohere/crucible": "0.0.1-dev.122",
|
|
34
|
+
"@decohere/devkit": "0.0.1-dev.122",
|
|
35
|
+
"@decohere/scribe": "0.0.1-dev.122",
|
|
36
|
+
"@decohere/workspace": "0.0.1-dev.122",
|
|
37
37
|
"@swc/core": "^1.11.11",
|
|
38
38
|
"async": "^3.2.6",
|
|
39
39
|
"parse-imports": "^2.2.1",
|
|
@@ -10,6 +10,11 @@ export declare class Copy implements Action<CopyOptions> {
|
|
|
10
10
|
private readonly workspace;
|
|
11
11
|
constructor(workspace: Workspace);
|
|
12
12
|
run: (options: CopyOptions) => Promise<ActionResponse>;
|
|
13
|
+
/**
|
|
14
|
+
* A package without a LICENSE of its own ships the workspace's, so a monorepo
|
|
15
|
+
* licensed once at the root still puts the license text in every tarball.
|
|
16
|
+
*/
|
|
17
|
+
private copyWorkspaceLicense;
|
|
13
18
|
/**
|
|
14
19
|
* Packed builds emit JS under `<out>/<target>/` (e.g. `es6/`). Assets listed on
|
|
15
20
|
* the *configuration* `copyFiles` (not the workspace defaults) are also copied
|
|
@@ -9,6 +9,13 @@ export declare class DependencyResolver {
|
|
|
9
9
|
computeDrift(current: PackageJson, computed: PackageJson): DriftEntry | null;
|
|
10
10
|
getWorkspacePackages(workspace: Workspace, projects: readonly Project[]): Promise<Map<string, Package>>;
|
|
11
11
|
computeProjectDependencies(project: Project, workspacePackages: Map<string, Package>): Promise<ProjectDependencies>;
|
|
12
|
+
/**
|
|
13
|
+
* A package declares its peers by name in its own `peerDependencies` (and marks optional ones in
|
|
14
|
+
* `peerDependenciesMeta`, which sync leaves as written). The root owns every version, so each
|
|
15
|
+
* peer gets the root's range; a peer the root doesn't list is an error. A declared peer stays
|
|
16
|
+
* even when no code imports it: a pino transport, say, is loaded by name.
|
|
17
|
+
*/
|
|
18
|
+
private getPeerDependencies;
|
|
12
19
|
private getDependencies;
|
|
13
20
|
private getDependencyFrom;
|
|
14
21
|
private getFileImports;
|
|
@@ -22,15 +22,14 @@ export type SyncprojectResult = ResultStatus & {
|
|
|
22
22
|
projectName: string;
|
|
23
23
|
drift?: DriftEntry | null;
|
|
24
24
|
};
|
|
25
|
+
export type DependencyLists = {
|
|
26
|
+
dependencies: Record<string, string>;
|
|
27
|
+
devDependencies: Record<string, string>;
|
|
28
|
+
peerDependencies: Record<string, string>;
|
|
29
|
+
};
|
|
25
30
|
export type DriftEntry = {
|
|
26
|
-
original:
|
|
27
|
-
|
|
28
|
-
devDependencies: Record<string, string>;
|
|
29
|
-
};
|
|
30
|
-
updated: {
|
|
31
|
-
dependencies: Record<string, string>;
|
|
32
|
-
devDependencies: Record<string, string>;
|
|
33
|
-
};
|
|
31
|
+
original: DependencyLists;
|
|
32
|
+
updated: DependencyLists;
|
|
34
33
|
};
|
|
35
34
|
export type DependencyWarning = {
|
|
36
35
|
file: string;
|