agent-ide 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/index.d.ts +1 -12
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +0 -16
- package/dist/application/index.js.map +1 -1
- package/dist/application/services/cache-coordinator.service.d.ts.map +1 -1
- package/dist/application/services/cache-coordinator.service.js.map +1 -1
- package/dist/application/services/module-coordinator.service.d.ts +3 -8
- package/dist/application/services/module-coordinator.service.d.ts.map +1 -1
- package/dist/application/services/module-coordinator.service.js +5 -139
- package/dist/application/services/module-coordinator.service.js.map +1 -1
- package/dist/application/types.d.ts +0 -63
- package/dist/application/types.d.ts.map +1 -1
- package/dist/application/types.js +1 -9
- package/dist/application/types.js.map +1 -1
- package/dist/core/call-hierarchy/call-hierarchy-analyzer.d.ts.map +1 -1
- package/dist/core/call-hierarchy/call-hierarchy-analyzer.js +3 -5
- package/dist/core/call-hierarchy/call-hierarchy-analyzer.js.map +1 -1
- package/dist/core/change-signature/call-site-updater.d.ts +108 -0
- package/dist/core/change-signature/call-site-updater.d.ts.map +1 -0
- package/dist/core/change-signature/call-site-updater.js +347 -0
- package/dist/core/change-signature/call-site-updater.js.map +1 -0
- package/dist/core/change-signature/change-signature-service.d.ts +11 -53
- package/dist/core/change-signature/change-signature-service.d.ts.map +1 -1
- package/dist/core/change-signature/change-signature-service.js +91 -475
- package/dist/core/change-signature/change-signature-service.js.map +1 -1
- package/dist/core/change-signature/index.d.ts +4 -0
- package/dist/core/change-signature/index.d.ts.map +1 -1
- package/dist/core/change-signature/index.js +5 -0
- package/dist/core/change-signature/index.js.map +1 -1
- package/dist/core/change-signature/signature-parser.d.ts +46 -4
- package/dist/core/change-signature/signature-parser.d.ts.map +1 -1
- package/dist/core/change-signature/signature-parser.js +180 -9
- package/dist/core/change-signature/signature-parser.js.map +1 -1
- package/dist/core/change-signature/signature-transformer.d.ts +20 -0
- package/dist/core/change-signature/signature-transformer.d.ts.map +1 -0
- package/dist/core/change-signature/signature-transformer.js +91 -0
- package/dist/core/change-signature/signature-transformer.js.map +1 -0
- package/dist/core/change-signature/signature-validator.d.ts +30 -0
- package/dist/core/change-signature/signature-validator.d.ts.map +1 -0
- package/dist/core/change-signature/signature-validator.js +152 -0
- package/dist/core/change-signature/signature-validator.js.map +1 -0
- package/dist/core/change-signature/utils.d.ts +19 -0
- package/dist/core/change-signature/utils.d.ts.map +1 -0
- package/dist/core/change-signature/utils.js +23 -0
- package/dist/core/change-signature/utils.js.map +1 -0
- package/dist/core/cycles/cycle-detector.d.ts.map +1 -1
- package/dist/core/cycles/cycle-detector.js +16 -8
- package/dist/core/cycles/cycle-detector.js.map +1 -1
- package/dist/core/cycles/index.d.ts +1 -1
- package/dist/core/cycles/index.d.ts.map +1 -1
- package/dist/core/cycles/index.js +1 -1
- package/dist/core/cycles/index.js.map +1 -1
- package/dist/core/cycles/types.d.ts +20 -4
- package/dist/core/cycles/types.d.ts.map +1 -1
- package/dist/core/cycles/types.js +22 -4
- package/dist/core/cycles/types.js.map +1 -1
- package/dist/core/deadcode/dead-code-detector.d.ts +22 -1
- package/dist/core/deadcode/dead-code-detector.d.ts.map +1 -1
- package/dist/core/deadcode/dead-code-detector.js +134 -61
- package/dist/core/deadcode/dead-code-detector.js.map +1 -1
- package/dist/core/deadcode/dead-code-remover.d.ts +11 -93
- package/dist/core/deadcode/dead-code-remover.d.ts.map +1 -1
- package/dist/core/deadcode/dead-code-remover.js +71 -690
- package/dist/core/deadcode/dead-code-remover.js.map +1 -1
- package/dist/core/deadcode/file-operations.d.ts +85 -0
- package/dist/core/deadcode/file-operations.d.ts.map +1 -0
- package/dist/core/deadcode/file-operations.js +263 -0
- package/dist/core/deadcode/file-operations.js.map +1 -0
- package/dist/core/deadcode/import-cleaner.d.ts +63 -0
- package/dist/core/deadcode/import-cleaner.d.ts.map +1 -0
- package/dist/core/deadcode/import-cleaner.js +229 -0
- package/dist/core/deadcode/import-cleaner.js.map +1 -0
- package/dist/core/deadcode/import-parser.d.ts +79 -0
- package/dist/core/deadcode/import-parser.d.ts.map +1 -0
- package/dist/core/deadcode/import-parser.js +229 -0
- package/dist/core/deadcode/import-parser.js.map +1 -0
- package/dist/core/deadcode/range-expander.d.ts +38 -0
- package/dist/core/deadcode/range-expander.d.ts.map +1 -0
- package/dist/core/deadcode/range-expander.js +175 -0
- package/dist/core/deadcode/range-expander.js.map +1 -0
- package/dist/core/deadcode/types.d.ts +2 -0
- package/dist/core/deadcode/types.d.ts.map +1 -1
- package/dist/core/deadcode/types.js.map +1 -1
- package/dist/core/find-references/index.d.ts +2 -2
- package/dist/core/find-references/index.d.ts.map +1 -1
- package/dist/core/find-references/index.js +1 -1
- package/dist/core/find-references/index.js.map +1 -1
- package/dist/core/impact/dependency-extractor.d.ts +25 -0
- package/dist/core/impact/dependency-extractor.d.ts.map +1 -0
- package/dist/core/impact/dependency-extractor.js +65 -0
- package/dist/core/impact/dependency-extractor.js.map +1 -0
- package/dist/core/impact/file-scanner.d.ts +52 -0
- package/dist/core/impact/file-scanner.d.ts.map +1 -0
- package/dist/core/impact/file-scanner.js +131 -0
- package/dist/core/impact/file-scanner.js.map +1 -0
- package/dist/core/impact/impact-analyzer.d.ts +3 -64
- package/dist/core/impact/impact-analyzer.d.ts.map +1 -1
- package/dist/core/impact/impact-analyzer.js +13 -312
- package/dist/core/impact/impact-analyzer.js.map +1 -1
- package/dist/core/impact/index.d.ts +3 -0
- package/dist/core/impact/index.d.ts.map +1 -1
- package/dist/core/impact/index.js +3 -0
- package/dist/core/impact/index.js.map +1 -1
- package/dist/core/impact/path-resolver.d.ts +35 -0
- package/dist/core/impact/path-resolver.d.ts.map +1 -0
- package/dist/core/impact/path-resolver.js +166 -0
- package/dist/core/impact/path-resolver.js.map +1 -0
- package/dist/core/move/file-scanner.d.ts +48 -0
- package/dist/core/move/file-scanner.d.ts.map +1 -0
- package/dist/core/move/file-scanner.js +137 -0
- package/dist/core/move/file-scanner.js.map +1 -0
- package/dist/core/move/import-resolver.d.ts +10 -0
- package/dist/core/move/import-resolver.d.ts.map +1 -1
- package/dist/core/move/import-resolver.js +31 -6
- package/dist/core/move/import-resolver.js.map +1 -1
- package/dist/core/move/index.d.ts +7 -3
- package/dist/core/move/index.d.ts.map +1 -1
- package/dist/core/move/index.js +7 -2
- package/dist/core/move/index.js.map +1 -1
- package/dist/core/move/move-service.d.ts +41 -52
- package/dist/core/move/move-service.d.ts.map +1 -1
- package/dist/core/move/move-service.js +189 -358
- package/dist/core/move/move-service.js.map +1 -1
- package/dist/core/move/path-calculator.d.ts +49 -0
- package/dist/core/move/path-calculator.d.ts.map +1 -0
- package/dist/core/move/path-calculator.js +158 -0
- package/dist/core/move/path-calculator.js.map +1 -0
- package/dist/core/move/path-parser.d.ts +33 -0
- package/dist/core/move/path-parser.d.ts.map +1 -0
- package/dist/core/move/path-parser.js +53 -0
- package/dist/core/move/path-parser.js.map +1 -0
- package/dist/core/move/path-utils.d.ts +70 -0
- package/dist/core/move/path-utils.d.ts.map +1 -0
- package/dist/core/move/path-utils.js +201 -0
- package/dist/core/move/path-utils.js.map +1 -0
- package/dist/core/move/types.d.ts +81 -18
- package/dist/core/move/types.d.ts.map +1 -1
- package/dist/core/move/types.js +73 -5
- package/dist/core/move/types.js.map +1 -1
- package/dist/core/move-member/change-applier.d.ts +23 -0
- package/dist/core/move-member/change-applier.d.ts.map +1 -0
- package/dist/core/move-member/change-applier.js +51 -0
- package/dist/core/move-member/change-applier.js.map +1 -0
- package/dist/core/move-member/file-change-preparer.d.ts +49 -0
- package/dist/core/move-member/file-change-preparer.d.ts.map +1 -0
- package/dist/core/move-member/file-change-preparer.js +340 -0
- package/dist/core/move-member/file-change-preparer.js.map +1 -0
- package/dist/core/move-member/index.d.ts +4 -1
- package/dist/core/move-member/index.d.ts.map +1 -1
- package/dist/core/move-member/index.js +3 -0
- package/dist/core/move-member/index.js.map +1 -1
- package/dist/core/move-member/member-extractor.d.ts +11 -0
- package/dist/core/move-member/member-extractor.d.ts.map +1 -1
- package/dist/core/move-member/member-extractor.js +116 -24
- package/dist/core/move-member/member-extractor.js.map +1 -1
- package/dist/core/move-member/move-member-service.d.ts +9 -78
- package/dist/core/move-member/move-member-service.d.ts.map +1 -1
- package/dist/core/move-member/move-member-service.js +86 -413
- package/dist/core/move-member/move-member-service.js.map +1 -1
- package/dist/core/move-member/reference-updater.d.ts +73 -0
- package/dist/core/move-member/reference-updater.d.ts.map +1 -0
- package/dist/core/move-member/reference-updater.js +286 -0
- package/dist/core/move-member/reference-updater.js.map +1 -0
- package/dist/core/move-member/types.d.ts +17 -2
- package/dist/core/move-member/types.d.ts.map +1 -1
- package/dist/core/move-member/types.js.map +1 -1
- package/dist/core/rename/reference-updater.d.ts +1 -35
- package/dist/core/rename/reference-updater.d.ts.map +1 -1
- package/dist/core/rename/reference-updater.js +8 -251
- package/dist/core/rename/reference-updater.js.map +1 -1
- package/dist/core/rename/rename-engine.d.ts +16 -22
- package/dist/core/rename/rename-engine.d.ts.map +1 -1
- package/dist/core/rename/rename-engine.js +59 -208
- package/dist/core/rename/rename-engine.js.map +1 -1
- package/dist/core/rename/scope-analyzer.d.ts.map +1 -1
- package/dist/core/shared/index.d.ts +1 -1
- package/dist/core/shared/index.d.ts.map +1 -1
- package/dist/core/shared/index.js +1 -1
- package/dist/core/shared/index.js.map +1 -1
- package/dist/core/shared/indexing/index-engine.d.ts +1 -1
- package/dist/core/shared/indexing/index-engine.d.ts.map +1 -1
- package/dist/core/shared/indexing/index-engine.js +1 -1
- package/dist/core/shared/indexing/index-engine.js.map +1 -1
- package/dist/core/shared/symbol-finder/call-site-parser.d.ts +71 -0
- package/dist/core/shared/symbol-finder/call-site-parser.d.ts.map +1 -0
- package/dist/core/shared/symbol-finder/call-site-parser.js +369 -0
- package/dist/core/shared/symbol-finder/call-site-parser.js.map +1 -0
- package/dist/core/shared/symbol-finder/index.d.ts +9 -0
- package/dist/core/shared/symbol-finder/index.d.ts.map +1 -0
- package/dist/core/shared/symbol-finder/index.js +12 -0
- package/dist/core/shared/symbol-finder/index.js.map +1 -0
- package/dist/core/shared/{symbol-finder.d.ts → symbol-finder/symbol-finder.d.ts} +27 -133
- package/dist/core/shared/symbol-finder/symbol-finder.d.ts.map +1 -0
- package/dist/core/shared/symbol-finder/symbol-finder.js +552 -0
- package/dist/core/shared/symbol-finder/symbol-finder.js.map +1 -0
- package/dist/core/shared/symbol-finder/text-matcher.d.ts +45 -0
- package/dist/core/shared/symbol-finder/text-matcher.d.ts.map +1 -0
- package/dist/core/shared/symbol-finder/text-matcher.js +195 -0
- package/dist/core/shared/symbol-finder/text-matcher.js.map +1 -0
- package/dist/core/shared/symbol-finder/types.d.ts +108 -0
- package/dist/core/shared/symbol-finder/types.d.ts.map +1 -0
- package/dist/core/shared/symbol-finder/types.js +71 -0
- package/dist/core/shared/symbol-finder/types.js.map +1 -0
- package/dist/core/snapshot/snapshot-generator.d.ts +16 -4
- package/dist/core/snapshot/snapshot-generator.d.ts.map +1 -1
- package/dist/core/snapshot/snapshot-generator.js +91 -18
- package/dist/core/snapshot/snapshot-generator.js.map +1 -1
- package/dist/infrastructure/cache/cache-manager.d.ts.map +1 -1
- package/dist/infrastructure/cache/cache-manager.js +4 -0
- package/dist/infrastructure/cache/cache-manager.js.map +1 -1
- package/dist/infrastructure/cache/index.d.ts +15 -9
- package/dist/infrastructure/cache/index.d.ts.map +1 -1
- package/dist/infrastructure/cache/index.js +5 -5
- package/dist/infrastructure/cache/index.js.map +1 -1
- package/dist/infrastructure/cache/memory-cache.d.ts.map +1 -1
- package/dist/infrastructure/cache/memory-cache.js.map +1 -1
- package/dist/infrastructure/cache/strategies.d.ts.map +1 -1
- package/dist/infrastructure/cache/strategies.js +2 -1
- package/dist/infrastructure/cache/strategies.js.map +1 -1
- package/dist/infrastructure/cache/types.d.ts +4 -4
- package/dist/infrastructure/cache/types.d.ts.map +1 -1
- package/dist/infrastructure/changeset/change-applicator.d.ts +95 -0
- package/dist/infrastructure/changeset/change-applicator.d.ts.map +1 -0
- package/dist/infrastructure/changeset/change-applicator.js +467 -0
- package/dist/infrastructure/changeset/change-applicator.js.map +1 -0
- package/dist/infrastructure/changeset/changeset-builder.d.ts +107 -0
- package/dist/infrastructure/changeset/changeset-builder.d.ts.map +1 -0
- package/dist/infrastructure/changeset/changeset-builder.js +188 -0
- package/dist/infrastructure/changeset/changeset-builder.js.map +1 -0
- package/dist/infrastructure/changeset/changeset-converter.d.ts +15 -0
- package/dist/infrastructure/changeset/changeset-converter.d.ts.map +1 -0
- package/dist/infrastructure/changeset/changeset-converter.js +370 -0
- package/dist/infrastructure/changeset/changeset-converter.js.map +1 -0
- package/dist/infrastructure/changeset/index.d.ts +10 -0
- package/dist/infrastructure/changeset/index.d.ts.map +1 -0
- package/dist/infrastructure/changeset/index.js +13 -0
- package/dist/infrastructure/changeset/index.js.map +1 -0
- package/dist/infrastructure/changeset/types.d.ts +147 -0
- package/dist/infrastructure/changeset/types.d.ts.map +1 -0
- package/dist/infrastructure/changeset/types.js +46 -0
- package/dist/infrastructure/changeset/types.js.map +1 -0
- package/dist/infrastructure/parser/base.d.ts.map +1 -1
- package/dist/infrastructure/parser/base.js +4 -3
- package/dist/infrastructure/parser/base.js.map +1 -1
- package/dist/infrastructure/parser/interface.d.ts +4 -2
- package/dist/infrastructure/parser/interface.d.ts.map +1 -1
- package/dist/infrastructure/parser/interface.js.map +1 -1
- package/dist/infrastructure/storage/file-system.d.ts.map +1 -1
- package/dist/infrastructure/storage/file-system.js +61 -54
- package/dist/infrastructure/storage/file-system.js.map +1 -1
- package/dist/interfaces/cli/cli.d.ts.map +1 -1
- package/dist/interfaces/cli/cli.js +1 -2
- package/dist/interfaces/cli/cli.js.map +1 -1
- package/dist/interfaces/cli/commands/change-signature.command.d.ts.map +1 -1
- package/dist/interfaces/cli/commands/change-signature.command.js +31 -107
- package/dist/interfaces/cli/commands/change-signature.command.js.map +1 -1
- package/dist/interfaces/cli/commands/deadcode.command.js +51 -46
- package/dist/interfaces/cli/commands/deadcode.command.js.map +1 -1
- package/dist/interfaces/cli/commands/find-references.command.d.ts.map +1 -1
- package/dist/interfaces/cli/commands/find-references.command.js +18 -3
- package/dist/interfaces/cli/commands/find-references.command.js.map +1 -1
- package/dist/interfaces/cli/commands/index.d.ts +0 -1
- package/dist/interfaces/cli/commands/index.d.ts.map +1 -1
- package/dist/interfaces/cli/commands/index.js +0 -1
- package/dist/interfaces/cli/commands/index.js.map +1 -1
- package/dist/interfaces/cli/commands/move.command.d.ts +1 -0
- package/dist/interfaces/cli/commands/move.command.d.ts.map +1 -1
- package/dist/interfaces/cli/commands/move.command.js +170 -65
- package/dist/interfaces/cli/commands/move.command.js.map +1 -1
- package/dist/interfaces/cli/commands/rename.command.d.ts.map +1 -1
- package/dist/interfaces/cli/commands/rename.command.js +133 -59
- package/dist/interfaces/cli/commands/rename.command.js.map +1 -1
- package/dist/interfaces/cli/output-formatter.d.ts +2 -2
- package/dist/interfaces/cli/output-formatter.d.ts.map +1 -1
- package/dist/interfaces/cli/output-formatter.js.map +1 -1
- package/dist/plugins/javascript/declaration-analyzer.d.ts.map +1 -1
- package/dist/plugins/javascript/declaration-analyzer.js +9 -0
- package/dist/plugins/javascript/declaration-analyzer.js.map +1 -1
- package/dist/plugins/javascript/parser.d.ts +11 -10
- package/dist/plugins/javascript/parser.d.ts.map +1 -1
- package/dist/plugins/javascript/parser.js +57 -53
- package/dist/plugins/javascript/parser.js.map +1 -1
- package/dist/plugins/javascript/pattern-analyzer.d.ts.map +1 -1
- package/dist/plugins/javascript/pattern-analyzer.js +4 -3
- package/dist/plugins/javascript/pattern-analyzer.js.map +1 -1
- package/dist/plugins/javascript/reference-finder.d.ts +21 -0
- package/dist/plugins/javascript/reference-finder.d.ts.map +1 -1
- package/dist/plugins/javascript/reference-finder.js +119 -76
- package/dist/plugins/javascript/reference-finder.js.map +1 -1
- package/dist/plugins/javascript/types.d.ts +4 -7
- package/dist/plugins/javascript/types.d.ts.map +1 -1
- package/dist/plugins/javascript/types.js +9 -9
- package/dist/plugins/javascript/types.js.map +1 -1
- package/dist/plugins/shared/parser-helpers.js.map +1 -1
- package/dist/{shared → plugins/shared}/utils/array.d.ts +6 -1
- package/dist/plugins/shared/utils/array.d.ts.map +1 -0
- package/dist/plugins/shared/utils/array.js.map +1 -0
- package/dist/{shared → plugins/shared}/utils/async.d.ts +1 -1
- package/dist/plugins/shared/utils/async.d.ts.map +1 -0
- package/dist/{shared → plugins/shared}/utils/async.js +2 -0
- package/dist/plugins/shared/utils/async.js.map +1 -0
- package/dist/{shared → plugins/shared}/utils/index.d.ts +0 -20
- package/dist/plugins/shared/utils/index.d.ts.map +1 -0
- package/dist/{shared → plugins/shared}/utils/index.js +0 -20
- package/dist/plugins/shared/utils/index.js.map +1 -0
- package/dist/plugins/shared/utils/memory-monitor.d.ts.map +1 -0
- package/dist/plugins/shared/utils/memory-monitor.js.map +1 -0
- package/dist/{shared → plugins/shared}/utils/object.d.ts +6 -6
- package/dist/plugins/shared/utils/object.d.ts.map +1 -0
- package/dist/{shared → plugins/shared}/utils/object.js +21 -3
- package/dist/plugins/shared/utils/object.js.map +1 -0
- package/dist/plugins/shared/utils/path.d.ts.map +1 -0
- package/dist/plugins/shared/utils/path.js.map +1 -0
- package/dist/{shared → plugins/shared}/utils/string.d.ts +1 -1
- package/dist/plugins/shared/utils/string.d.ts.map +1 -0
- package/dist/{shared → plugins/shared}/utils/string.js +7 -4
- package/dist/plugins/shared/utils/string.js.map +1 -0
- package/dist/plugins/typescript/declaration-analyzer.d.ts +4 -4
- package/dist/plugins/typescript/declaration-analyzer.d.ts.map +1 -1
- package/dist/plugins/typescript/declaration-analyzer.js +13 -6
- package/dist/plugins/typescript/declaration-analyzer.js.map +1 -1
- package/dist/plugins/typescript/language-service.d.ts +1 -1
- package/dist/plugins/typescript/language-service.d.ts.map +1 -1
- package/dist/plugins/typescript/parser.d.ts +3 -2
- package/dist/plugins/typescript/parser.d.ts.map +1 -1
- package/dist/plugins/typescript/parser.js +18 -13
- package/dist/plugins/typescript/parser.js.map +1 -1
- package/dist/plugins/typescript/reference-finder.d.ts +3 -0
- package/dist/plugins/typescript/reference-finder.d.ts.map +1 -1
- package/dist/plugins/typescript/reference-finder.js +16 -4
- package/dist/plugins/typescript/reference-finder.js.map +1 -1
- package/dist/plugins/typescript/symbol-extractor.d.ts +4 -0
- package/dist/plugins/typescript/symbol-extractor.d.ts.map +1 -1
- package/dist/plugins/typescript/symbol-extractor.js +24 -6
- package/dist/plugins/typescript/symbol-extractor.js.map +1 -1
- package/dist/plugins/typescript/types.d.ts +0 -3
- package/dist/plugins/typescript/types.d.ts.map +1 -1
- package/dist/plugins/typescript/types.js +6 -9
- package/dist/plugins/typescript/types.js.map +1 -1
- package/dist/shared/errors/index.d.ts +1 -1
- package/dist/shared/errors/index.d.ts.map +1 -1
- package/dist/shared/errors/parser-error.js +5 -5
- package/dist/shared/errors/parser-error.js.map +1 -1
- package/dist/shared/errors/validation-error.d.ts +2 -2
- package/dist/shared/errors/validation-error.d.ts.map +1 -1
- package/dist/shared/errors/validation-error.js.map +1 -1
- package/dist/shared/types/ast.d.ts +4 -4
- package/dist/shared/types/ast.d.ts.map +1 -1
- package/dist/shared/types/ast.js.map +1 -1
- package/dist/shared/types/core.d.ts +1 -1
- package/dist/shared/types/core.d.ts.map +1 -1
- package/dist/shared/types/index.d.ts +1 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/shared/types/index.js +2 -0
- package/dist/shared/types/index.js.map +1 -1
- package/dist/shared/types/line-number.d.ts +22 -0
- package/dist/shared/types/line-number.d.ts.map +1 -0
- package/dist/shared/types/line-number.js +28 -0
- package/dist/shared/types/line-number.js.map +1 -0
- package/package.json +2 -2
- package/dist/application/services/workflow-engine.service.d.ts +0 -76
- package/dist/application/services/workflow-engine.service.d.ts.map +0 -1
- package/dist/application/services/workflow-engine.service.js +0 -405
- package/dist/application/services/workflow-engine.service.js.map +0 -1
- package/dist/application/workflows/analysis-workflow.d.ts +0 -239
- package/dist/application/workflows/analysis-workflow.d.ts.map +0 -1
- package/dist/application/workflows/analysis-workflow.js +0 -395
- package/dist/application/workflows/analysis-workflow.js.map +0 -1
- package/dist/application/workflows/base-workflow.d.ts +0 -108
- package/dist/application/workflows/base-workflow.d.ts.map +0 -1
- package/dist/application/workflows/base-workflow.js +0 -236
- package/dist/application/workflows/base-workflow.js.map +0 -1
- package/dist/application/workflows/index.d.ts +0 -115
- package/dist/application/workflows/index.d.ts.map +0 -1
- package/dist/application/workflows/index.js +0 -218
- package/dist/application/workflows/index.js.map +0 -1
- package/dist/application/workflows/refactor-workflow.d.ts +0 -107
- package/dist/application/workflows/refactor-workflow.d.ts.map +0 -1
- package/dist/application/workflows/refactor-workflow.js +0 -310
- package/dist/application/workflows/refactor-workflow.js.map +0 -1
- package/dist/core/shared/symbol-finder.d.ts.map +0 -1
- package/dist/core/shared/symbol-finder.js +0 -882
- package/dist/core/shared/symbol-finder.js.map +0 -1
- package/dist/interfaces/cli/commands/move-member.command.d.ts +0 -11
- package/dist/interfaces/cli/commands/move-member.command.d.ts.map +0 -1
- package/dist/interfaces/cli/commands/move-member.command.js +0 -211
- package/dist/interfaces/cli/commands/move-member.command.js.map +0 -1
- package/dist/plugins/javascript/code-analyzer.d.ts +0 -68
- package/dist/plugins/javascript/code-analyzer.d.ts.map +0 -1
- package/dist/plugins/javascript/code-analyzer.js +0 -302
- package/dist/plugins/javascript/code-analyzer.js.map +0 -1
- package/dist/plugins/javascript/index.d.ts +0 -12
- package/dist/plugins/javascript/index.d.ts.map +0 -1
- package/dist/plugins/javascript/index.js +0 -16
- package/dist/plugins/javascript/index.js.map +0 -1
- package/dist/plugins/typescript/index.d.ts +0 -14
- package/dist/plugins/typescript/index.d.ts.map +0 -1
- package/dist/plugins/typescript/index.js +0 -13
- package/dist/plugins/typescript/index.js.map +0 -1
- package/dist/shared/utils/array.d.ts.map +0 -1
- package/dist/shared/utils/array.js.map +0 -1
- package/dist/shared/utils/async.d.ts.map +0 -1
- package/dist/shared/utils/async.js.map +0 -1
- package/dist/shared/utils/index.d.ts.map +0 -1
- package/dist/shared/utils/index.js.map +0 -1
- package/dist/shared/utils/memory-monitor.d.ts.map +0 -1
- package/dist/shared/utils/memory-monitor.js.map +0 -1
- package/dist/shared/utils/object.d.ts.map +0 -1
- package/dist/shared/utils/object.js.map +0 -1
- package/dist/shared/utils/path.d.ts.map +0 -1
- package/dist/shared/utils/path.js.map +0 -1
- package/dist/shared/utils/string.d.ts.map +0 -1
- package/dist/shared/utils/string.js.map +0 -1
- /package/dist/{shared → plugins/shared}/utils/array.js +0 -0
- /package/dist/{shared → plugins/shared}/utils/memory-monitor.d.ts +0 -0
- /package/dist/{shared → plugins/shared}/utils/memory-monitor.js +0 -0
- /package/dist/{shared → plugins/shared}/utils/path.d.ts +0 -0
- /package/dist/{shared → plugins/shared}/utils/path.js +0 -0
|
@@ -3,22 +3,27 @@
|
|
|
3
3
|
* 負責刪除未使用的程式碼並清理相關 import
|
|
4
4
|
*/
|
|
5
5
|
import { minimatch } from 'minimatch';
|
|
6
|
-
import {
|
|
6
|
+
import { createChangesetBuilder, ChangesetCommand, TextEditOperationType } from '../../infrastructure/changeset/index.js';
|
|
7
7
|
import { DEFAULT_REMOVAL_OPTIONS } from './types.js';
|
|
8
|
+
import { RangeExpander } from './range-expander.js';
|
|
9
|
+
import { ImportCleaner } from './import-cleaner.js';
|
|
10
|
+
import { FileOperationsHandler } from './file-operations.js';
|
|
8
11
|
/**
|
|
9
12
|
* Dead Code 刪除器
|
|
10
13
|
*/
|
|
11
14
|
export class DeadCodeRemover {
|
|
12
15
|
fileSystem;
|
|
13
|
-
parserRegistry;
|
|
14
16
|
options;
|
|
15
17
|
fileCache = new Map();
|
|
16
|
-
|
|
18
|
+
rangeExpander;
|
|
19
|
+
importCleaner;
|
|
20
|
+
fileOperations;
|
|
17
21
|
constructor(fileSystem, parserRegistry, options) {
|
|
18
22
|
this.fileSystem = fileSystem;
|
|
19
|
-
this.parserRegistry = parserRegistry;
|
|
20
23
|
this.options = { ...DEFAULT_REMOVAL_OPTIONS, ...options };
|
|
21
|
-
this.
|
|
24
|
+
this.rangeExpander = new RangeExpander(parserRegistry);
|
|
25
|
+
this.importCleaner = new ImportCleaner(fileSystem, parserRegistry);
|
|
26
|
+
this.fileOperations = new FileOperationsHandler(fileSystem);
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* 預覽刪除操作
|
|
@@ -36,14 +41,14 @@ export class DeadCodeRemover {
|
|
|
36
41
|
// 3. 分析並產生 import 清理操作
|
|
37
42
|
let importCleanups = [];
|
|
38
43
|
if (this.options.cleanupImports) {
|
|
39
|
-
const importResult = await this.analyzeImportCleanups(removals);
|
|
44
|
+
const importResult = await this.importCleaner.analyzeImportCleanups(removals);
|
|
40
45
|
importCleanups = importResult.cleanups;
|
|
41
46
|
warnings.push(...importResult.warnings);
|
|
42
47
|
}
|
|
43
48
|
// 4. 計算統計
|
|
44
|
-
const summary = this.calculateSummary(removals, importCleanups);
|
|
49
|
+
const summary = this.fileOperations.calculateSummary(removals, importCleanups);
|
|
45
50
|
// 5. 收集影響的檔案
|
|
46
|
-
const affectedFiles = this.collectAffectedFiles(removals, importCleanups);
|
|
51
|
+
const affectedFiles = this.fileOperations.collectAffectedFiles(removals, importCleanups);
|
|
47
52
|
return {
|
|
48
53
|
success: true,
|
|
49
54
|
removals,
|
|
@@ -64,6 +69,51 @@ export class DeadCodeRemover {
|
|
|
64
69
|
};
|
|
65
70
|
}
|
|
66
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* 生成死代碼刪除的 Changeset
|
|
74
|
+
* @param deadCodeItems 待刪除的死代碼項目
|
|
75
|
+
* @returns Changeset 變更集
|
|
76
|
+
*/
|
|
77
|
+
async generateChangeset(deadCodeItems) {
|
|
78
|
+
const builder = createChangesetBuilder()
|
|
79
|
+
.forCommand(ChangesetCommand.Deadcode);
|
|
80
|
+
// 使用現有的 preview 邏輯收集變更
|
|
81
|
+
const preview = await this.preview(deadCodeItems);
|
|
82
|
+
if (!preview.success) {
|
|
83
|
+
return builder
|
|
84
|
+
.addError(preview.errors?.join(', ') ?? 'Preview failed')
|
|
85
|
+
.build();
|
|
86
|
+
}
|
|
87
|
+
// 轉換 removals 為 TextEdit
|
|
88
|
+
for (const removal of preview.removals) {
|
|
89
|
+
builder.addTextChange(removal.filePath, [{
|
|
90
|
+
range: removal.range,
|
|
91
|
+
newText: '',
|
|
92
|
+
description: `Remove ${removal.symbolType}: ${removal.symbolName}`
|
|
93
|
+
}], TextEditOperationType.Delete);
|
|
94
|
+
}
|
|
95
|
+
// 轉換 importCleanups 為 TextEdit
|
|
96
|
+
for (const cleanup of preview.importCleanups) {
|
|
97
|
+
const newText = cleanup.cleanupType === 'partial' && cleanup.newImport
|
|
98
|
+
? cleanup.newImport
|
|
99
|
+
: '';
|
|
100
|
+
builder.addTextChange(cleanup.filePath, [{
|
|
101
|
+
range: cleanup.range,
|
|
102
|
+
newText,
|
|
103
|
+
description: cleanup.cleanupType === 'delete'
|
|
104
|
+
? `Remove import: ${cleanup.unusedSymbols.join(', ')}`
|
|
105
|
+
: `Clean import: ${cleanup.unusedSymbols.join(', ')}`
|
|
106
|
+
}], cleanup.cleanupType === 'delete' ? TextEditOperationType.Delete : TextEditOperationType.Modify);
|
|
107
|
+
}
|
|
108
|
+
// 設定描述
|
|
109
|
+
const { totalRemovals, importsCleanedUp } = preview.summary;
|
|
110
|
+
builder.withDescription(`Removed ${totalRemovals} dead code items and cleaned ${importsCleanedUp} imports`);
|
|
111
|
+
// 加入警告
|
|
112
|
+
for (const warning of preview.warnings ?? []) {
|
|
113
|
+
builder.addWarning(warning);
|
|
114
|
+
}
|
|
115
|
+
return builder.build();
|
|
116
|
+
}
|
|
67
117
|
/**
|
|
68
118
|
* 執行刪除(非 dry-run 時)
|
|
69
119
|
*/
|
|
@@ -79,11 +129,11 @@ export class DeadCodeRemover {
|
|
|
79
129
|
const errors = [];
|
|
80
130
|
const updatedFiles = [];
|
|
81
131
|
// 按檔案分組操作
|
|
82
|
-
const
|
|
132
|
+
const fileOperationsMap = this.fileOperations.groupOperationsByFile(preview);
|
|
83
133
|
// 逐檔案套用變更
|
|
84
|
-
for (const [filePath, operations] of
|
|
134
|
+
for (const [filePath, operations] of fileOperationsMap) {
|
|
85
135
|
try {
|
|
86
|
-
const result = await this.applyFileOperations(filePath, operations);
|
|
136
|
+
const result = await this.fileOperations.applyFileOperations(filePath, operations);
|
|
87
137
|
updatedFiles.push(result);
|
|
88
138
|
}
|
|
89
139
|
catch (error) {
|
|
@@ -131,8 +181,8 @@ export class DeadCodeRemover {
|
|
|
131
181
|
continue;
|
|
132
182
|
}
|
|
133
183
|
// 擴展範圍以包含完整宣告(含 JSDoc 註解)
|
|
134
|
-
const expandedRange =
|
|
135
|
-
const originalCode = this.extractCode(content, expandedRange);
|
|
184
|
+
const expandedRange = this.rangeExpander.expandRangeToFullDeclaration(content, item.location.range, item.type, item.name, item.location.filePath);
|
|
185
|
+
const originalCode = this.fileOperations.extractCode(content, expandedRange);
|
|
136
186
|
operations.push({
|
|
137
187
|
filePath: item.location.filePath,
|
|
138
188
|
range: expandedRange,
|
|
@@ -143,673 +193,6 @@ export class DeadCodeRemover {
|
|
|
143
193
|
}
|
|
144
194
|
return { operations, warnings };
|
|
145
195
|
}
|
|
146
|
-
/**
|
|
147
|
-
* 分析需要清理的 import
|
|
148
|
-
* 支援部分清理:當 import { A, B, C } 中只有部分符號未使用時,保留其他符號
|
|
149
|
-
*/
|
|
150
|
-
async analyzeImportCleanups(removals) {
|
|
151
|
-
const cleanups = [];
|
|
152
|
-
const warnings = [];
|
|
153
|
-
const affectedFiles = new Set(removals.map(r => r.filePath));
|
|
154
|
-
const removedSymbols = new Set(removals.map(r => r.symbolName));
|
|
155
|
-
for (const filePath of affectedFiles) {
|
|
156
|
-
const content = await this.readFile(filePath);
|
|
157
|
-
if (!content) {
|
|
158
|
-
warnings.push(`跳過 import 清理:無法讀取檔案 ${filePath}`);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
// 解析 import 語句(以語句為單位)
|
|
162
|
-
// 優先使用 Parser 的 AST 解析,fallback 到字串解析
|
|
163
|
-
const importStatements = this.parseImportStatements(content, filePath);
|
|
164
|
-
const fileRemovals = removals.filter(r => r.filePath === filePath);
|
|
165
|
-
for (const stmt of importStatements) {
|
|
166
|
-
// 找出此 import 中需要清理的符號
|
|
167
|
-
const unusedSymbols = [];
|
|
168
|
-
const usedSymbols = [];
|
|
169
|
-
for (const symbol of stmt.symbols) {
|
|
170
|
-
// 符號是否在被刪除的列表中,且刪除後不再使用
|
|
171
|
-
if (removedSymbols.has(symbol.name)) {
|
|
172
|
-
const stillUsed = await this.isImportStillUsed(filePath, symbol.name, fileRemovals);
|
|
173
|
-
if (!stillUsed) {
|
|
174
|
-
unusedSymbols.push(symbol.name);
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
usedSymbols.push(symbol.name);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
usedSymbols.push(symbol.name);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// 沒有需要清理的符號,跳過
|
|
185
|
-
if (unusedSymbols.length === 0) {
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
// 判斷清理類型
|
|
189
|
-
if (usedSymbols.length === 0) {
|
|
190
|
-
// 所有符號都未使用,刪除整行
|
|
191
|
-
cleanups.push({
|
|
192
|
-
filePath,
|
|
193
|
-
range: stmt.range,
|
|
194
|
-
originalImport: stmt.statement,
|
|
195
|
-
unusedSymbols,
|
|
196
|
-
cleanupType: 'delete'
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
// 部分符號仍在使用,產生新的 import 語句
|
|
201
|
-
const newImport = this.generatePartialImport(stmt, usedSymbols);
|
|
202
|
-
if (newImport) {
|
|
203
|
-
cleanups.push({
|
|
204
|
-
filePath,
|
|
205
|
-
range: stmt.range,
|
|
206
|
-
originalImport: stmt.statement,
|
|
207
|
-
unusedSymbols,
|
|
208
|
-
cleanupType: 'partial',
|
|
209
|
-
newImport
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return { cleanups, warnings };
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* 產生部分清理後的 import 語句
|
|
219
|
-
* 支援:純 named import、混合 default + named import
|
|
220
|
-
*/
|
|
221
|
-
generatePartialImport(stmt, usedSymbols) {
|
|
222
|
-
// Namespace import 不支援部分清理(整體使用)
|
|
223
|
-
if (stmt.isNamespace) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
// 從原始語句中提取 from 路徑
|
|
227
|
-
const fromMatch = stmt.statement.match(/from\s+(['"])(.+?)\1/);
|
|
228
|
-
if (!fromMatch) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
const fromPath = fromMatch[2];
|
|
232
|
-
const quote = fromMatch[1];
|
|
233
|
-
// 分離 default 和 named symbols
|
|
234
|
-
const defaultSymbol = stmt.symbols.find(s => s.isDefault);
|
|
235
|
-
const namedSymbols = stmt.symbols.filter(s => !s.isDefault);
|
|
236
|
-
// 檢查 default import 是否仍需保留
|
|
237
|
-
const keepDefault = defaultSymbol && usedSymbols.includes(defaultSymbol.name);
|
|
238
|
-
// 過濾出需要保留的 named symbols,並保留別名資訊
|
|
239
|
-
// 同時檢查 name 和 alias,因為 usedSymbols 可能包含別名
|
|
240
|
-
const keptNamedSymbols = namedSymbols
|
|
241
|
-
.filter(s => usedSymbols.includes(s.name) || (s.alias && usedSymbols.includes(s.alias)))
|
|
242
|
-
.map(s => s.alias ? `${s.name} as ${s.alias}` : s.name);
|
|
243
|
-
// 判斷是否需要 type 關鍵字(僅對純 named import)
|
|
244
|
-
const isTypeImport = stmt.statement.match(/import\s+type\s*\{/);
|
|
245
|
-
const typePrefix = isTypeImport ? 'type ' : '';
|
|
246
|
-
// 建構新的 import 語句
|
|
247
|
-
if (keepDefault && keptNamedSymbols.length > 0) {
|
|
248
|
-
// 混合格式:import X, { Y, Z } from '...'
|
|
249
|
-
return `import ${defaultSymbol.name}, { ${keptNamedSymbols.join(', ')} } from ${quote}${fromPath}${quote};`;
|
|
250
|
-
}
|
|
251
|
-
else if (keepDefault) {
|
|
252
|
-
// 只有 default:import X from '...'
|
|
253
|
-
return `import ${defaultSymbol.name} from ${quote}${fromPath}${quote};`;
|
|
254
|
-
}
|
|
255
|
-
else if (keptNamedSymbols.length > 0) {
|
|
256
|
-
// 只有 named:import { Y, Z } from '...'
|
|
257
|
-
return `import ${typePrefix}{ ${keptNamedSymbols.join(', ')} } from ${quote}${fromPath}${quote};`;
|
|
258
|
-
}
|
|
259
|
-
// 沒有任何符號需要保留
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* 解析 import 語句(以語句為單位)
|
|
264
|
-
* 優先使用 Parser 的 AST 解析(精確),fallback 到字串解析(向後相容)
|
|
265
|
-
* @param content 檔案內容
|
|
266
|
-
* @param filePath 檔案路徑(用於取得對應的 Parser)
|
|
267
|
-
*/
|
|
268
|
-
parseImportStatements(content, filePath) {
|
|
269
|
-
// 1. 優先嘗試使用 Parser 的 getImportDeclarations 方法
|
|
270
|
-
const parserResult = this.parseImportStatementsWithParser(content, filePath);
|
|
271
|
-
if (parserResult) {
|
|
272
|
-
return parserResult;
|
|
273
|
-
}
|
|
274
|
-
// 2. Fallback:使用原有的字串解析邏輯
|
|
275
|
-
return this.parseImportStatementsFallback(content);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* 使用 Parser AST 解析 import 語句
|
|
279
|
-
* @returns ImportStatementInfo[] 如果成功,null 如果 Parser 不支援或解析失敗
|
|
280
|
-
*/
|
|
281
|
-
parseImportStatementsWithParser(content, filePath) {
|
|
282
|
-
const parser = this.parserRegistry.getParser(this.getFileExtension(filePath));
|
|
283
|
-
if (!parser?.getImportDeclarations) {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
const declarations = parser.getImportDeclarations(content);
|
|
287
|
-
if (!declarations) {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
// 將 Parser 的 ImportDeclaration 轉換為內部的 ImportStatementInfo
|
|
291
|
-
return declarations.map(decl => this.convertImportDeclaration(decl));
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* 將 Parser 的 ImportDeclaration 轉換為內部的 ImportStatementInfo
|
|
295
|
-
*/
|
|
296
|
-
convertImportDeclaration(decl) {
|
|
297
|
-
const symbols = [];
|
|
298
|
-
// 處理 default import
|
|
299
|
-
if (decl.defaultImport) {
|
|
300
|
-
symbols.push({ name: decl.defaultImport, isDefault: true });
|
|
301
|
-
}
|
|
302
|
-
// 處理 namespace import
|
|
303
|
-
if (decl.namespaceImport) {
|
|
304
|
-
symbols.push({ name: decl.namespaceImport, isNamespace: true });
|
|
305
|
-
}
|
|
306
|
-
// 處理 named imports
|
|
307
|
-
for (const named of decl.namedImports) {
|
|
308
|
-
symbols.push({
|
|
309
|
-
name: named.name,
|
|
310
|
-
alias: named.alias
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
statement: decl.rawStatement,
|
|
315
|
-
range: decl.range,
|
|
316
|
-
symbols,
|
|
317
|
-
hasDefault: !!decl.defaultImport,
|
|
318
|
-
isNamespace: !!decl.namespaceImport
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Fallback:使用字串解析 import 語句
|
|
323
|
-
* 保留原有邏輯以確保向後相容
|
|
324
|
-
*/
|
|
325
|
-
parseImportStatementsFallback(content) {
|
|
326
|
-
const statements = [];
|
|
327
|
-
const lines = content.split('\n');
|
|
328
|
-
// 用於處理多行 import
|
|
329
|
-
let multiLineImport = '';
|
|
330
|
-
let multiLineStartLine = -1;
|
|
331
|
-
let multiLineCount = 0;
|
|
332
|
-
const MAX_MULTILINE_IMPORT = 20; // 安全限制:最多 20 行
|
|
333
|
-
for (let i = 0; i < lines.length; i++) {
|
|
334
|
-
const line = lines[i];
|
|
335
|
-
const lineNumber = i + 1;
|
|
336
|
-
// 處理多行 import
|
|
337
|
-
if (multiLineImport) {
|
|
338
|
-
multiLineImport += '\n' + line;
|
|
339
|
-
multiLineCount++;
|
|
340
|
-
// 檢測結束條件:有 from 和 引號,或超過安全限制
|
|
341
|
-
const cleanLine = line.replace(/\/\/.*/, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
342
|
-
const isComplete = cleanLine.includes('from') && /['"]/.test(cleanLine);
|
|
343
|
-
const isOverLimit = multiLineCount > MAX_MULTILINE_IMPORT;
|
|
344
|
-
if (isComplete || isOverLimit) {
|
|
345
|
-
// 多行 import 結束
|
|
346
|
-
const stmt = this.parseImportStatementLine(multiLineImport, multiLineStartLine, lineNumber, lines);
|
|
347
|
-
if (stmt) {
|
|
348
|
-
statements.push(stmt);
|
|
349
|
-
}
|
|
350
|
-
multiLineImport = '';
|
|
351
|
-
multiLineStartLine = -1;
|
|
352
|
-
multiLineCount = 0;
|
|
353
|
-
}
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
// 檢查是否為多行 import 開始(有 { 但沒有 } 或沒有 from)
|
|
357
|
-
if (line.match(/^\s*import\s+(?:type\s*)?\{/) && !line.includes('}')) {
|
|
358
|
-
multiLineImport = line;
|
|
359
|
-
multiLineStartLine = lineNumber;
|
|
360
|
-
multiLineCount = 1;
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
// 單行處理
|
|
364
|
-
const stmt = this.parseImportStatementLine(line, lineNumber, lineNumber, lines);
|
|
365
|
-
if (stmt) {
|
|
366
|
-
statements.push(stmt);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return statements;
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* 解析單行或合併後的 import 語句
|
|
373
|
-
*/
|
|
374
|
-
parseImportStatementLine(line, startLine, endLine, lines) {
|
|
375
|
-
const trimmedLine = line.replace(/\s+/g, ' ').trim();
|
|
376
|
-
// 不是 import 語句
|
|
377
|
-
if (!trimmedLine.startsWith('import ')) {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
// Side-effect import: import '...' (沒有符號)
|
|
381
|
-
if (trimmedLine.match(/^import\s+['"][^'"]+['"]/)) {
|
|
382
|
-
return null;
|
|
383
|
-
}
|
|
384
|
-
const range = {
|
|
385
|
-
start: { line: startLine, column: 1, offset: undefined },
|
|
386
|
-
end: { line: endLine, column: (lines[endLine - 1] || '').length + 1, offset: undefined }
|
|
387
|
-
};
|
|
388
|
-
const symbols = [];
|
|
389
|
-
let hasDefault = false;
|
|
390
|
-
let isNamespace = false;
|
|
391
|
-
// 1. Namespace import: import * as X from '...'
|
|
392
|
-
const namespaceMatch = trimmedLine.match(/import\s+\*\s+as\s+(\w+)\s+from/);
|
|
393
|
-
if (namespaceMatch) {
|
|
394
|
-
symbols.push({ name: namespaceMatch[1], isNamespace: true });
|
|
395
|
-
isNamespace = true;
|
|
396
|
-
return { statement: trimmedLine, range, symbols, hasDefault, isNamespace };
|
|
397
|
-
}
|
|
398
|
-
// 2. Default import with named: import X, { Y, Z } from '...'
|
|
399
|
-
const defaultWithNamedMatch = trimmedLine.match(/import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from/);
|
|
400
|
-
if (defaultWithNamedMatch) {
|
|
401
|
-
hasDefault = true;
|
|
402
|
-
symbols.push({ name: defaultWithNamedMatch[1], isDefault: true });
|
|
403
|
-
this.parseNamedSymbols(defaultWithNamedMatch[2], symbols);
|
|
404
|
-
return { statement: trimmedLine, range, symbols, hasDefault, isNamespace };
|
|
405
|
-
}
|
|
406
|
-
// 3. Default import only: import X from '...'
|
|
407
|
-
const defaultMatch = trimmedLine.match(/import\s+(\w+)\s+from\s+['"]/);
|
|
408
|
-
if (defaultMatch && !trimmedLine.includes('{')) {
|
|
409
|
-
hasDefault = true;
|
|
410
|
-
symbols.push({ name: defaultMatch[1], isDefault: true });
|
|
411
|
-
return { statement: trimmedLine, range, symbols, hasDefault, isNamespace };
|
|
412
|
-
}
|
|
413
|
-
// 4. Named import: import { X, Y } from '...' or import type { X } from '...'
|
|
414
|
-
const namedImportMatch = trimmedLine.match(/import\s+(?:type\s*)?\{([^}]+)\}\s*from/);
|
|
415
|
-
if (namedImportMatch) {
|
|
416
|
-
this.parseNamedSymbols(namedImportMatch[1], symbols);
|
|
417
|
-
if (symbols.length > 0) {
|
|
418
|
-
return { statement: trimmedLine, range, symbols, hasDefault, isNamespace };
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
/**
|
|
424
|
-
* 解析 named import 中的符號
|
|
425
|
-
*/
|
|
426
|
-
parseNamedSymbols(symbolsStr, symbols) {
|
|
427
|
-
const parts = symbolsStr.split(',').map(s => s.trim());
|
|
428
|
-
for (const part of parts) {
|
|
429
|
-
// 跳過空字串和 type-only imports
|
|
430
|
-
if (!part || part.startsWith('type ')) {
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
// 處理 as 別名: X as Y
|
|
434
|
-
const asMatch = part.match(/^(\w+)\s+as\s+(\w+)$/);
|
|
435
|
-
if (asMatch) {
|
|
436
|
-
symbols.push({ name: asMatch[1], alias: asMatch[2] });
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
const cleanSymbol = part.trim();
|
|
440
|
-
if (cleanSymbol) {
|
|
441
|
-
symbols.push({ name: cleanSymbol });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* 檢查 import 是否仍被使用
|
|
448
|
-
* 使用 SymbolFinder.findReferencesInFile 進行語義分析
|
|
449
|
-
*/
|
|
450
|
-
async isImportStillUsed(filePath, symbolName, removalsInFile) {
|
|
451
|
-
// 使用 SymbolFinder 查找該檔案中的所有引用
|
|
452
|
-
const references = await this.symbolFinder.findReferencesInFile(filePath, symbolName);
|
|
453
|
-
// 過濾掉 import 類型的引用(import 語句本身)
|
|
454
|
-
const usageRefs = references.filter(ref => ref.type === SymbolReferenceType.Usage);
|
|
455
|
-
// 過濾掉被刪除程式碼區塊內的引用
|
|
456
|
-
const remainingRefs = usageRefs.filter(ref => {
|
|
457
|
-
const refLine = ref.location.range.start.line;
|
|
458
|
-
// 檢查引用是否在任一刪除範圍內
|
|
459
|
-
for (const removal of removalsInFile) {
|
|
460
|
-
if (refLine >= removal.range.start.line && refLine <= removal.range.end.line) {
|
|
461
|
-
return false; // 在刪除範圍內,過濾掉
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return true;
|
|
465
|
-
});
|
|
466
|
-
// 如果還有剩餘的使用引用,表示 import 仍需要
|
|
467
|
-
return remainingRefs.length > 0;
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* 擴展範圍至完整宣告(包含前導註解和空行)
|
|
471
|
-
* 優先使用 Parser 的 getFullDeclarationRange 方法(AST 精確解析)
|
|
472
|
-
* 若 Parser 不支援或回傳 null,fallback 到字串匹配邏輯
|
|
473
|
-
*/
|
|
474
|
-
async expandRangeToFullDeclaration(content, range, symbolType, symbolName, filePath) {
|
|
475
|
-
// 1. 優先嘗試使用 Parser 的 getFullDeclarationRange 方法
|
|
476
|
-
const parser = this.parserRegistry.getParser(this.getFileExtension(filePath));
|
|
477
|
-
if (parser?.getFullDeclarationRange) {
|
|
478
|
-
const parserRange = parser.getFullDeclarationRange(content, symbolName, symbolType, range.start.line);
|
|
479
|
-
if (parserRange) {
|
|
480
|
-
// Parser 成功解析,處理後續空行
|
|
481
|
-
const lines = content.split('\n');
|
|
482
|
-
let endLine = parserRange.end.line - 1; // 轉為 0-based
|
|
483
|
-
if (endLine < lines.length - 1 && lines[endLine + 1]?.trim() === '') {
|
|
484
|
-
endLine++;
|
|
485
|
-
}
|
|
486
|
-
return {
|
|
487
|
-
start: parserRange.start,
|
|
488
|
-
end: {
|
|
489
|
-
line: endLine + 1,
|
|
490
|
-
column: (lines[endLine]?.length ?? 0) + 1,
|
|
491
|
-
offset: parserRange.end.offset
|
|
492
|
-
}
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
// 2. Fallback:使用原有的字串匹配邏輯
|
|
497
|
-
return this.expandRangeByStringMatching(content, range, symbolType);
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* 使用字串匹配邏輯擴展範圍(fallback 方法)
|
|
501
|
-
* 使用清理後的內容進行括號匹配,避免字串/註解中的括號干擾
|
|
502
|
-
*/
|
|
503
|
-
expandRangeByStringMatching(content, range, symbolType) {
|
|
504
|
-
const lines = content.split('\n');
|
|
505
|
-
let startLine = range.start.line - 1; // 轉為 0-based
|
|
506
|
-
// 向上擴展:包含 JSDoc 註解和裝飾器
|
|
507
|
-
while (startLine > 0) {
|
|
508
|
-
const prevLine = lines[startLine - 1].trim();
|
|
509
|
-
// Bug #32 修復:如果遇到 JSDoc 結尾 */,繼續向上找到開始 /**
|
|
510
|
-
if (prevLine.endsWith('*/')) {
|
|
511
|
-
startLine--;
|
|
512
|
-
// 繼續向上找到 JSDoc 開始 /**(使用 >= 0 確保第 0 行也能檢查)
|
|
513
|
-
while (startLine > 0) {
|
|
514
|
-
const jsdocLine = lines[startLine - 1].trim();
|
|
515
|
-
if (jsdocLine.startsWith('/**')) {
|
|
516
|
-
startLine--;
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
startLine--;
|
|
520
|
-
}
|
|
521
|
-
// 額外檢查第 0 行是否為 JSDoc 開始
|
|
522
|
-
if (startLine === 0 && lines[0].trim().startsWith('/**')) {
|
|
523
|
-
// 已經到達第 0 行,不需要再減
|
|
524
|
-
}
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
// 處理單行註解、裝飾器、空行、JSDoc 中間行
|
|
528
|
-
if (prevLine.startsWith('*') ||
|
|
529
|
-
prevLine.startsWith('//') ||
|
|
530
|
-
prevLine.startsWith('@') ||
|
|
531
|
-
prevLine === '') {
|
|
532
|
-
startLine--;
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
break;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
// 向下擴展:確保包含完整的結尾
|
|
539
|
-
let endLine = range.end.line - 1;
|
|
540
|
-
// 對於 class/function,需要找到對應的結尾括號
|
|
541
|
-
if (symbolType === 'class' || symbolType === 'function') {
|
|
542
|
-
let braceCount = 0;
|
|
543
|
-
let foundOpenBrace = false;
|
|
544
|
-
for (let i = range.start.line - 1; i < lines.length; i++) {
|
|
545
|
-
// 清理該行的註解和字串,避免括號誤判
|
|
546
|
-
const cleanLine = this.removeCommentsAndStringsFromLine(lines[i]);
|
|
547
|
-
for (const char of cleanLine) {
|
|
548
|
-
if (char === '{') {
|
|
549
|
-
braceCount++;
|
|
550
|
-
foundOpenBrace = true;
|
|
551
|
-
}
|
|
552
|
-
if (char === '}') {
|
|
553
|
-
braceCount--;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
if (foundOpenBrace && braceCount === 0) {
|
|
557
|
-
endLine = i;
|
|
558
|
-
break;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// 對於 variable(可能是 arrow function),只有當包含 { 時才做括號匹配
|
|
563
|
-
if (symbolType === 'variable') {
|
|
564
|
-
const startLineContent = lines[range.start.line - 1] || '';
|
|
565
|
-
// 檢查是否包含 arrow function 的 block body
|
|
566
|
-
if (startLineContent.includes('=>') && startLineContent.includes('{')) {
|
|
567
|
-
let braceCount = 0;
|
|
568
|
-
let foundOpenBrace = false;
|
|
569
|
-
for (let i = range.start.line - 1; i < lines.length; i++) {
|
|
570
|
-
const cleanLine = this.removeCommentsAndStringsFromLine(lines[i]);
|
|
571
|
-
for (const char of cleanLine) {
|
|
572
|
-
if (char === '{') {
|
|
573
|
-
braceCount++;
|
|
574
|
-
foundOpenBrace = true;
|
|
575
|
-
}
|
|
576
|
-
if (char === '}') {
|
|
577
|
-
braceCount--;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
if (foundOpenBrace && braceCount === 0) {
|
|
581
|
-
endLine = i;
|
|
582
|
-
break;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
// 包含後續空行(最多一行)
|
|
588
|
-
if (endLine < lines.length - 1 && lines[endLine + 1].trim() === '') {
|
|
589
|
-
endLine++;
|
|
590
|
-
}
|
|
591
|
-
return {
|
|
592
|
-
start: { line: startLine + 1, column: 1, offset: undefined },
|
|
593
|
-
end: { line: endLine + 1, column: lines[endLine].length + 1, offset: undefined }
|
|
594
|
-
};
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* 從檔案路徑取得副檔名
|
|
598
|
-
*/
|
|
599
|
-
getFileExtension(filePath) {
|
|
600
|
-
const lastDot = filePath.lastIndexOf('.');
|
|
601
|
-
return lastDot === -1 ? '' : filePath.substring(lastDot);
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* 移除單行中的註解和字串(用於括號匹配)
|
|
605
|
-
*/
|
|
606
|
-
removeCommentsAndStringsFromLine(line) {
|
|
607
|
-
let result = line;
|
|
608
|
-
// 移除單行註解 // ...
|
|
609
|
-
const commentIndex = result.indexOf('//');
|
|
610
|
-
if (commentIndex !== -1) {
|
|
611
|
-
// 確保 // 不在字串中
|
|
612
|
-
const beforeComment = result.substring(0, commentIndex);
|
|
613
|
-
const quoteCount = (beforeComment.match(/['"]/g) || []).length;
|
|
614
|
-
if (quoteCount % 2 === 0) {
|
|
615
|
-
result = beforeComment;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
// 移除字串(簡化處理)
|
|
619
|
-
result = result.replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
|
620
|
-
result = result.replace(/'(?:[^'\\]|\\.)*'/g, '\'\'');
|
|
621
|
-
result = result.replace(/`(?:[^`\\]|\\.)*`/g, '""');
|
|
622
|
-
return result;
|
|
623
|
-
}
|
|
624
|
-
/**
|
|
625
|
-
* 提取程式碼
|
|
626
|
-
*/
|
|
627
|
-
extractCode(content, range) {
|
|
628
|
-
const lines = content.split('\n');
|
|
629
|
-
// 邊界檢查:確保索引在有效範圍內
|
|
630
|
-
const startLine = Math.max(0, Math.min(range.start.line - 1, lines.length - 1));
|
|
631
|
-
const endLine = Math.max(0, Math.min(range.end.line - 1, lines.length - 1));
|
|
632
|
-
if (startLine === endLine) {
|
|
633
|
-
const line = lines[startLine] || '';
|
|
634
|
-
return line.substring(range.start.column - 1, range.end.column - 1);
|
|
635
|
-
}
|
|
636
|
-
const result = [];
|
|
637
|
-
for (let i = startLine; i <= endLine; i++) {
|
|
638
|
-
const line = lines[i] || '';
|
|
639
|
-
if (i === startLine) {
|
|
640
|
-
result.push(line.substring(range.start.column - 1));
|
|
641
|
-
}
|
|
642
|
-
else if (i === endLine) {
|
|
643
|
-
result.push(line.substring(0, range.end.column - 1));
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
result.push(line);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
return result.join('\n');
|
|
650
|
-
}
|
|
651
|
-
/**
|
|
652
|
-
* 按檔案分組操作(去重相同 range)
|
|
653
|
-
*/
|
|
654
|
-
groupOperationsByFile(preview) {
|
|
655
|
-
const fileOperations = new Map();
|
|
656
|
-
// 用於檢查 range 是否重複
|
|
657
|
-
const rangeKey = (r) => `${r.start.line}:${r.start.column}-${r.end.line}:${r.end.column}`;
|
|
658
|
-
const seenRanges = new Map();
|
|
659
|
-
const addOperation = (filePath, op) => {
|
|
660
|
-
const existing = fileOperations.get(filePath) || [];
|
|
661
|
-
const seen = seenRanges.get(filePath) || new Set();
|
|
662
|
-
const key = rangeKey(op.range);
|
|
663
|
-
// 去重:相同 range 只加入一次
|
|
664
|
-
if (!seen.has(key)) {
|
|
665
|
-
existing.push(op);
|
|
666
|
-
seen.add(key);
|
|
667
|
-
fileOperations.set(filePath, existing);
|
|
668
|
-
seenRanges.set(filePath, seen);
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
// 加入刪除操作
|
|
672
|
-
for (const removal of preview.removals) {
|
|
673
|
-
addOperation(removal.filePath, { range: removal.range, type: 'removal' });
|
|
674
|
-
}
|
|
675
|
-
// 加入 import 清理操作
|
|
676
|
-
for (const cleanup of preview.importCleanups) {
|
|
677
|
-
if (cleanup.cleanupType === 'partial' && cleanup.newImport) {
|
|
678
|
-
addOperation(cleanup.filePath, {
|
|
679
|
-
range: cleanup.range,
|
|
680
|
-
type: 'import-partial',
|
|
681
|
-
newContent: cleanup.newImport
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
else {
|
|
685
|
-
addOperation(cleanup.filePath, { range: cleanup.range, type: 'import-delete' });
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
return fileOperations;
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* 套用檔案操作
|
|
692
|
-
*/
|
|
693
|
-
async applyFileOperations(filePath, operations) {
|
|
694
|
-
const originalContent = await this.readFile(filePath);
|
|
695
|
-
if (!originalContent) {
|
|
696
|
-
throw new Error(`無法讀取檔案: ${filePath}`);
|
|
697
|
-
}
|
|
698
|
-
// 按位置從後往前排序(避免位置偏移)
|
|
699
|
-
// 第三層:type 排序確保穩定性(import 清理優先於符號刪除)
|
|
700
|
-
const typeOrder = {
|
|
701
|
-
'import-partial': 0,
|
|
702
|
-
'import-delete': 1,
|
|
703
|
-
'removal': 2
|
|
704
|
-
};
|
|
705
|
-
const sortedOps = [...operations].sort((a, b) => {
|
|
706
|
-
if (a.range.start.line !== b.range.start.line) {
|
|
707
|
-
return b.range.start.line - a.range.start.line;
|
|
708
|
-
}
|
|
709
|
-
if (a.range.start.column !== b.range.start.column) {
|
|
710
|
-
return b.range.start.column - a.range.start.column;
|
|
711
|
-
}
|
|
712
|
-
return typeOrder[a.type] - typeOrder[b.type];
|
|
713
|
-
});
|
|
714
|
-
let lines = originalContent.split('\n');
|
|
715
|
-
let removedSymbols = 0;
|
|
716
|
-
let cleanedImports = 0;
|
|
717
|
-
for (const op of sortedOps) {
|
|
718
|
-
// 邊界檢查:確保索引在有效範圍內
|
|
719
|
-
const startLine = Math.max(0, Math.min(op.range.start.line - 1, lines.length - 1));
|
|
720
|
-
const endLine = Math.max(startLine, Math.min(op.range.end.line - 1, lines.length - 1));
|
|
721
|
-
const deleteCount = endLine - startLine + 1;
|
|
722
|
-
if (op.type === 'import-partial' && op.newContent) {
|
|
723
|
-
// 部分清理:替換而非刪除
|
|
724
|
-
if (startLine < lines.length && deleteCount > 0) {
|
|
725
|
-
// 保留原始縮排
|
|
726
|
-
const originalIndent = lines[startLine].match(/^(\s*)/)?.[1] || '';
|
|
727
|
-
lines.splice(startLine, deleteCount, originalIndent + op.newContent);
|
|
728
|
-
}
|
|
729
|
-
cleanedImports++;
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
// 完整刪除
|
|
733
|
-
if (startLine < lines.length && deleteCount > 0) {
|
|
734
|
-
lines.splice(startLine, deleteCount);
|
|
735
|
-
}
|
|
736
|
-
if (op.type === 'removal') {
|
|
737
|
-
removedSymbols++;
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
cleanedImports++;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
// 清理連續空行(最多保留一行)
|
|
745
|
-
lines = this.cleanupEmptyLines(lines);
|
|
746
|
-
const newContent = lines.join('\n');
|
|
747
|
-
// 寫入檔案
|
|
748
|
-
await this.writeFile(filePath, newContent);
|
|
749
|
-
return {
|
|
750
|
-
filePath,
|
|
751
|
-
removedSymbols,
|
|
752
|
-
cleanedImports
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* 清理連續空行
|
|
757
|
-
*/
|
|
758
|
-
cleanupEmptyLines(lines) {
|
|
759
|
-
const result = [];
|
|
760
|
-
let prevEmpty = false;
|
|
761
|
-
for (const line of lines) {
|
|
762
|
-
const isEmpty = line.trim() === '';
|
|
763
|
-
if (isEmpty && prevEmpty) {
|
|
764
|
-
// 跳過連續的空行
|
|
765
|
-
continue;
|
|
766
|
-
}
|
|
767
|
-
result.push(line);
|
|
768
|
-
prevEmpty = isEmpty;
|
|
769
|
-
}
|
|
770
|
-
return result;
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* 計算統計摘要
|
|
774
|
-
*/
|
|
775
|
-
calculateSummary(removals, importCleanups) {
|
|
776
|
-
const byType = {};
|
|
777
|
-
for (const removal of removals) {
|
|
778
|
-
byType[removal.symbolType] = (byType[removal.symbolType] || 0) + 1;
|
|
779
|
-
}
|
|
780
|
-
const filesAffected = new Set([
|
|
781
|
-
...removals.map(r => r.filePath),
|
|
782
|
-
...importCleanups.map(c => c.filePath)
|
|
783
|
-
]).size;
|
|
784
|
-
// 計算刪除的行數
|
|
785
|
-
let linesRemoved = 0;
|
|
786
|
-
for (const removal of removals) {
|
|
787
|
-
linesRemoved += removal.range.end.line - removal.range.start.line + 1;
|
|
788
|
-
}
|
|
789
|
-
for (const cleanup of importCleanups) {
|
|
790
|
-
linesRemoved += cleanup.range.end.line - cleanup.range.start.line + 1;
|
|
791
|
-
}
|
|
792
|
-
return {
|
|
793
|
-
totalRemovals: removals.length,
|
|
794
|
-
byType,
|
|
795
|
-
filesAffected,
|
|
796
|
-
linesRemoved,
|
|
797
|
-
importsCleanedUp: importCleanups.length
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* 收集影響的檔案
|
|
802
|
-
*/
|
|
803
|
-
collectAffectedFiles(removals, importCleanups) {
|
|
804
|
-
const files = new Set();
|
|
805
|
-
for (const removal of removals) {
|
|
806
|
-
files.add(removal.filePath);
|
|
807
|
-
}
|
|
808
|
-
for (const cleanup of importCleanups) {
|
|
809
|
-
files.add(cleanup.filePath);
|
|
810
|
-
}
|
|
811
|
-
return Array.from(files);
|
|
812
|
-
}
|
|
813
196
|
/**
|
|
814
197
|
* 建立空的預覽結果
|
|
815
198
|
*/
|
|
@@ -837,6 +220,7 @@ export class DeadCodeRemover {
|
|
|
837
220
|
}
|
|
838
221
|
/**
|
|
839
222
|
* 讀取檔案
|
|
223
|
+
* 檔案不存在時快取 null,避免重複讀取
|
|
840
224
|
*/
|
|
841
225
|
async readFile(filePath) {
|
|
842
226
|
if (this.fileCache.has(filePath)) {
|
|
@@ -848,19 +232,14 @@ export class DeadCodeRemover {
|
|
|
848
232
|
this.fileCache.set(filePath, contentStr);
|
|
849
233
|
return contentStr;
|
|
850
234
|
}
|
|
851
|
-
catch {
|
|
852
|
-
//
|
|
853
|
-
|
|
235
|
+
catch (error) {
|
|
236
|
+
// 檔案不存在時快取 null,避免重複讀取
|
|
237
|
+
if (error instanceof Error && error.message.includes('ENOENT')) {
|
|
238
|
+
this.fileCache.set(filePath, null);
|
|
239
|
+
}
|
|
854
240
|
return null;
|
|
855
241
|
}
|
|
856
242
|
}
|
|
857
|
-
/**
|
|
858
|
-
* 寫入檔案
|
|
859
|
-
*/
|
|
860
|
-
async writeFile(filePath, content) {
|
|
861
|
-
await this.fileSystem.writeFile(filePath, content);
|
|
862
|
-
this.fileCache.set(filePath, content);
|
|
863
|
-
}
|
|
864
243
|
/**
|
|
865
244
|
* 檢查檔案路徑是否匹配排除模式
|
|
866
245
|
* 支援 glob 模式(如 *.test.ts、**\/__tests__/**)和簡單字串匹配
|
|
@@ -878,6 +257,8 @@ export class DeadCodeRemover {
|
|
|
878
257
|
*/
|
|
879
258
|
clearCache() {
|
|
880
259
|
this.fileCache.clear();
|
|
260
|
+
this.importCleaner.clearCache();
|
|
261
|
+
this.fileOperations.clearCache();
|
|
881
262
|
}
|
|
882
263
|
}
|
|
883
264
|
/**
|