bussing 0.0.1 → 0.0.4

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ari.dc.uba.ar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # bussing
2
+
3
+
4
+ Reviews the local development environment after a task (or at any time) and detects the repositories left untidy: uncommitted or unpushed changes, versions not bumped or not published, outdated cross dependencies and installations contaminated by `npm link` or `devel-sync`. It also tells in which order to clean them up.
5
+
6
+
7
+ [![npm-version](https://img.shields.io/npm/v/bussing.svg)](https://npmjs.org/package/bussing)
8
+ [![downloads](https://img.shields.io/npm/dm/bussing.svg)](https://npmjs.org/package/bussing)
9
+ [![build](https://github.com/ari-dc-uba-ar/bussing/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/ari-dc-uba-ar/bussing/actions/workflows/build-and-test.yml)
10
+ [![qa-control](https://github.com/ari-dc-uba-ar/bussing/actions/workflows/qa-control.yml/badge.svg)](https://github.com/ari-dc-uba-ar/bussing/actions/workflows/qa-control.yml)
11
+
12
+
13
+ language: ![English](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-en.png)
14
+ also available in:
15
+ [![Spanish](https://raw.githubusercontent.com/codenautas/multilang/master/img/lang-es.png)](LEEME.md)
16
+
17
+
18
+ ## Reviewed repositories
19
+
20
+ The root folders are set with `--path` (it can be repeated). Every subfolder of a root folder that has a `package.json` is a repository to review, except those named as one of the values of `--ignore` (it can be repeated too).
21
+
22
+ ## Default values
23
+
24
+ The optional file `~/.bussing.yaml` sets default values for the parameters; the keys are the parameter names (`bussing --help`). What is given in the command line takes precedence. Relative folders are relative to the current folder, as in the command line.
25
+
26
+ ```yaml
27
+ path:
28
+ - /path/to/repos
29
+ - /path/to/other-repos
30
+ ignore:
31
+ - old-repo
32
+ priority:
33
+ qa-control: 1
34
+ require-bro: -1
35
+ ```
36
+
37
+ ## Checks
38
+
39
+ ### 1. git
40
+
41
+ * There are uncommitted changes (including untracked files that are not ignored).
42
+ * There are unpushed commits.
43
+ * The current branch has no upstream.
44
+
45
+ ### 2. Publication
46
+
47
+ The local `package.json` is compared with the npm registry:
48
+
49
+ * **Unpublished version:** the local version is greater than the last published one.
50
+ * **Version not bumped:** the local version equals the published one, but the local `HEAD` differs from the `gitHead` npm recorded when that version was published (there are commits after the publication).
51
+ * **Not published:** the package does not exist in npm.
52
+
53
+ Packages with `"private": true` are not checked.
54
+
55
+ ### 3. Cross dependencies
56
+
57
+ If repository **A** depends on repository **B** (both reviewed) and A's `package.json` declares B with a version lower than B's local version, A has an outdated dependency. `dependencies`, `devDependencies`, `optionalDependencies` and `peerDependencies` are checked; the declared version is the minimum of the range (`^1.2.0` is lower than `1.3.0` even if the range allows it).
58
+
59
+ If a package name is in more than one reviewed repository, it is reported and that name is not used for cross dependencies nor for the order.
60
+
61
+ ### 4. Contaminated installation
62
+
63
+ For each dependency in `node_modules`:
64
+
65
+ * **`npm link`:** `node_modules/<package>` is a symbolic link.
66
+ * **Copy (`devel-sync` or other):** the content of `node_modules/<package>` does not match the published package of the version recorded in `package-lock.json`. Only packages that are reviewed repositories are checked (the only ones copied during development). It takes into account that, when installing, npm changes CRLF to LF in the shebang of the executables declared in `bin`.
67
+
68
+ ### 5. Cleanup order
69
+
70
+ With the untidy repositories a topological order is built: first the ones that do not depend on any other repository of the list. Those that depend on a repository whose problems reach its users also enter, marked as "by dependency" (with the field suffix): uncommitted or unpushed changes, version not bumped or not published, or outdated production, peer or optional dependencies. Outdated devDependencies and altered node_modules do not reach the package's users. For each repository the reason it is on the list is shown.
71
+
72
+ `dependencies`, `optionalDependencies` and `peerDependencies` are strong dependencies: they define which repositories enter the list and are always respected in the order. `devDependencies` are weak: they are respected unless they close a cycle, because among one's own packages they form cycles (for example, qa-control is a devDependency of libraries it depends on). Those that would close a cycle are deferred: the step shows it and that dependency stays outdated until a later review. With `--priority package=number` (it can be repeated; 0 by default) you decide which weak dependencies are respected first: higher numbers are respected earlier and lower numbers are the first to be deferred. If there is a cycle of strong dependencies, its members are placed in the same step and the cycle is shown.
73
+
74
+ Each step shows the most serious problem of the repository. From most to least serious: uncommitted changes, unpushed commits, altered node_modules, then waiting for / outdated `dependencies`, `peerDependencies`, `optionalDependencies` and `devDependencies` (in that order; waiting means some outdated dependency has its own problems and comes earlier in the order), version not bumped, not published. Those that enter only because of their dependencies are marked as such.
75
+
76
+ ## Usage
77
+
78
+ ### Command line
79
+
80
+ ```sh
81
+ bussing --path /path/to/repos --path /path/to/other-repos
82
+ bussing --json
83
+ ```
84
+
85
+ It shows a readable report; with `--json` it shows the full report as JSON. The parameters and the exit codes are described in `bussing --help`.
86
+
87
+ ### As a library
88
+
89
+ `bussing` can be included from another system. `reviewEnvironment` receives typed options and returns the typed report; it does not write to the output nor end the process.
90
+
91
+ ```ts
92
+ import { reviewEnvironment } from 'bussing';
93
+
94
+ const report = await reviewEnvironment({ roots: ['/path/to/repos'] });
95
+ ```
96
+
97
+
98
+ ## License
99
+
100
+ [MIT](LICENSE)
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ import { type GuaranteedType } from 'guarantee-type';
3
+ import type { ProblemCategory, BussingReport, Problem } from './bussing.js';
4
+ export declare const EXIT_CODE: {
5
+ readonly clean: 0;
6
+ readonly error: 1;
7
+ readonly problems: 2;
8
+ };
9
+ /** Cómo se nombra el archivo de valores por omisión en los mensajes y en la ayuda. */
10
+ export declare const DEFAULTS_FILE_LABEL = "~/.bussing.yaml";
11
+ export declare function defaultsPath(): string;
12
+ /** Interpreta `paquete=número` (el número puede ser negativo). */
13
+ export declare function parsePriority(value: string): [string, number];
14
+ declare const defaultsDescription: {
15
+ object: {
16
+ path: {
17
+ optional: {
18
+ union: ({
19
+ array: {
20
+ string: import("guarantee-type").Opts;
21
+ };
22
+ } | {
23
+ string: import("guarantee-type").Opts;
24
+ })[];
25
+ };
26
+ };
27
+ ignore: {
28
+ optional: {
29
+ union: ({
30
+ array: {
31
+ string: import("guarantee-type").Opts;
32
+ };
33
+ } | {
34
+ string: import("guarantee-type").Opts;
35
+ })[];
36
+ };
37
+ };
38
+ priority: {
39
+ optional: {
40
+ recordString: {
41
+ number: import("guarantee-type").Opts;
42
+ };
43
+ };
44
+ };
45
+ registry: {
46
+ optional: {
47
+ string: import("guarantee-type").Opts;
48
+ };
49
+ };
50
+ json: {
51
+ optional: {
52
+ boolean: import("guarantee-type").Opts;
53
+ };
54
+ };
55
+ };
56
+ };
57
+ type DefaultsFile = GuaranteedType<typeof defaultsDescription>;
58
+ /** Valores por omisión ya normalizados: `path` e `ignore` siempre son listas. */
59
+ export type Defaults = Omit<DefaultsFile, 'path' | 'ignore'> & {
60
+ path?: string[];
61
+ ignore?: string[];
62
+ };
63
+ export interface CliOptions {
64
+ roots: string[];
65
+ ignore: string[];
66
+ priority: Record<string, number>;
67
+ registry: string;
68
+ json: boolean;
69
+ }
70
+ /**
71
+ * Lee el archivo de valores por omisión. Si no existe o está vacío no hay valores por omisión.
72
+ * `label` es el nombre del archivo en los mensajes de error.
73
+ */
74
+ export declare function readDefaults(file: string, label: string): Defaults;
75
+ /**
76
+ * Interpreta los argumentos. cleye muestra la ayuda y termina el proceso ante `--help`, `--version` o un flag desconocido.
77
+ * La primera línea de la salida es la versión, salvo que la salida sea JSON; `write` indica dónde se escribe (por omisión, la salida estándar).
78
+ */
79
+ export declare function parseArgs(argv: string[], defaults: Defaults, write?: (text: string) => void): CliOptions;
80
+ export declare function describeProblem(problem: Problem): string;
81
+ export declare const CATEGORY_LABEL: Record<ProblemCategory, string>;
82
+ export declare function formatReport(report: BussingReport): string;
83
+ export declare function main(argv: string[]): Promise<number>;
84
+ export {};
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env node
2
+ // Línea de comandos de bussing. Es el único módulo que usa cleye y process.
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { basename, join, resolve } from 'node:path';
6
+ import { cli, parseCommandLine, runMain } from 'commendatore';
7
+ import { guarantee, is } from 'guarantee-type';
8
+ import { load } from 'js-yaml';
9
+ import { DEFAULT_REGISTRY_URL, createNpmRegistry, reviewEnvironment } from './bussing.js';
10
+ export const EXIT_CODE = {
11
+ clean: 0,
12
+ error: 1,
13
+ problems: 2,
14
+ };
15
+ const EXIT_CODES = [
16
+ { code: EXIT_CODE.clean, description: 'sin problemas' },
17
+ { code: EXIT_CODE.error, description: 'error' },
18
+ { code: EXIT_CODE.problems, description: 'hay problemas' },
19
+ ];
20
+ /** Archivo opcional, en la carpeta del usuario, con valores por omisión para los parámetros. */
21
+ const DEFAULTS_FILE_NAME = '.bussing.yaml';
22
+ /** Cómo se nombra el archivo de valores por omisión en los mensajes y en la ayuda. */
23
+ export const DEFAULTS_FILE_LABEL = `~/${DEFAULTS_FILE_NAME}`;
24
+ export function defaultsPath() {
25
+ return join(homedir(), DEFAULTS_FILE_NAME);
26
+ }
27
+ /** Interpreta `paquete=número` (el número puede ser negativo). */
28
+ export function parsePriority(value) {
29
+ const match = /^(.+)=(-?\d+)$/.exec(value);
30
+ if (match === null || match[1] === undefined || match[2] === undefined) {
31
+ throw new Error(`--priority ${value} no tiene la forma paquete=número`);
32
+ }
33
+ return [match[1], Number(match[2])];
34
+ }
35
+ /** Parámetros de la línea de comandos. Las mismas claves se pueden usar en el archivo de valores por omisión. */
36
+ const flags = {
37
+ path: {
38
+ type: [String],
39
+ alias: 'p',
40
+ description: 'Carpeta raíz a revisar; se puede repetir (cada subcarpeta con package.json es un repositorio)',
41
+ },
42
+ ignore: {
43
+ type: [String],
44
+ description: 'Nombre de una subcarpeta que no se revisa; se puede repetir',
45
+ },
46
+ priority: {
47
+ type: [parsePriority],
48
+ description: 'Prioridad de un paquete como devDependency al romper ciclos del orden de limpieza, como paquete=número ' +
49
+ '(por omisión 0; las de mayor número se respetan primero); se puede repetir',
50
+ },
51
+ registry: {
52
+ type: String,
53
+ description: `URL del registro de npm (por omisión ${DEFAULT_REGISTRY_URL})`,
54
+ },
55
+ json: {
56
+ type: Boolean,
57
+ description: 'Muestra el informe completo en JSON',
58
+ },
59
+ };
60
+ /** Tipo de cada clave del archivo de valores por omisión: una por parámetro (el compilador verifica que sean las mismas). */
61
+ const defaultsFields = {
62
+ // una carpeta sola o una lista
63
+ path: { optional: is.union([is.string, is.array.string]) },
64
+ ignore: { optional: is.union([is.string, is.array.string]) },
65
+ // en el archivo es un mapa paquete: número
66
+ priority: is.optional.recordString.number,
67
+ registry: is.optional.string,
68
+ json: is.optional.boolean,
69
+ };
70
+ const defaultsDescription = is.object(defaultsFields);
71
+ /**
72
+ * Lee el archivo de valores por omisión. Si no existe o está vacío no hay valores por omisión.
73
+ * `label` es el nombre del archivo en los mensajes de error.
74
+ */
75
+ export function readDefaults(file, label) {
76
+ if (!existsSync(file))
77
+ return {};
78
+ const text = readFileSync(file, 'utf8');
79
+ if (text.trim() === '')
80
+ return {};
81
+ let content;
82
+ try {
83
+ content = load(text);
84
+ }
85
+ catch (err) {
86
+ throw new Error(`${label} no es un YAML válido (${err instanceof Error ? err.message : String(err)})`);
87
+ }
88
+ if (content === undefined || content === null)
89
+ return {};
90
+ if (typeof content !== 'object' || Array.isArray(content))
91
+ throw new Error(`${label} no contiene claves y valores`);
92
+ const unknownKeys = Object.keys(content).filter(function (key) { return !(key in flags); });
93
+ if (unknownKeys.length > 0) {
94
+ throw new Error(`${label} tiene claves desconocidas: ${unknownKeys.join(', ')} (las claves válidas son: ${Object.keys(flags).join(', ')})`);
95
+ }
96
+ let values;
97
+ try {
98
+ values = guarantee(defaultsDescription, content);
99
+ }
100
+ catch (err) {
101
+ throw new Error(`${label} tiene valores de tipo incorrecto (${err instanceof Error ? err.message : String(err)})`);
102
+ }
103
+ const { path, ignore, ...others } = values;
104
+ const defaults = others;
105
+ if (path !== undefined)
106
+ defaults.path = typeof path === 'string' ? [path] : path;
107
+ if (ignore !== undefined)
108
+ defaults.ignore = typeof ignore === 'string' ? [ignore] : ignore;
109
+ return defaults;
110
+ }
111
+ /**
112
+ * Interpreta los argumentos. cleye muestra la ayuda y termina el proceso ante `--help`, `--version` o un flag desconocido.
113
+ * La primera línea de la salida es la versión, salvo que la salida sea JSON; `write` indica dónde se escribe (por omisión, la salida estándar).
114
+ */
115
+ export function parseArgs(argv, defaults, write) {
116
+ const { parsed } = parseCommandLine({
117
+ moduleUrl: import.meta.url,
118
+ description: 'Revisa los repositorios locales y muestra los que quedaron desprolijos y el orden en que conviene limpiarlos.\n' +
119
+ `Los valores por omisión de los parámetros se pueden definir en ${DEFAULTS_FILE_LABEL} (las claves son los nombres de los parámetros).`,
120
+ examples: ['bussing --path ../repos --path ../otros-repos --ignore repo-viejo', 'bussing --priority qa-control=1 --priority require-bro=-1', 'bussing --json'],
121
+ exitCodes: EXIT_CODES,
122
+ }, function (base) {
123
+ return cli({ ...base, flags }, undefined, argv);
124
+ }, {
125
+ show: function (parsedArgs) { return !(parsedArgs.flags.json ?? defaults.json ?? false); },
126
+ ...(write === undefined ? {} : { write }),
127
+ });
128
+ // las carpetas relativas son relativas a la carpeta actual, vengan de la línea de comandos o del archivo
129
+ const roots = (parsed.flags.path.length > 0 ? parsed.flags.path : defaults.path ?? []).map(function (path) { return resolve(path); });
130
+ if (roots.length === 0) {
131
+ throw new Error(`Falta indicar la carpeta con --path o definir los parámetros predeterminados en ${DEFAULTS_FILE_LABEL}`);
132
+ }
133
+ return {
134
+ roots,
135
+ ignore: parsed.flags.ignore.length > 0 ? parsed.flags.ignore : defaults.ignore ?? [],
136
+ priority: parsed.flags.priority.length > 0 ? Object.fromEntries(parsed.flags.priority) : defaults.priority ?? {},
137
+ registry: parsed.flags.registry ?? defaults.registry ?? DEFAULT_REGISTRY_URL,
138
+ json: parsed.flags.json ?? defaults.json ?? false,
139
+ };
140
+ }
141
+ const MAX_LISTED = 5;
142
+ function listSome(items) {
143
+ return items.slice(0, MAX_LISTED).join(', ') + (items.length > MAX_LISTED ? `, … (${items.length - MAX_LISTED} más)` : '');
144
+ }
145
+ function plural(count, singular, pluralForm) {
146
+ return `${count} ${count === 1 ? singular : pluralForm}`;
147
+ }
148
+ export function describeProblem(problem) {
149
+ switch (problem.kind) {
150
+ case 'uncommitted':
151
+ return `${plural(problem.files.length, 'archivo sin commitear', 'archivos sin commitear')}: ${listSome(problem.files)}`;
152
+ case 'unpushed':
153
+ return plural(problem.commits, 'commit sin pushear', 'commits sin pushear');
154
+ case 'no-upstream':
155
+ return `la rama ${problem.branch} no tiene upstream`;
156
+ case 'not-published':
157
+ return `el paquete no está publicado en npm (versión local ${problem.version})`;
158
+ case 'unpublished-version':
159
+ return `versión ${problem.version} sin publicar (en npm: ${problem.latest ?? 'sin latest'})`;
160
+ case 'version-not-bumped':
161
+ return `versión ${problem.version} sin subir: ${plural(problem.commitsAfterPublish, 'commit posterior', 'commits posteriores')} a la publicación`;
162
+ case 'outdated-dependency':
163
+ return `dependencia desactualizada: ${problem.dependency} "${problem.declared}" (${problem.field}), versión local ${problem.local}`;
164
+ case 'linked-dependency':
165
+ return `node_modules/${problem.dependency} es un enlace a ${problem.target}`;
166
+ case 'copied-dependency':
167
+ return `node_modules/${problem.dependency} no coincide con la versión publicada ${problem.version}` +
168
+ (problem.installedVersion !== problem.version ? ` (el package.json instalado dice ${problem.installedVersion ?? '?'})` : '') + ': ' +
169
+ listSome(problem.differences.map(function (difference) { return `${difference.path} (${difference.kind})`; }));
170
+ }
171
+ }
172
+ function describeWarning(warning) {
173
+ return `advertencia (${warning.check}): ${warning.message}`;
174
+ }
175
+ function describeRepo(repo) {
176
+ const title = repo.name === null ? repo.dir : `${repo.name}${repo.version === null ? '' : '@' + repo.version} ${repo.dir}`;
177
+ return [
178
+ title,
179
+ ...repo.problems.map(function (problem) { return ' - ' + describeProblem(problem); }),
180
+ ...repo.warnings.map(function (warning) { return ' ? ' + describeWarning(warning); }),
181
+ ];
182
+ }
183
+ /** Sufijo de cada campo de dependencias en las etiquetas ("dependencias-dev desactualizadas", "por dependencia-peer"). */
184
+ const FIELD_SUFFIX = {
185
+ 'dependencies': '',
186
+ 'peerDependencies': '-peer',
187
+ 'optionalDependencies': '-opt',
188
+ 'devDependencies': '-dev',
189
+ };
190
+ export const CATEGORY_LABEL = {
191
+ 'uncommitted': 'falta commitear',
192
+ 'unpushed': 'falta pushear',
193
+ 'altered-node-modules': 'node_modules alterado',
194
+ 'waiting-dependencies': `espera dependencias${FIELD_SUFFIX.dependencies}`,
195
+ 'outdated-dependencies': `dependencias${FIELD_SUFFIX.dependencies} desactualizadas`,
196
+ 'waiting-peer-dependencies': `espera dependencias${FIELD_SUFFIX.peerDependencies}`,
197
+ 'outdated-peer-dependencies': `dependencias${FIELD_SUFFIX.peerDependencies} desactualizadas`,
198
+ 'waiting-optional-dependencies': `espera dependencias${FIELD_SUFFIX.optionalDependencies}`,
199
+ 'outdated-optional-dependencies': `dependencias${FIELD_SUFFIX.optionalDependencies} desactualizadas`,
200
+ 'waiting-dev-dependencies': `espera dependencias${FIELD_SUFFIX.devDependencies}`,
201
+ 'outdated-dev-dependencies': `dependencias${FIELD_SUFFIX.devDependencies} desactualizadas`,
202
+ 'version-not-bumped': 'falta subir versión',
203
+ 'unpublished': 'falta publicar',
204
+ };
205
+ function describeStep(step) {
206
+ const details = [];
207
+ if (step.dependsOn.length > 0)
208
+ details.push('depende de ' + step.dependsOn.join(', '));
209
+ if (step.inCycleWith.length > 0)
210
+ details.push('ciclo con ' + step.inCycleWith.join(', '));
211
+ if (step.deferred.length > 0)
212
+ details.push(`ciclo: ${step.deferred.join(', ')} ${step.deferred.length === 1 ? 'se actualiza' : 'se actualizan'} después`);
213
+ const folder = basename(step.dir);
214
+ const label = step.mainProblem !== null ? CATEGORY_LABEL[step.mainProblem]
215
+ : `por dependencia${step.dependencyField === null ? '' : FIELD_SUFFIX[step.dependencyField]}`;
216
+ return ` ${step.level + 1}. ${step.name}${folder === step.name ? '' : ` [${folder}]`} ${label}` + (details.length > 0 ? ` (${details.join('; ')})` : '');
217
+ }
218
+ export function formatReport(report) {
219
+ const withProblems = report.repos.filter(function (repo) { return repo.problems.length > 0; });
220
+ const withWarningsOnly = report.repos.filter(function (repo) { return repo.problems.length === 0 && repo.warnings.length > 0; });
221
+ const lines = [`${plural(withProblems.length, 'repositorio con problemas', 'repositorios con problemas')} de ${report.repos.length} revisados` +
222
+ (report.ignored.length > 0 ? ` (${plural(report.ignored.length, 'ignorado', 'ignorados')}: ${report.ignored.map(function (dir) { return basename(dir); }).join(', ')})` : '')];
223
+ for (const repo of [...withProblems, ...withWarningsOnly])
224
+ lines.push('', ...describeRepo(repo));
225
+ if (report.cleanupOrder.length > 0) {
226
+ lines.push('', 'Orden de limpieza (los del mismo número no dependen entre sí):', ...report.cleanupOrder.map(describeStep));
227
+ }
228
+ if (report.warnings.length > 0)
229
+ lines.push('', ...report.warnings.map(describeWarning));
230
+ return lines.join('\n') + '\n';
231
+ }
232
+ export async function main(argv) {
233
+ try {
234
+ const options = parseArgs(argv, readDefaults(defaultsPath(), DEFAULTS_FILE_LABEL));
235
+ const reviewOptions = {
236
+ roots: options.roots,
237
+ ignore: options.ignore,
238
+ dependencyPriority: options.priority,
239
+ registry: createNpmRegistry(options.registry),
240
+ log: function (message) { if (process.stderr.isTTY)
241
+ process.stderr.write(`\r\u001b[K${message}`); },
242
+ };
243
+ const report = await reviewEnvironment(reviewOptions);
244
+ if (process.stderr.isTTY)
245
+ process.stderr.write('\r\u001b[K');
246
+ process.stdout.write(options.json ? JSON.stringify(report, null, 2) + '\n' : formatReport(report));
247
+ return report.repos.some(function (repo) { return repo.problems.length > 0; }) ? EXIT_CODE.problems : EXIT_CODE.clean;
248
+ }
249
+ catch (err) {
250
+ if (process.stderr.isTTY)
251
+ process.stderr.write('\r\u001b[K');
252
+ process.stderr.write(`bussing: ${err instanceof Error ? err.message : String(err)}\n`);
253
+ return EXIT_CODE.error;
254
+ }
255
+ }
256
+ // Solo se ejecuta al invocarlo como programa (no al importarlo desde los tests)
257
+ if (import.meta.main) {
258
+ runMain(function () { return main(process.argv.slice(2)); }, EXIT_CODE.error);
259
+ }
260
+ //# sourceMappingURL=bussing-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bussing-cli.js","sourceRoot":"","sources":["../src/bussing-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { BussingOptions, BussingReport } from './types.js';
2
+ export type * from './types.js';
3
+ export { createNpmRegistry, DEFAULT_REGISTRY_URL } from './registry.js';
4
+ export { DEPENDENCY_CATEGORY, mainCategory, PROBLEM_CATEGORIES, PROBLEM_CATEGORY, problemCategory, REACHES_DEPENDENTS } from './categories.js';
5
+ export declare function reviewEnvironment(options: BussingOptions): Promise<BussingReport>;
@@ -0,0 +1,140 @@
1
+ // bussing: revisa el ambiente local de repositorios y arma el orden de limpieza.
2
+ // Es la librería (el core): no lee process.argv, no escribe en la salida y no termina el proceso.
3
+ import { basename } from 'node:path';
4
+ import { fieldGravity, mainCategory, problemCategory, REACHES_DEPENDENTS } from './categories.js';
5
+ import { checkCrossDependencies, checkGit, checkInstallation, checkPublication } from './checks.js';
6
+ import { computeCleanupOrder } from './order.js';
7
+ import { createNpmRegistry } from './registry.js';
8
+ import { declaredDependencies, findRepos, ORDER_DEPENDENCY_FIELDS, readPackageJson, WEAK_ORDER_DEPENDENCY_FIELDS } from './repos.js';
9
+ import { defined, mustGet } from './util.js';
10
+ export { createNpmRegistry, DEFAULT_REGISTRY_URL } from './registry.js';
11
+ export { DEPENDENCY_CATEGORY, mainCategory, PROBLEM_CATEGORIES, PROBLEM_CATEGORY, problemCategory, REACHES_DEPENDENTS } from './categories.js';
12
+ /** Cantidad de repositorios que se revisan en paralelo. */
13
+ const CONCURRENCY = 8;
14
+ async function mapWithConcurrency(items, limit, fn) {
15
+ const results = new Array(items.length);
16
+ let next = 0;
17
+ async function worker() {
18
+ while (next < items.length) {
19
+ const i = next++;
20
+ results[i] = await fn(defined(items[i], 'elemento'));
21
+ }
22
+ }
23
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
24
+ return results;
25
+ }
26
+ function errorMessage(err) {
27
+ return err instanceof Error ? err.message : String(err);
28
+ }
29
+ /** Ejecuta un control; si falla, lo informa como advertencia del repositorio en lugar de interrumpir la revisión. */
30
+ async function runCheck(report, check, fn) {
31
+ try {
32
+ const result = await fn();
33
+ report.problems.push(...result.problems);
34
+ report.warnings.push(...result.warnings);
35
+ }
36
+ catch (err) {
37
+ report.warnings.push({ check, message: `falló el control: ${errorMessage(err)}` });
38
+ }
39
+ }
40
+ export async function reviewEnvironment(options) {
41
+ const registry = options.registry ?? createNpmRegistry();
42
+ const log = options.log ?? function () { };
43
+ const warnings = [];
44
+ const ignore = options.ignore ?? [];
45
+ const { repos: dirs, ignored } = await findRepos(options.roots, ignore);
46
+ for (const name of ignore) {
47
+ if (!ignored.some(function (dir) { return basename(dir) === name; })) {
48
+ warnings.push({ check: 'repo', message: `ninguna carpeta de repositorio se llama ${name} (ignore)` });
49
+ }
50
+ }
51
+ log(`${dirs.length} repositorios encontrados`);
52
+ // package.json de cada repositorio
53
+ const repos = await Promise.all(dirs.map(async function (dir) {
54
+ const report = { dir, name: null, version: null, problems: [], warnings: [] };
55
+ try {
56
+ const pkg = await readPackageJson(dir);
57
+ report.name = pkg.name ?? null;
58
+ report.version = pkg.version ?? null;
59
+ return { report, pkg };
60
+ }
61
+ catch (err) {
62
+ report.warnings.push({ check: 'repo', message: `no se pudo leer package.json: ${errorMessage(err)}` });
63
+ return { report, pkg: null };
64
+ }
65
+ }));
66
+ // Nombres locales: los que corresponden a más de un repositorio no se usan para dependencias cruzadas ni orden
67
+ const dirsByName = new Map();
68
+ for (const { report } of repos) {
69
+ if (report.name !== null)
70
+ dirsByName.set(report.name, [...(dirsByName.get(report.name) ?? []), report.dir]);
71
+ }
72
+ const uniqueDirByName = new Map();
73
+ const localVersions = new Map();
74
+ for (const [name, list] of dirsByName) {
75
+ if (list.length > 1) {
76
+ warnings.push({ check: 'cross-dependencies', message: `el paquete ${name} está en varios repositorios (${list.join(', ')}); no se usa para dependencias cruzadas ni para el orden` });
77
+ continue;
78
+ }
79
+ const dir = defined(list[0], 'carpeta del repositorio');
80
+ uniqueDirByName.set(name, dir);
81
+ const version = repos.find(function (repo) { return repo.report.dir === dir; })?.report.version;
82
+ if (version !== null && version !== undefined)
83
+ localVersions.set(name, version);
84
+ }
85
+ const localNames = new Set(dirsByName.keys());
86
+ await mapWithConcurrency(repos, CONCURRENCY, async function ({ report, pkg }) {
87
+ log(`revisando ${report.dir}`);
88
+ let gitState = null;
89
+ await runCheck(report, 'git', async function () {
90
+ const result = await checkGit(report.dir);
91
+ gitState = result.state;
92
+ return result;
93
+ });
94
+ if (pkg === null)
95
+ return;
96
+ await runCheck(report, 'publication', function () { return checkPublication(report.dir, pkg, gitState, registry); });
97
+ await runCheck(report, 'cross-dependencies', function () { return checkCrossDependencies(pkg, localVersions); });
98
+ await runCheck(report, 'installation', function () { return checkInstallation(report.dir, localNames, registry); });
99
+ });
100
+ /** Repositorios revisados de los que depende en los campos indicados, con el campo más grave si figura en varios. */
101
+ function localDependencies(report, pkg, fields) {
102
+ const result = new Map();
103
+ for (const dep of pkg === null ? [] : declaredDependencies(pkg, fields)) {
104
+ const depDir = uniqueDirByName.get(dep.name);
105
+ if (depDir === undefined || depDir === report.dir)
106
+ continue;
107
+ const previous = result.get(depDir);
108
+ if (previous === undefined || fieldGravity(dep.field) < fieldGravity(previous))
109
+ result.set(depDir, dep.field);
110
+ }
111
+ return [...result].map(function ([dir, field]) { return { dir, field }; });
112
+ }
113
+ const noWait = function () { return false; };
114
+ const priority = new Map(Object.entries(options.dependencyPriority ?? {}));
115
+ for (const name of priority.keys()) {
116
+ if (!uniqueDirByName.has(name))
117
+ warnings.push({ check: 'order', message: `ningún repositorio revisado tiene el paquete ${name} (prioridad de dependencia)` });
118
+ }
119
+ const nodes = repos.map(function ({ report, pkg }) {
120
+ return {
121
+ dir: report.dir,
122
+ name: report.name ?? basename(report.dir),
123
+ mainProblem: mainCategory(report.problems, noWait),
124
+ reachesDependents: report.problems.some(function (problem) { return REACHES_DEPENDENTS[problemCategory(problem, noWait)]; }),
125
+ dependsOn: localDependencies(report, pkg, ORDER_DEPENDENCY_FIELDS),
126
+ weakDependsOn: localDependencies(report, pkg, WEAK_ORDER_DEPENDENCY_FIELDS).map(function (dep) { return dep.dir; }),
127
+ };
128
+ });
129
+ const cleanupOrder = computeCleanupOrder(nodes, priority);
130
+ // "espera dependencias" solo si alguna dependencia desactualizada tiene problemas que llegan a quien la usa y queda antes en el orden
131
+ const hasProblems = new Set(nodes.filter(function (node) { return node.reachesDependents; }).map(function (node) { return node.name; }));
132
+ const reportByDir = new Map(repos.map(function (repo) { return [repo.report.dir, repo.report]; }));
133
+ for (const step of cleanupOrder) {
134
+ step.mainProblem = mainCategory(mustGet(reportByDir, step.dir).problems, function (name) {
135
+ return hasProblems.has(name) && step.dependsOn.includes(name);
136
+ });
137
+ }
138
+ return { repos: repos.map(function (repo) { return repo.report; }), ignored, cleanupOrder, warnings };
139
+ }
140
+ //# sourceMappingURL=bussing.js.map