pull-request-split-advisor 3.1.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.
- package/LICENSE +52 -0
- package/README.md +168 -0
- package/dist/ai/config-wizard.js +282 -0
- package/dist/ai/enricher.js +290 -0
- package/dist/ai/prompts.js +231 -0
- package/dist/ai/provider.js +265 -0
- package/dist/cli.js +442 -0
- package/dist/config/config.js +315 -0
- package/dist/config/default-config.js +223 -0
- package/dist/core/blocks.js +145 -0
- package/dist/core/commit-planner.js +273 -0
- package/dist/core/dependency.js +284 -0
- package/dist/core/file-stats.js +341 -0
- package/dist/core/history.js +72 -0
- package/dist/core/planner.js +25 -0
- package/dist/core/scoring.js +166 -0
- package/dist/core/strategy.js +486 -0
- package/dist/git/branch-naming.js +120 -0
- package/dist/git/executor.js +378 -0
- package/dist/git/git.js +239 -0
- package/dist/output/report-styles.generated.js +10 -0
- package/dist/output/report.js +726 -0
- package/dist/output/ui.js +417 -0
- package/dist/shared/constants.js +59 -0
- package/dist/shared/types.js +7 -0
- package/dist/shared/utils.js +73 -0
- package/node_modules/@colors/colors/LICENSE +26 -0
- package/node_modules/@colors/colors/README.md +219 -0
- package/node_modules/@colors/colors/examples/normal-usage.js +83 -0
- package/node_modules/@colors/colors/examples/safe-string.js +80 -0
- package/node_modules/@colors/colors/index.d.ts +136 -0
- package/node_modules/@colors/colors/lib/colors.js +211 -0
- package/node_modules/@colors/colors/lib/custom/trap.js +46 -0
- package/node_modules/@colors/colors/lib/custom/zalgo.js +110 -0
- package/node_modules/@colors/colors/lib/extendStringPrototype.js +110 -0
- package/node_modules/@colors/colors/lib/index.js +13 -0
- package/node_modules/@colors/colors/lib/maps/america.js +10 -0
- package/node_modules/@colors/colors/lib/maps/rainbow.js +12 -0
- package/node_modules/@colors/colors/lib/maps/random.js +11 -0
- package/node_modules/@colors/colors/lib/maps/zebra.js +5 -0
- package/node_modules/@colors/colors/lib/styles.js +95 -0
- package/node_modules/@colors/colors/lib/system/has-flag.js +35 -0
- package/node_modules/@colors/colors/lib/system/supports-colors.js +151 -0
- package/node_modules/@colors/colors/package.json +45 -0
- package/node_modules/@colors/colors/safe.d.ts +48 -0
- package/node_modules/@colors/colors/safe.js +10 -0
- package/node_modules/@colors/colors/themes/generic-logging.js +12 -0
- package/node_modules/ansi-align/LICENSE +13 -0
- package/node_modules/ansi-align/README.md +80 -0
- package/node_modules/ansi-align/index.js +61 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/index.d.ts +37 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/index.js +10 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/license +9 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/package.json +55 -0
- package/node_modules/ansi-align/node_modules/ansi-regex/readme.md +78 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/README.md +73 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/es2015/index.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/es2015/text.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/index.d.ts +23 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/index.js +6 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/package.json +50 -0
- package/node_modules/ansi-align/node_modules/emoji-regex/text.js +6 -0
- package/node_modules/ansi-align/node_modules/string-width/index.d.ts +29 -0
- package/node_modules/ansi-align/node_modules/string-width/index.js +47 -0
- package/node_modules/ansi-align/node_modules/string-width/license +9 -0
- package/node_modules/ansi-align/node_modules/string-width/package.json +56 -0
- package/node_modules/ansi-align/node_modules/string-width/readme.md +50 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/index.d.ts +17 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/index.js +4 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/license +9 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/package.json +54 -0
- package/node_modules/ansi-align/node_modules/strip-ansi/readme.md +46 -0
- package/node_modules/ansi-align/package.json +43 -0
- package/node_modules/ansi-regex/index.d.ts +33 -0
- package/node_modules/ansi-regex/index.js +14 -0
- package/node_modules/ansi-regex/license +9 -0
- package/node_modules/ansi-regex/package.json +61 -0
- package/node_modules/ansi-regex/readme.md +66 -0
- package/node_modules/ansi-styles/index.d.ts +236 -0
- package/node_modules/ansi-styles/index.js +223 -0
- package/node_modules/ansi-styles/license +9 -0
- package/node_modules/ansi-styles/package.json +54 -0
- package/node_modules/ansi-styles/readme.md +173 -0
- package/node_modules/boxen/index.d.ts +267 -0
- package/node_modules/boxen/index.js +376 -0
- package/node_modules/boxen/license +9 -0
- package/node_modules/boxen/package.json +69 -0
- package/node_modules/boxen/readme.md +300 -0
- package/node_modules/camelcase/index.d.ts +102 -0
- package/node_modules/camelcase/index.js +110 -0
- package/node_modules/camelcase/license +9 -0
- package/node_modules/camelcase/package.json +47 -0
- package/node_modules/camelcase/readme.md +135 -0
- package/node_modules/chalk/license +9 -0
- package/node_modules/chalk/package.json +83 -0
- package/node_modules/chalk/readme.md +297 -0
- package/node_modules/chalk/source/index.d.ts +325 -0
- package/node_modules/chalk/source/index.js +225 -0
- package/node_modules/chalk/source/utilities.js +33 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/node_modules/cli-boxes/boxes.json +82 -0
- package/node_modules/cli-boxes/index.d.ts +127 -0
- package/node_modules/cli-boxes/index.js +6 -0
- package/node_modules/cli-boxes/license +9 -0
- package/node_modules/cli-boxes/package.json +42 -0
- package/node_modules/cli-boxes/readme.md +115 -0
- package/node_modules/cli-table3/LICENSE +21 -0
- package/node_modules/cli-table3/README.md +236 -0
- package/node_modules/cli-table3/index.d.ts +96 -0
- package/node_modules/cli-table3/index.js +1 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/index.d.ts +37 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/index.js +10 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/license +9 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/package.json +55 -0
- package/node_modules/cli-table3/node_modules/ansi-regex/readme.md +78 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/README.md +73 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/es2015/index.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/es2015/text.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/index.d.ts +23 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/index.js +6 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/package.json +50 -0
- package/node_modules/cli-table3/node_modules/emoji-regex/text.js +6 -0
- package/node_modules/cli-table3/node_modules/string-width/index.d.ts +29 -0
- package/node_modules/cli-table3/node_modules/string-width/index.js +47 -0
- package/node_modules/cli-table3/node_modules/string-width/license +9 -0
- package/node_modules/cli-table3/node_modules/string-width/package.json +56 -0
- package/node_modules/cli-table3/node_modules/string-width/readme.md +50 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/index.d.ts +17 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/index.js +4 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/license +9 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/package.json +54 -0
- package/node_modules/cli-table3/node_modules/strip-ansi/readme.md +46 -0
- package/node_modules/cli-table3/package.json +100 -0
- package/node_modules/cli-table3/src/cell.js +409 -0
- package/node_modules/cli-table3/src/debug.js +28 -0
- package/node_modules/cli-table3/src/layout-manager.js +254 -0
- package/node_modules/cli-table3/src/table.js +106 -0
- package/node_modules/cli-table3/src/utils.js +344 -0
- package/node_modules/commander/LICENSE +22 -0
- package/node_modules/commander/Readme.md +1157 -0
- package/node_modules/commander/esm.mjs +16 -0
- package/node_modules/commander/index.js +24 -0
- package/node_modules/commander/lib/argument.js +149 -0
- package/node_modules/commander/lib/command.js +2509 -0
- package/node_modules/commander/lib/error.js +39 -0
- package/node_modules/commander/lib/help.js +520 -0
- package/node_modules/commander/lib/option.js +330 -0
- package/node_modules/commander/lib/suggestSimilar.js +101 -0
- package/node_modules/commander/package-support.json +16 -0
- package/node_modules/commander/package.json +84 -0
- package/node_modules/commander/typings/esm.d.mts +3 -0
- package/node_modules/commander/typings/index.d.ts +969 -0
- package/node_modules/emoji-regex/LICENSE-MIT.txt +20 -0
- package/node_modules/emoji-regex/README.md +107 -0
- package/node_modules/emoji-regex/index.d.ts +3 -0
- package/node_modules/emoji-regex/index.js +4 -0
- package/node_modules/emoji-regex/index.mjs +4 -0
- package/node_modules/emoji-regex/package.json +45 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +18 -0
- package/node_modules/get-east-asian-width/lookup.js +135 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/node_modules/is-fullwidth-code-point/index.d.ts +17 -0
- package/node_modules/is-fullwidth-code-point/index.js +50 -0
- package/node_modules/is-fullwidth-code-point/license +9 -0
- package/node_modules/is-fullwidth-code-point/package.json +42 -0
- package/node_modules/is-fullwidth-code-point/readme.md +39 -0
- package/node_modules/isbinaryfile/LICENSE.txt +22 -0
- package/node_modules/isbinaryfile/README.md +70 -0
- package/node_modules/isbinaryfile/lib/index.d.ts +3 -0
- package/node_modules/isbinaryfile/lib/index.js +256 -0
- package/node_modules/isbinaryfile/package.json +64 -0
- package/node_modules/string-width/index.d.ts +39 -0
- package/node_modules/string-width/index.js +82 -0
- package/node_modules/string-width/license +9 -0
- package/node_modules/string-width/package.json +64 -0
- package/node_modules/string-width/readme.md +66 -0
- package/node_modules/strip-ansi/index.d.ts +15 -0
- package/node_modules/strip-ansi/index.js +19 -0
- package/node_modules/strip-ansi/license +9 -0
- package/node_modules/strip-ansi/package.json +59 -0
- package/node_modules/strip-ansi/readme.md +37 -0
- package/node_modules/type-fest/index.d.ts +178 -0
- package/node_modules/type-fest/license-cc0 +121 -0
- package/node_modules/type-fest/license-mit +9 -0
- package/node_modules/type-fest/package.json +91 -0
- package/node_modules/type-fest/readme.md +1060 -0
- package/node_modules/type-fest/source/all-union-fields.d.ts +88 -0
- package/node_modules/type-fest/source/and.d.ts +25 -0
- package/node_modules/type-fest/source/array-indices.d.ts +23 -0
- package/node_modules/type-fest/source/array-slice.d.ts +109 -0
- package/node_modules/type-fest/source/array-splice.d.ts +99 -0
- package/node_modules/type-fest/source/array-tail.d.ts +76 -0
- package/node_modules/type-fest/source/array-values.d.ts +22 -0
- package/node_modules/type-fest/source/arrayable.d.ts +29 -0
- package/node_modules/type-fest/source/async-return-type.d.ts +23 -0
- package/node_modules/type-fest/source/asyncify.d.ts +32 -0
- package/node_modules/type-fest/source/basic.d.ts +68 -0
- package/node_modules/type-fest/source/camel-case.d.ts +89 -0
- package/node_modules/type-fest/source/camel-cased-properties-deep.d.ts +97 -0
- package/node_modules/type-fest/source/camel-cased-properties.d.ts +43 -0
- package/node_modules/type-fest/source/conditional-except.d.ts +45 -0
- package/node_modules/type-fest/source/conditional-keys.d.ts +47 -0
- package/node_modules/type-fest/source/conditional-pick-deep.d.ts +118 -0
- package/node_modules/type-fest/source/conditional-pick.d.ts +44 -0
- package/node_modules/type-fest/source/conditional-simplify.d.ts +32 -0
- package/node_modules/type-fest/source/delimiter-case.d.ts +78 -0
- package/node_modules/type-fest/source/delimiter-cased-properties-deep.d.ts +106 -0
- package/node_modules/type-fest/source/delimiter-cased-properties.d.ts +46 -0
- package/node_modules/type-fest/source/distributed-omit.d.ts +89 -0
- package/node_modules/type-fest/source/distributed-pick.d.ts +85 -0
- package/node_modules/type-fest/source/empty-object.d.ts +46 -0
- package/node_modules/type-fest/source/enforce-optional.d.ts +47 -0
- package/node_modules/type-fest/source/entries.d.ts +62 -0
- package/node_modules/type-fest/source/entry.d.ts +65 -0
- package/node_modules/type-fest/source/exact.d.ts +68 -0
- package/node_modules/type-fest/source/except.d.ts +108 -0
- package/node_modules/type-fest/source/find-global-type.d.ts +64 -0
- package/node_modules/type-fest/source/fixed-length-array.d.ts +43 -0
- package/node_modules/type-fest/source/get.d.ts +219 -0
- package/node_modules/type-fest/source/global-this.d.ts +21 -0
- package/node_modules/type-fest/source/greater-than-or-equal.d.ts +22 -0
- package/node_modules/type-fest/source/greater-than.d.ts +56 -0
- package/node_modules/type-fest/source/has-optional-keys.d.ts +21 -0
- package/node_modules/type-fest/source/has-readonly-keys.d.ts +21 -0
- package/node_modules/type-fest/source/has-required-keys.d.ts +59 -0
- package/node_modules/type-fest/source/has-writable-keys.d.ts +21 -0
- package/node_modules/type-fest/source/if-any.d.ts +24 -0
- package/node_modules/type-fest/source/if-empty-object.d.ts +26 -0
- package/node_modules/type-fest/source/if-never.d.ts +24 -0
- package/node_modules/type-fest/source/if-null.d.ts +24 -0
- package/node_modules/type-fest/source/if-unknown.d.ts +24 -0
- package/node_modules/type-fest/source/includes.d.ts +22 -0
- package/node_modules/type-fest/source/int-closed-range.d.ts +35 -0
- package/node_modules/type-fest/source/int-range.d.ts +55 -0
- package/node_modules/type-fest/source/internal/array.d.ts +126 -0
- package/node_modules/type-fest/source/internal/characters.d.ts +67 -0
- package/node_modules/type-fest/source/internal/index.d.ts +8 -0
- package/node_modules/type-fest/source/internal/keys.d.ts +97 -0
- package/node_modules/type-fest/source/internal/numeric.d.ts +118 -0
- package/node_modules/type-fest/source/internal/object.d.ts +236 -0
- package/node_modules/type-fest/source/internal/string.d.ts +210 -0
- package/node_modules/type-fest/source/internal/tuple.d.ts +90 -0
- package/node_modules/type-fest/source/internal/type.d.ts +139 -0
- package/node_modules/type-fest/source/invariant-of.d.ts +76 -0
- package/node_modules/type-fest/source/is-any.d.ts +33 -0
- package/node_modules/type-fest/source/is-equal.d.ts +31 -0
- package/node_modules/type-fest/source/is-float.d.ts +41 -0
- package/node_modules/type-fest/source/is-integer.d.ts +58 -0
- package/node_modules/type-fest/source/is-literal.d.ts +296 -0
- package/node_modules/type-fest/source/is-never.d.ts +42 -0
- package/node_modules/type-fest/source/is-null.d.ts +20 -0
- package/node_modules/type-fest/source/is-tuple.d.ts +89 -0
- package/node_modules/type-fest/source/is-unknown.d.ts +52 -0
- package/node_modules/type-fest/source/iterable-element.d.ts +64 -0
- package/node_modules/type-fest/source/join.d.ts +68 -0
- package/node_modules/type-fest/source/jsonifiable.d.ts +37 -0
- package/node_modules/type-fest/source/jsonify.d.ts +122 -0
- package/node_modules/type-fest/source/kebab-case.d.ts +44 -0
- package/node_modules/type-fest/source/kebab-cased-properties-deep.d.ts +63 -0
- package/node_modules/type-fest/source/kebab-cased-properties.d.ts +40 -0
- package/node_modules/type-fest/source/keys-of-union.d.ts +42 -0
- package/node_modules/type-fest/source/last-array-element.d.ts +38 -0
- package/node_modules/type-fest/source/less-than-or-equal.d.ts +22 -0
- package/node_modules/type-fest/source/less-than.d.ts +26 -0
- package/node_modules/type-fest/source/literal-to-primitive-deep.d.ts +36 -0
- package/node_modules/type-fest/source/literal-to-primitive.d.ts +36 -0
- package/node_modules/type-fest/source/literal-union.d.ts +37 -0
- package/node_modules/type-fest/source/merge-deep.d.ts +486 -0
- package/node_modules/type-fest/source/merge-exclusive.d.ts +41 -0
- package/node_modules/type-fest/source/merge.d.ts +48 -0
- package/node_modules/type-fest/source/multidimensional-array.d.ts +44 -0
- package/node_modules/type-fest/source/multidimensional-readonly-array.d.ts +48 -0
- package/node_modules/type-fest/source/non-empty-object.d.ts +35 -0
- package/node_modules/type-fest/source/non-empty-string.d.ts +28 -0
- package/node_modules/type-fest/source/non-empty-tuple.d.ts +21 -0
- package/node_modules/type-fest/source/numeric.d.ts +222 -0
- package/node_modules/type-fest/source/observable-like.d.ts +63 -0
- package/node_modules/type-fest/source/omit-deep.d.ts +167 -0
- package/node_modules/type-fest/source/omit-index-signature.d.ts +95 -0
- package/node_modules/type-fest/source/opaque.d.ts +1 -0
- package/node_modules/type-fest/source/optional-keys-of.d.ts +39 -0
- package/node_modules/type-fest/source/or.d.ts +25 -0
- package/node_modules/type-fest/source/override-properties.d.ts +36 -0
- package/node_modules/type-fest/source/package-json.d.ts +676 -0
- package/node_modules/type-fest/source/partial-deep.d.ts +151 -0
- package/node_modules/type-fest/source/partial-on-undefined-deep.d.ts +78 -0
- package/node_modules/type-fest/source/pascal-case.d.ts +42 -0
- package/node_modules/type-fest/source/pascal-cased-properties-deep.d.ts +62 -0
- package/node_modules/type-fest/source/pascal-cased-properties.d.ts +36 -0
- package/node_modules/type-fest/source/paths.d.ts +262 -0
- package/node_modules/type-fest/source/pick-deep.d.ts +149 -0
- package/node_modules/type-fest/source/pick-index-signature.d.ts +50 -0
- package/node_modules/type-fest/source/primitive.d.ts +13 -0
- package/node_modules/type-fest/source/promisable.d.ts +25 -0
- package/node_modules/type-fest/source/readonly-deep.d.ts +81 -0
- package/node_modules/type-fest/source/readonly-keys-of.d.ts +30 -0
- package/node_modules/type-fest/source/readonly-tuple.d.ts +41 -0
- package/node_modules/type-fest/source/replace.d.ts +85 -0
- package/node_modules/type-fest/source/require-all-or-none.d.ts +51 -0
- package/node_modules/type-fest/source/require-at-least-one.d.ts +47 -0
- package/node_modules/type-fest/source/require-exactly-one.d.ts +45 -0
- package/node_modules/type-fest/source/require-one-or-none.d.ts +46 -0
- package/node_modules/type-fest/source/required-deep.d.ts +78 -0
- package/node_modules/type-fest/source/required-keys-of.d.ts +30 -0
- package/node_modules/type-fest/source/schema.d.ts +114 -0
- package/node_modules/type-fest/source/screaming-snake-case.d.ts +28 -0
- package/node_modules/type-fest/source/set-field-type.d.ts +65 -0
- package/node_modules/type-fest/source/set-non-nullable-deep.d.ts +83 -0
- package/node_modules/type-fest/source/set-non-nullable.d.ts +39 -0
- package/node_modules/type-fest/source/set-optional.d.ts +38 -0
- package/node_modules/type-fest/source/set-parameter-type.d.ts +117 -0
- package/node_modules/type-fest/source/set-readonly.d.ts +39 -0
- package/node_modules/type-fest/source/set-required-deep.d.ts +68 -0
- package/node_modules/type-fest/source/set-required.d.ts +70 -0
- package/node_modules/type-fest/source/set-return-type.d.ts +29 -0
- package/node_modules/type-fest/source/shared-union-fields-deep.d.ts +178 -0
- package/node_modules/type-fest/source/shared-union-fields.d.ts +76 -0
- package/node_modules/type-fest/source/simplify-deep.d.ts +115 -0
- package/node_modules/type-fest/source/simplify.d.ts +58 -0
- package/node_modules/type-fest/source/single-key-object.d.ts +29 -0
- package/node_modules/type-fest/source/snake-case.d.ts +45 -0
- package/node_modules/type-fest/source/snake-cased-properties-deep.d.ts +63 -0
- package/node_modules/type-fest/source/snake-cased-properties.d.ts +40 -0
- package/node_modules/type-fest/source/split.d.ts +88 -0
- package/node_modules/type-fest/source/spread.d.ts +84 -0
- package/node_modules/type-fest/source/string-key-of.d.ts +25 -0
- package/node_modules/type-fest/source/string-repeat.d.ts +47 -0
- package/node_modules/type-fest/source/string-slice.d.ts +37 -0
- package/node_modules/type-fest/source/stringified.d.ts +23 -0
- package/node_modules/type-fest/source/structured-cloneable.d.ts +92 -0
- package/node_modules/type-fest/source/subtract.d.ts +83 -0
- package/node_modules/type-fest/source/sum.d.ts +78 -0
- package/node_modules/type-fest/source/tagged-union.d.ts +51 -0
- package/node_modules/type-fest/source/tagged.d.ts +256 -0
- package/node_modules/type-fest/source/trim.d.ts +27 -0
- package/node_modules/type-fest/source/tsconfig-json.d.ts +1294 -0
- package/node_modules/type-fest/source/tuple-to-object.d.ts +42 -0
- package/node_modules/type-fest/source/tuple-to-union.d.ts +51 -0
- package/node_modules/type-fest/source/typed-array.d.ts +17 -0
- package/node_modules/type-fest/source/undefined-on-partial-deep.d.ts +80 -0
- package/node_modules/type-fest/source/union-to-intersection.d.ts +61 -0
- package/node_modules/type-fest/source/union-to-tuple.d.ts +56 -0
- package/node_modules/type-fest/source/unknown-array.d.ts +25 -0
- package/node_modules/type-fest/source/unknown-map.d.ts +24 -0
- package/node_modules/type-fest/source/unknown-record.d.ts +31 -0
- package/node_modules/type-fest/source/unknown-set.d.ts +24 -0
- package/node_modules/type-fest/source/value-of.d.ts +42 -0
- package/node_modules/type-fest/source/words.d.ts +118 -0
- package/node_modules/type-fest/source/writable-deep.d.ts +83 -0
- package/node_modules/type-fest/source/writable-keys-of.d.ts +33 -0
- package/node_modules/type-fest/source/writable.d.ts +68 -0
- package/node_modules/widest-line/index.d.ts +12 -0
- package/node_modules/widest-line/index.js +11 -0
- package/node_modules/widest-line/license +9 -0
- package/node_modules/widest-line/package.json +60 -0
- package/node_modules/widest-line/readme.md +26 -0
- package/node_modules/wrap-ansi/index.d.ts +41 -0
- package/node_modules/wrap-ansi/index.js +222 -0
- package/node_modules/wrap-ansi/license +9 -0
- package/node_modules/wrap-ansi/package.json +69 -0
- package/node_modules/wrap-ansi/readme.md +75 -0
- package/package.json +78 -0
- package/scripts/postinstall.cjs +122 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commit-planner.ts — Generación del plan de commits para una rama.
|
|
3
|
+
*
|
|
4
|
+
* A partir de los bloques asignados a una rama produce una secuencia ordenada
|
|
5
|
+
* de {@link CommitPlan}: cada elemento representa un commit con sus archivos,
|
|
6
|
+
* mensaje sugerido y metadatos (type, scope, subject).
|
|
7
|
+
*
|
|
8
|
+
* ### Algoritmo de agrupación
|
|
9
|
+
* 1. Los bloques se ordenan: prioridad asc → depScore desc → indivisibles primero → más líneas primero.
|
|
10
|
+
* 2. Bloques **indivisibles** o **medianos** (`lines > mediumFileThreshold`) van
|
|
11
|
+
* siempre en su propio commit (`mustIsolate = true`).
|
|
12
|
+
* 3. Los demás bloques se acumulan en el commit actual hasta superar
|
|
13
|
+
* `maxLinesPerCommitIdeal` o `maxFilesPerCommit`; entonces se flush y se empieza uno nuevo.
|
|
14
|
+
* 4. Al terminar el bucle, el grupo pendiente se flushea.
|
|
15
|
+
*
|
|
16
|
+
* ### Inferencia del mensaje de commit
|
|
17
|
+
* El tipo, scope y subject se infieren heurísticamente a partir de los nombres y
|
|
18
|
+
* rutas de los archivos del commit. Si `enrichCommitTickets` se ejecuta después,
|
|
19
|
+
* el ticket se incrusta en `suggestedMessage`.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { basename, dirname } from "node:path";
|
|
24
|
+
import { normalizePathValue } from "../shared/utils.js";
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Commit type / scope / subject detection
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Inferencia de tipo, scope y subject del commit
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/**
|
|
32
|
+
* Infiere el tipo Conventional Commits a partir de los patrones de ruta y nombre de los archivos.
|
|
33
|
+
*
|
|
34
|
+
* Tipos soportados (en orden de prioridad):
|
|
35
|
+
* 1. `ci` — archivos de CI/CD o DevSecOps
|
|
36
|
+
* 2. `docs` — todos los archivos son documentación
|
|
37
|
+
* 3. `test` — todos los archivos son pruebas
|
|
38
|
+
* 4. `style` — todos son archivos de formato/linters (CSS, .eslintrc, .prettierrc…)
|
|
39
|
+
* 5. `perf` — archivos con patrones de rendimiento/optimización
|
|
40
|
+
* 6. `refactor` — tipos, interfaces, schemas, DTOs, modelos
|
|
41
|
+
* 7. `fix` — archivos con patrones de corrección de errores (bugfix/hotfix)
|
|
42
|
+
* 8. `chore` — configuraciones, dependencias, YAML/TOML no-CI
|
|
43
|
+
* 9. `feat` — fallback para todo lo demás
|
|
44
|
+
*
|
|
45
|
+
* @param files - Rutas de los archivos del commit.
|
|
46
|
+
*/
|
|
47
|
+
function detectCommitType(files) {
|
|
48
|
+
const lower = files.map((f) => f.toLowerCase());
|
|
49
|
+
// ci: integración continua / entrega continua / DevSecOps
|
|
50
|
+
if (lower.some((f) => /(?:^|\/)\.github\/workflows\//.test(f) ||
|
|
51
|
+
/(?:^|\/)\.gitlab-ci\.ya?ml$/.test(f) ||
|
|
52
|
+
/(?:^|\/)[jJ]enkinsfile(\.[^/]*)?$/.test(f) ||
|
|
53
|
+
/(?:^|\/)\.circleci\//.test(f) ||
|
|
54
|
+
/(?:^|\/)\.travis\.ya?ml$/.test(f) ||
|
|
55
|
+
/(?:^|\/)azure-pipelines\.ya?ml$/.test(f) ||
|
|
56
|
+
/(?:^|\/)bitbucket-pipelines\.ya?ml$/.test(f) ||
|
|
57
|
+
/(?:^|\/)[dD]ockerfile(\.[^/]*)?$/.test(f) ||
|
|
58
|
+
/(?:^|\/)docker-compose[^/]*\.ya?ml$/.test(f))) {
|
|
59
|
+
return "ci";
|
|
60
|
+
}
|
|
61
|
+
// docs: todos los archivos son documentación
|
|
62
|
+
if (lower.every((f) => /(^|\/)(docs|doc)(\/|$)|(^|\/)readme(\.[^/]*)?$|(^|\/)changelog(\.[^/]*)?$|\.md$|\.adoc$|\.rst$/.test(f))) {
|
|
63
|
+
return "docs";
|
|
64
|
+
}
|
|
65
|
+
// test: todos los archivos son pruebas unitarias
|
|
66
|
+
if (lower.every((f) => /(^|\/)(__tests__|tests|test)(\/|$)|(\.spec\.|\.test\.)/.test(f))) {
|
|
67
|
+
return "test";
|
|
68
|
+
}
|
|
69
|
+
// style: todos son archivos de formato o herramientas de estilo (linters, formateadores, CSS)
|
|
70
|
+
if (lower.every((f) => /\.(css|scss|sass|less)$/.test(f) ||
|
|
71
|
+
/(?:^|\/)\.eslintrc[^/]*$/.test(f) ||
|
|
72
|
+
/(?:^|\/)\.prettierrc[^/]*$/.test(f) ||
|
|
73
|
+
/(?:^|\/)\.editorconfig$/.test(f) ||
|
|
74
|
+
/(?:^|\/)\.stylelintrc[^/]*$/.test(f))) {
|
|
75
|
+
return "style";
|
|
76
|
+
}
|
|
77
|
+
// perf: mejoras de rendimiento sin cambios funcionales
|
|
78
|
+
if (lower.some((f) => /\b(perf|performance|optim|optimize|cache|memo|memoize)\b/.test(f))) {
|
|
79
|
+
return "perf";
|
|
80
|
+
}
|
|
81
|
+
// refactor: reestructuración de código sin nuevas funcionalidades ni correcciones
|
|
82
|
+
if (lower.some((f) => /\b(type|types|interface|interfaces|schema|schemas|dto|dtos|model|models)\b/.test(f))) {
|
|
83
|
+
return "refactor";
|
|
84
|
+
}
|
|
85
|
+
// fix: corrección de errores (aplica también a ramas bugfix y hotfix)
|
|
86
|
+
if (lower.some((f) => /\b(fix|bug|hotfix|patch)\b/.test(f))) {
|
|
87
|
+
return "fix";
|
|
88
|
+
}
|
|
89
|
+
// chore: configuraciones, dependencias externas, empaquetadores, migraciones
|
|
90
|
+
// .json solo es chore cuando es un archivo de configuración conocido; los .json en src/ pueden ser feat.
|
|
91
|
+
if (lower.some((f) => /(^|\/)(config|configs|settings)(\/|$)|\.ya?ml$|\.toml$/.test(f) ||
|
|
92
|
+
/(?:^|\/)(?:package|tsconfig[^/]*|\.babelrc|[a-z.]+\.config)\.json$/.test(f))) {
|
|
93
|
+
return "chore";
|
|
94
|
+
}
|
|
95
|
+
return "feat";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Infiere el scope del commit a partir del directorio más frecuente entre los archivos.
|
|
99
|
+
*
|
|
100
|
+
* Ignora directorios genéricos (`src`, `lib`, `app`, `dist`) que no aportan
|
|
101
|
+
* contexto útil. Si todos los archivos están en directorios genéricos, usa el
|
|
102
|
+
* nombre base del primer archivo como fallback.
|
|
103
|
+
*
|
|
104
|
+
* @param files - Rutas de los archivos del commit.
|
|
105
|
+
*/
|
|
106
|
+
function detectCommitScope(files) {
|
|
107
|
+
const normalized = files.map((f) => normalizePathValue(f).toLowerCase());
|
|
108
|
+
// Derivar scope del directorio padre más frecuente (genérico para cualquier proyecto)
|
|
109
|
+
const dirs = normalized
|
|
110
|
+
.map((f) => dirname(f))
|
|
111
|
+
.filter((d) => d && d !== "." && d !== "/")
|
|
112
|
+
.map((d) => d.split("/").pop() ?? d);
|
|
113
|
+
if (dirs.length) {
|
|
114
|
+
const counts = new Map();
|
|
115
|
+
for (const dir of dirs)
|
|
116
|
+
counts.set(dir, (counts.get(dir) ?? 0) + 1);
|
|
117
|
+
const top = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
118
|
+
// Excluir carpetas genéricas que no aportan contexto
|
|
119
|
+
const generic = new Set(["src", "lib", "app", "dist"]);
|
|
120
|
+
if (top && !generic.has(top))
|
|
121
|
+
return top;
|
|
122
|
+
}
|
|
123
|
+
// Fallback: nombre base del primer archivo
|
|
124
|
+
const baseNames = normalized
|
|
125
|
+
.map((f) => basename(f).replace(/\.[^.]+$/, ""))
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
if (baseNames.length)
|
|
128
|
+
return baseNames[0];
|
|
129
|
+
return "core";
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Genera un subject breve y legible para el commit a partir del tipo y los archivos.
|
|
133
|
+
*
|
|
134
|
+
* Las descripciones están en español para coincidir con la UI del proyecto.
|
|
135
|
+
* Para commits de tipo `feat` con un solo archivo, incluye el nombre del archivo
|
|
136
|
+
* para dar más contexto. El parámetro `_scope` está reservado para futura
|
|
137
|
+
* personalización por scope.
|
|
138
|
+
*
|
|
139
|
+
* @param files - Rutas de los archivos del commit.
|
|
140
|
+
* @param type - Tipo Conventional Commits ya inferido.
|
|
141
|
+
* @param _scope - Scope ya inferido (no usado actualmente, reservado).
|
|
142
|
+
*/
|
|
143
|
+
function detectCommitSubject(files, type, _scope) {
|
|
144
|
+
const lower = files.map((f) => f.toLowerCase());
|
|
145
|
+
// Sujetos basados en el tipo (siempre aplicables, independientes del proyecto)
|
|
146
|
+
if (type === "ci")
|
|
147
|
+
return "actualizar configuracion de CI/CD";
|
|
148
|
+
if (type === "docs")
|
|
149
|
+
return "actualizar documentacion relacionada";
|
|
150
|
+
if (type === "test")
|
|
151
|
+
return "agregar o corregir pruebas unitarias";
|
|
152
|
+
if (type === "style")
|
|
153
|
+
return "aplicar formato y estilo de codigo";
|
|
154
|
+
if (type === "perf")
|
|
155
|
+
return "mejorar rendimiento de la aplicacion";
|
|
156
|
+
if (type === "fix")
|
|
157
|
+
return "corregir comportamiento incorrecto";
|
|
158
|
+
if (type === "revert")
|
|
159
|
+
return "revertir cambios anteriores";
|
|
160
|
+
if (type === "refactor")
|
|
161
|
+
return "refactorizar sin cambios de negocio";
|
|
162
|
+
if (type === "chore") {
|
|
163
|
+
if (lower.some((f) => /\.(ya?ml|toml)$/.test(f)))
|
|
164
|
+
return "actualizar archivos de configuracion";
|
|
165
|
+
return "actualizar dependencias o configuracion";
|
|
166
|
+
}
|
|
167
|
+
// Sujeto basado en extensión / tipo de archivo
|
|
168
|
+
if (lower.some((f) => f.endsWith(".html") || f.endsWith(".css"))) {
|
|
169
|
+
return "actualizar vistas y estilos";
|
|
170
|
+
}
|
|
171
|
+
// Fallback genérico
|
|
172
|
+
return files.length === 1
|
|
173
|
+
? `actualizar ${basename(files[0]).toLowerCase()}`
|
|
174
|
+
: "actualizar archivos relacionados";
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Combina las tres funciones de detección en un solo objeto de partes del título.
|
|
178
|
+
* Mantiene el orden de llamadas estándar para evitar repetirlo en cada sitio de uso.
|
|
179
|
+
*/
|
|
180
|
+
export function buildCommitTitleParts(files) {
|
|
181
|
+
const suggestedType = detectCommitType(files);
|
|
182
|
+
const suggestedScope = detectCommitScope(files);
|
|
183
|
+
const suggestedSubject = detectCommitSubject(files, suggestedType, suggestedScope);
|
|
184
|
+
return { suggestedType, suggestedScope, suggestedSubject };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Construye el mensaje de commit en formato Conventional Commits:
|
|
188
|
+
* `type(scope): [ticket] subject`.
|
|
189
|
+
*
|
|
190
|
+
* @param suggestedType - Tipo inferido (feat, fix, chore, etc.).
|
|
191
|
+
* @param suggestedScope - Scope inferido del directorio/nombre de archivo.
|
|
192
|
+
* @param suggestedSubject - Descripción breve de los cambios.
|
|
193
|
+
* @param ticketCode - Prefijo de ticket opcional (p.ej. "ABC-123").
|
|
194
|
+
*/
|
|
195
|
+
export function buildSuggestedMessage(suggestedType, suggestedScope, suggestedSubject, ticketCode) {
|
|
196
|
+
const ticketPart = ticketCode ? `${ticketCode} ` : "";
|
|
197
|
+
return `${suggestedType}(${suggestedScope}): ${ticketPart}${suggestedSubject}`;
|
|
198
|
+
}
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
// Public
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
/**
|
|
203
|
+
* Genera el plan de commits para un conjunto de bloques.
|
|
204
|
+
*
|
|
205
|
+
* Ordena los bloques por prioridad (asc), luego por depScore (desc), luego por
|
|
206
|
+
* divisibilidad y por tamaño, y los agrupa respetando `config.maxFilesPerCommit`
|
|
207
|
+
* y `config.maxLinesPerCommitIdeal`. Para cada grupo infiere type, scope y
|
|
208
|
+
* subject a partir de los nombres y rutas de los archivos.
|
|
209
|
+
*
|
|
210
|
+
* @param blocks - Bloques a incluir en el plan.
|
|
211
|
+
* @param config - Configuración activa del proyecto.
|
|
212
|
+
* @returns Array de {@link CommitPlan} en el orden de commit sugerido.
|
|
213
|
+
*/
|
|
214
|
+
export function buildCommitPlan(blocks, config) {
|
|
215
|
+
// Si la rama tiene contexto de nomenclatura, usamos su ticket como prefijo automático.
|
|
216
|
+
const autoTicket = config.branchNamingContext
|
|
217
|
+
? `${config.branchNamingContext.teamCode}-${config.branchNamingContext.storyNumber}`
|
|
218
|
+
: undefined;
|
|
219
|
+
const ordered = [...blocks].sort((a, b) => {
|
|
220
|
+
if (a.priority !== b.priority)
|
|
221
|
+
return a.priority - b.priority;
|
|
222
|
+
if (a.depScore !== b.depScore)
|
|
223
|
+
return b.depScore - a.depScore;
|
|
224
|
+
if (a.divisible !== b.divisible)
|
|
225
|
+
return a.divisible ? 1 : -1;
|
|
226
|
+
return b.lines - a.lines;
|
|
227
|
+
});
|
|
228
|
+
const commits = [];
|
|
229
|
+
let currentFiles = [];
|
|
230
|
+
let currentLines = 0;
|
|
231
|
+
let index = 1;
|
|
232
|
+
const pushCurrent = () => {
|
|
233
|
+
if (!currentFiles.length)
|
|
234
|
+
return;
|
|
235
|
+
const titleParts = buildCommitTitleParts(currentFiles);
|
|
236
|
+
commits.push({
|
|
237
|
+
index,
|
|
238
|
+
files: [...currentFiles],
|
|
239
|
+
totalLines: currentLines,
|
|
240
|
+
...titleParts,
|
|
241
|
+
suggestedMessage: buildSuggestedMessage(titleParts.suggestedType, titleParts.suggestedScope, titleParts.suggestedSubject, autoTicket)
|
|
242
|
+
});
|
|
243
|
+
index += 1;
|
|
244
|
+
currentFiles = [];
|
|
245
|
+
currentLines = 0;
|
|
246
|
+
};
|
|
247
|
+
for (const block of ordered) {
|
|
248
|
+
const mustIsolate = !block.divisible || block.lines > config.mediumFileThreshold;
|
|
249
|
+
if (mustIsolate) {
|
|
250
|
+
pushCurrent();
|
|
251
|
+
const titleParts = buildCommitTitleParts(block.files);
|
|
252
|
+
commits.push({
|
|
253
|
+
index,
|
|
254
|
+
files: [...block.files],
|
|
255
|
+
totalLines: block.lines,
|
|
256
|
+
...titleParts,
|
|
257
|
+
suggestedMessage: buildSuggestedMessage(titleParts.suggestedType, titleParts.suggestedScope, titleParts.suggestedSubject, autoTicket)
|
|
258
|
+
});
|
|
259
|
+
index += 1;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const projectedLines = currentLines + block.lines;
|
|
263
|
+
const projectedFiles = currentFiles.length + block.files.length;
|
|
264
|
+
if (projectedLines > config.maxLinesPerCommitIdeal ||
|
|
265
|
+
projectedFiles > config.maxFilesPerCommit) {
|
|
266
|
+
pushCurrent();
|
|
267
|
+
}
|
|
268
|
+
currentFiles.push(...block.files);
|
|
269
|
+
currentLines += block.lines;
|
|
270
|
+
}
|
|
271
|
+
pushCurrent();
|
|
272
|
+
return commits;
|
|
273
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dependency.ts — Detección heurística de dependencias entre archivos cambiados.
|
|
3
|
+
*
|
|
4
|
+
* Analiza las sentencias de importación de los archivos modificados para
|
|
5
|
+
* construir un grafo de aristas `{ from, to }` donde ambos nodos pertenecen
|
|
6
|
+
* al conjunto de archivos cambiados. Las importaciones hacia código no tocado
|
|
7
|
+
* se ignoran (no aportan información útil para la división del PR).
|
|
8
|
+
*
|
|
9
|
+
* ### Lenguajes soportados
|
|
10
|
+
* - **TypeScript / JavaScript** (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`):
|
|
11
|
+
* detecta `import … from`, `require()` e `import()` dinámico.
|
|
12
|
+
* Resuelve rutas relativas probando las extensiones más comunes y los
|
|
13
|
+
* patrones de módulo índice (`index.ts`, `index.js`, etc.).
|
|
14
|
+
* - **Python** (`.py`): detecta `import …` y `from … import`.
|
|
15
|
+
* Soporta imports relativos (`from . import …`) y absolutos.
|
|
16
|
+
* - **Java** (`.java`): detecta `import com.package.ClassName;`.
|
|
17
|
+
* Construye el conjunto de archivos `.java` del repositorio una sola vez
|
|
18
|
+
* (con `git ls-files -- '*.java'`) para resolver en O(1) sin `find` por clase.
|
|
19
|
+
*
|
|
20
|
+
* ### Cachés de rendimiento
|
|
21
|
+
* - `_fileContentCache`: evita releer o ejecutar `git show HEAD:<archivo>` más de
|
|
22
|
+
* una vez por archivo en un mismo análisis. Se limpia al inicio de cada llamada
|
|
23
|
+
* a `buildDependencyEdges` para garantizar datos frescos.
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync } from "node:fs";
|
|
26
|
+
import { dirname, extname } from "node:path";
|
|
27
|
+
import { q, shSafe } from "../git/git.js";
|
|
28
|
+
import { normalizePathValue, fileExistsInRepoOrFs } from "../shared/utils.js";
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Detección de lenguaje
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Clasifica un archivo por su lenguaje de programación basándose en la extensión.
|
|
34
|
+
* Los lenguajes no reconocidos devuelven `"other"` y no se analiza su contenido.
|
|
35
|
+
*/
|
|
36
|
+
function detectLanguage(file) {
|
|
37
|
+
const ext = extname(file).toLowerCase();
|
|
38
|
+
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext))
|
|
39
|
+
return "js";
|
|
40
|
+
if (ext === ".py")
|
|
41
|
+
return "py";
|
|
42
|
+
if (ext === ".java")
|
|
43
|
+
return "java";
|
|
44
|
+
return "other";
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Lectura de archivos con caché en memoria
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Caché de contenido por ruta para evitar releer o ejecutar `git show` múltiples veces. */
|
|
50
|
+
const _fileContentCache = new Map();
|
|
51
|
+
/**
|
|
52
|
+
* Limpia la caché de contenido de archivos.
|
|
53
|
+
* Útil para garantizar datos frescos entre análisis sucesivos en el mismo proceso (tests, llamadas encadenadas).
|
|
54
|
+
*/
|
|
55
|
+
export function clearDependencyCache() {
|
|
56
|
+
_fileContentCache.clear();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Lee el contenido de un archivo para parsear sus imports.
|
|
60
|
+
*
|
|
61
|
+
* Estrategia:
|
|
62
|
+
* 1. Si el archivo existe en disco (working tree), lo lee directamente.
|
|
63
|
+
* 2. Si fue eliminado o no está en disco, intenta obtenerlo desde `git show HEAD:<ruta>`.
|
|
64
|
+
* Esto permite detectar dependencias de archivos que han sido borrados pero
|
|
65
|
+
* aún están en el último commit.
|
|
66
|
+
* 3. El resultado se cachéa para evitar releer el mismo archivo en el mismo análisis.
|
|
67
|
+
*
|
|
68
|
+
* @param file - Ruta relativa del archivo a leer.
|
|
69
|
+
* @returns Contenido del archivo como texto, o `""` si no se puede obtener.
|
|
70
|
+
*/
|
|
71
|
+
function readFileForParsing(file) {
|
|
72
|
+
const cached = _fileContentCache.get(file);
|
|
73
|
+
if (cached !== undefined)
|
|
74
|
+
return cached;
|
|
75
|
+
let content;
|
|
76
|
+
try {
|
|
77
|
+
content = readFileSync(file, "utf8");
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
content = shSafe(`git show HEAD:${q(file)}`);
|
|
81
|
+
}
|
|
82
|
+
_fileContentCache.set(file, content);
|
|
83
|
+
return content;
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Resolvedores de rutas de import por lenguaje
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/**
|
|
89
|
+
* Resuelve un import relativo JS/TS a una ruta de archivo concreta.
|
|
90
|
+
*
|
|
91
|
+
* Solo procesa imports relativos (que comienzan por `"."`). Los imports de
|
|
92
|
+
* módulos de node_modules o paths absolutos se ignoran porque no pertenecerán
|
|
93
|
+
* al conjunto de archivos cambiados.
|
|
94
|
+
*
|
|
95
|
+
* Prueba en orden: la ruta tal cual, + las extensiones `.ts`, `.tsx`, `.js`, `.jsx`,
|
|
96
|
+
* y los variantes `/index.<ext>`. Devuelve la primera que exista en disco o en git.
|
|
97
|
+
*
|
|
98
|
+
* @param importer - Ruta del archivo que contiene el import.
|
|
99
|
+
* @param imp - Valor del import (ej. `"./utils"`, `"../core/service"`).
|
|
100
|
+
* @returns Ruta resuelta o `null` si no se encuentra.
|
|
101
|
+
*/
|
|
102
|
+
function resolveJsImport(importer, imp) {
|
|
103
|
+
if (!imp.startsWith("."))
|
|
104
|
+
return null;
|
|
105
|
+
const importerDir = dirname(importer);
|
|
106
|
+
const candidate = normalizePathValue(`${importerDir}/${imp}`);
|
|
107
|
+
const variants = [
|
|
108
|
+
candidate,
|
|
109
|
+
`${candidate}.ts`,
|
|
110
|
+
`${candidate}.tsx`,
|
|
111
|
+
`${candidate}.js`,
|
|
112
|
+
`${candidate}.jsx`,
|
|
113
|
+
`${candidate}.mjs`,
|
|
114
|
+
`${candidate}.cjs`,
|
|
115
|
+
`${candidate}/index.ts`,
|
|
116
|
+
`${candidate}/index.tsx`,
|
|
117
|
+
`${candidate}/index.js`,
|
|
118
|
+
`${candidate}/index.jsx`,
|
|
119
|
+
`${candidate}/index.mjs`,
|
|
120
|
+
`${candidate}/index.cjs`
|
|
121
|
+
];
|
|
122
|
+
for (const v of variants) {
|
|
123
|
+
if (fileExistsInRepoOrFs(v))
|
|
124
|
+
return v;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Resuelve un import Python (relativo o absoluto) a una ruta de archivo.
|
|
130
|
+
*
|
|
131
|
+
* - Imports relativos (`from . import`, `from .utils import`): usa el directorio
|
|
132
|
+
* del importador como base y construye la ruta.
|
|
133
|
+
* - Imports absolutos (`import os.path`): convierte los puntos del paquete a `/`.
|
|
134
|
+
* - Prueba tanto `<ruta>.py` como `<ruta>/__init__.py` para soportar paquetes.
|
|
135
|
+
*
|
|
136
|
+
* @param importer - Ruta del archivo Python que contiene el import.
|
|
137
|
+
* @param imp - Módulo o paquete importado (ej. `"utils"`, `".helpers"`, `"core.service"`).
|
|
138
|
+
* @returns Ruta resuelta o `null` si no se encuentra.
|
|
139
|
+
*/
|
|
140
|
+
function resolvePyImport(importer, imp) {
|
|
141
|
+
if (imp.startsWith(".")) {
|
|
142
|
+
const dots = imp.match(/^\.+/)[0].length;
|
|
143
|
+
let base = dirname(importer);
|
|
144
|
+
for (let i = 1; i < dots; i++)
|
|
145
|
+
base = dirname(base);
|
|
146
|
+
const trimmed = imp.replace(/^\.+/, "");
|
|
147
|
+
const target = trimmed
|
|
148
|
+
? `${base}/${trimmed.replace(/\./g, "/")}`
|
|
149
|
+
: base;
|
|
150
|
+
for (const v of [`${target}.py`, `${target}/__init__.py`]) {
|
|
151
|
+
const normalized = normalizePathValue(v);
|
|
152
|
+
if (fileExistsInRepoOrFs(normalized))
|
|
153
|
+
return normalized;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const base = imp.replace(/\./g, "/");
|
|
158
|
+
for (const v of [`${base}.py`, `${base}/__init__.py`]) {
|
|
159
|
+
const normalized = normalizePathValue(v);
|
|
160
|
+
if (fileExistsInRepoOrFs(normalized))
|
|
161
|
+
return normalized;
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resuelve un import Java buscando en el conjunto pre-cargado de archivos .java.
|
|
167
|
+
* Evita lanzar un proceso `find` por cada sentencia import (O(n) por archivo → O(1) por import).
|
|
168
|
+
*
|
|
169
|
+
* @param imp - Nombre de clase o paquete Java a resolver.
|
|
170
|
+
* @param javaFiles - Conjunto de rutas .java del repositorio (construido una vez por análisis).
|
|
171
|
+
*/
|
|
172
|
+
function resolveJavaImport(imp, javaFiles) {
|
|
173
|
+
if (!/^[a-zA-Z0-9_.]+$/.test(imp))
|
|
174
|
+
return null;
|
|
175
|
+
const rel = imp.replace(/\./g, "/") + ".java";
|
|
176
|
+
for (const f of javaFiles) {
|
|
177
|
+
if (f === rel || f.endsWith(`/${rel}`))
|
|
178
|
+
return f;
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// Deduplicación de aristas
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
/**
|
|
186
|
+
* Elimina aristas duplicadas del grafo de dependencias.
|
|
187
|
+
*
|
|
188
|
+
* Una arista duplicada puede ocurrir cuando el mismo par `from → to` aparece
|
|
189
|
+
* en múltiples sentencias de import dentro del mismo archivo (poco frecuente,
|
|
190
|
+
* pero posible con barrel imports o re-exports).
|
|
191
|
+
*
|
|
192
|
+
* Usa un `Set<string>` de claves `"from=>to"` para detección O(n).
|
|
193
|
+
*/
|
|
194
|
+
function dedupeEdges(edges) {
|
|
195
|
+
const seen = new Set();
|
|
196
|
+
const out = [];
|
|
197
|
+
for (const e of edges) {
|
|
198
|
+
const key = `${e.from}=>${e.to}`;
|
|
199
|
+
if (!seen.has(key)) {
|
|
200
|
+
seen.add(key);
|
|
201
|
+
out.push(e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Public
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
/**
|
|
210
|
+
* Detecta aristas de dependencia entre los archivos cambiados analizando sus
|
|
211
|
+
* sentencias de importación (TypeScript/JS, Python, Java).
|
|
212
|
+
*
|
|
213
|
+
* Solo se incluyen dependencias cuyo destino también esté en el conjunto de
|
|
214
|
+
* archivos cambiados; las importaciones hacia código no modificado se ignoran.
|
|
215
|
+
*
|
|
216
|
+
* Los contenidos de archivos se cachean en memoria para evitar múltiples
|
|
217
|
+
* llamadas a `readFileSync` o `git show` por el mismo archivo.
|
|
218
|
+
*
|
|
219
|
+
* @param files - Rutas relativas de los archivos cambiados en el working tree.
|
|
220
|
+
* @returns Array de aristas `{ from, to }` sin duplicados.
|
|
221
|
+
*/
|
|
222
|
+
export function buildDependencyEdges(files) {
|
|
223
|
+
// Limpiar caché de contenido al iniciar cada análisis para evitar datos obsoletos.
|
|
224
|
+
// No se limpia _trackedFilesCache: ya es fresca desde getFileStats (llamado antes
|
|
225
|
+
// en el flujo CLI) y relanzar git ls-files sería redundante.
|
|
226
|
+
clearDependencyCache();
|
|
227
|
+
// Construir el conjunto de archivos .java del repositorio una sola vez.
|
|
228
|
+
// Evita lanzar un subproceso `find` por cada sentencia import Java.
|
|
229
|
+
const javaFiles = new Set(shSafe("git ls-files -- '*.java'")
|
|
230
|
+
.split("\n")
|
|
231
|
+
.map((s) => s.trim())
|
|
232
|
+
.filter(Boolean));
|
|
233
|
+
const changedSet = new Set(files);
|
|
234
|
+
const edges = [];
|
|
235
|
+
for (const file of files) {
|
|
236
|
+
const lang = detectLanguage(file);
|
|
237
|
+
const content = readFileForParsing(file);
|
|
238
|
+
if (lang === "js") {
|
|
239
|
+
const matches = [
|
|
240
|
+
...content.matchAll(/from\s+['"]([^'"]+)['"]/g),
|
|
241
|
+
...content.matchAll(/require\(['"]([^'"]+)['"]\)/g),
|
|
242
|
+
...content.matchAll(/import\(['"]([^'"]+)['"]\)/g)
|
|
243
|
+
];
|
|
244
|
+
for (const m of matches) {
|
|
245
|
+
const resolved = resolveJsImport(file, m[1]);
|
|
246
|
+
if (resolved && changedSet.has(resolved)) {
|
|
247
|
+
edges.push({ from: file, to: resolved });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (lang === "py") {
|
|
252
|
+
const lines = content.split("\n");
|
|
253
|
+
for (const line of lines) {
|
|
254
|
+
let m = line.match(/^import\s+([a-zA-Z0-9_.]+)/);
|
|
255
|
+
if (m) {
|
|
256
|
+
const resolved = resolvePyImport(file, m[1]);
|
|
257
|
+
if (resolved && changedSet.has(resolved)) {
|
|
258
|
+
edges.push({ from: file, to: resolved });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
m = line.match(/^from\s+([.a-zA-Z0-9_]+)\s+import/);
|
|
262
|
+
if (m) {
|
|
263
|
+
const resolved = resolvePyImport(file, m[1]);
|
|
264
|
+
if (resolved && changedSet.has(resolved)) {
|
|
265
|
+
edges.push({ from: file, to: resolved });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (lang === "java") {
|
|
271
|
+
const lines = content.split("\n");
|
|
272
|
+
for (const line of lines) {
|
|
273
|
+
const m = line.match(/^import\s+([a-zA-Z0-9_.]+);/);
|
|
274
|
+
if (m) {
|
|
275
|
+
const resolved = resolveJavaImport(m[1], javaFiles);
|
|
276
|
+
if (resolved && changedSet.has(resolved)) {
|
|
277
|
+
edges.push({ from: file, to: resolved });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return dedupeEdges(edges);
|
|
284
|
+
}
|