codeboarding 0.15.0__tar.gz → 0.15.1__tar.gz
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.
- {codeboarding-0.15.0 → codeboarding-0.15.1}/PKG-INFO +1 -1
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/PKG-INFO +1 -1
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/SOURCES.txt +3 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/pyproject.toml +5 -1
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/__init__.py +11 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/analysis_cache.py +2 -1
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/typescript_adapter.py +31 -1
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/language_adapter.py +8 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/lsp_client.py +13 -3
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/exceptions.py +4 -0
- codeboarding-0.15.1/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/index.js +132 -0
- codeboarding-0.15.1/static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/package.json +6 -0
- codeboarding-0.15.1/static_analyzer/typescript_workspace.py +81 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/LICENSE +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/PYPI.md +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/README.md +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/agent_responses.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/change_status.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/component_ownership.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/constants.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/content_hash.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/file_index_models.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/incremental_results.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/llm_config.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/llm_errors.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/llm_renderers/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/llm_renderers/call_graph.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/llm_renderers/scope.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/model_capabilities.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/planner_agent.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/prompts/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/prompts/profiles.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/prompts/shared.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/relation_edges.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/retry.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/scope_analysis_agent.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/scope_ids.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/tools/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/tools/base.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/tools/get_method_calls.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/tools/read_file.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/agents/tree_planner_agent.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/dependency_links.txt +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/entry_points.txt +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/requires.txt +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding.egg-info/top_level.txt +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/bootstrap.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/commands/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/commands/full_analysis.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/commands/incremental_analysis.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/commands/partial_analysis.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/github_action.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/main.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/render.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/view_instructions.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/analysis.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/fingerprint_diff.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/orchestration.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/sources/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/sources/local.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_workflows/sources/remote.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/analysis_json.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/cluster_relations.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/diagram_generator.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/exceptions.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/file_coverage.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/file_index.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/incremental_changes.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/incremental_update.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/io_utils.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/reference_resolver.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/run_context.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/run_mode.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/scope_assembly.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/scope_plan.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/diagram_analysis/tree_shape.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/checks/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/checks/circular_deps.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/checks/function_size.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/checks/unused_code_diagnostics.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/config.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/main.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/models.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/plugins/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/plugins/plugin_loader.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/plugins/protocols.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/plugins/registry.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/health/runner.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/clustering_ids.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/constants.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/install.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/logging_config.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/callbacks.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/context.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/mixin.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/paths.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/stats.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/telemetry/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/telemetry/device_id.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/telemetry/events.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/telemetry/schemas.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/telemetry/service.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/monitoring/writers.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/change_detector.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/errors.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/git_ops.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/ignore.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/repo_utils/path_utils.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/grammars.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/installers.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/java.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/manifest.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/paths.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/tool_registry/registry.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/user_config.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/utils.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/infra/vscode_constants.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/html.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/html_template.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/markdown.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/mdx.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/rendering.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/output_generators/sphinx.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/setup.cfg +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/analysis_result.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/cfg/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/cfg/call_graph.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/cfg/edge.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/cfg/location_key.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/exceptions.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/models.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/draft.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/frontier.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/inventory.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/replay.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/spec.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/names/tokens.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/clustering/service.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/config.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/csharp_config_scanner.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/dotnet_sdk.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/dotnet_solution.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/csharp_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/go_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/java_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/kotlin_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/php_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/python_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/rust_adapter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/call_graph_builder.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/edge_build_context.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/edge_builder.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/hierarchy_builder.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/lsp_constants.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/lsp_recycler.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/models.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/process_memory.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/progress.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/protocols.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/result_converter.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/source_inspector.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/symbol_table.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/utils.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/external_calls.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/graph_definitions.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/incremental_orchestrator.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/internal_references.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/java_config_scanner.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/java_utils.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/language_results.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/lsp_client/__init__.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/lsp_client/diagnostics.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/node.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/programming_language.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/scanner.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/typescript_config_scanner.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/workspace_dependencies.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/tests/test_persisted_analysis_artifacts.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/tests/test_pyproject_packages.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/tests/test_view_instructions.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/tests/test_windows_compatibility.py +0 -0
- {codeboarding-0.15.0 → codeboarding-0.15.1}/tests/test_windows_encoding.py +0 -0
|
@@ -140,6 +140,7 @@ static_analyzer/node.py
|
|
|
140
140
|
static_analyzer/programming_language.py
|
|
141
141
|
static_analyzer/scanner.py
|
|
142
142
|
static_analyzer/typescript_config_scanner.py
|
|
143
|
+
static_analyzer/typescript_workspace.py
|
|
143
144
|
static_analyzer/workspace_dependencies.py
|
|
144
145
|
static_analyzer/cfg/__init__.py
|
|
145
146
|
static_analyzer/cfg/call_graph.py
|
|
@@ -184,6 +185,8 @@ static_analyzer/engine/adapters/rust_adapter.py
|
|
|
184
185
|
static_analyzer/engine/adapters/typescript_adapter.py
|
|
185
186
|
static_analyzer/lsp_client/__init__.py
|
|
186
187
|
static_analyzer/lsp_client/diagnostics.py
|
|
188
|
+
static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/index.js
|
|
189
|
+
static_analyzer/typescript_plugin/node_modules/codeboarding-workspace-sources/package.json
|
|
187
190
|
tests/test_persisted_analysis_artifacts.py
|
|
188
191
|
tests/test_pyproject_packages.py
|
|
189
192
|
tests/test_view_instructions.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codeboarding"
|
|
7
|
-
version = "0.15.
|
|
7
|
+
version = "0.15.1"
|
|
8
8
|
description = "Interactive Diagrams for Code"
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -106,6 +106,10 @@ packages = [
|
|
|
106
106
|
]
|
|
107
107
|
include-package-data = true
|
|
108
108
|
|
|
109
|
+
[tool.setuptools.package-data]
|
|
110
|
+
# tsserver loads a plugin only from <location>/node_modules/<name>.
|
|
111
|
+
static_analyzer = ["typescript_plugin/node_modules/codeboarding-workspace-sources/*"]
|
|
112
|
+
|
|
109
113
|
[project.scripts]
|
|
110
114
|
codeboarding = "codeboarding_cli.main:main"
|
|
111
115
|
codeboarding-setup = "infra.install:main"
|
|
@@ -363,6 +363,10 @@ class StaticAnalyzer:
|
|
|
363
363
|
continue
|
|
364
364
|
try:
|
|
365
365
|
started.append((engine_config, self._spawn_engine_client(engine_config)))
|
|
366
|
+
except StaticAnalysisFatalError:
|
|
367
|
+
for _, client in started: # never handed to ``stop_clients``
|
|
368
|
+
client.shutdown()
|
|
369
|
+
raise
|
|
366
370
|
except Exception as exc:
|
|
367
371
|
logger.exception(
|
|
368
372
|
f"Failed to start engine LSP client for {adapter.language}; "
|
|
@@ -500,6 +504,7 @@ class StaticAnalyzer:
|
|
|
500
504
|
extra_client_capabilities=getattr(adapter, "extra_client_capabilities", {}) or {},
|
|
501
505
|
error_verdict=adapter.error_verdict,
|
|
502
506
|
workspace_folders=workspace_folders,
|
|
507
|
+
commands=adapter.get_lsp_commands(project_path),
|
|
503
508
|
)
|
|
504
509
|
try:
|
|
505
510
|
engine_client.start()
|
|
@@ -918,6 +923,12 @@ class StaticAnalyzer:
|
|
|
918
923
|
adapter, project_path = engine_config.adapter, engine_config.project_path
|
|
919
924
|
language = adapter.results_language
|
|
920
925
|
changed_files = self._changed_files_for_language(project_path, cached_sha, adapter.language)
|
|
926
|
+
if changed_files is not None and adapter.rebuilds_for(changed_files, project_path):
|
|
927
|
+
logger.info(f"warmstart {adapter.language}: a change alters how imports resolve; rebuilding")
|
|
928
|
+
changed_files = None
|
|
929
|
+
if resident_client is not None:
|
|
930
|
+
# Why: a server resolves imports as the project was when it started; a stopped one is replaced.
|
|
931
|
+
resident_client.shutdown()
|
|
921
932
|
# A full re-analysis is the full pass's, so a split config runs as its parts. Changed files stay on the
|
|
922
933
|
# config's own server: tsserver loads only the projects of the files it opens.
|
|
923
934
|
if changed_files is None:
|
|
@@ -74,8 +74,9 @@ _LEGACY_CACHE_SUBDIR = "cache"
|
|
|
74
74
|
# v11: every language builds its call edges from definitions at the call site.
|
|
75
75
|
# v12: a large TypeScript monorepo runs one server per tsconfig project, so a graph one out-of-memory
|
|
76
76
|
# server left without edges is rebuilt; bases, implementations and edge-only declarations link across engines.
|
|
77
|
+
# v13: a TypeScript call into a sibling workspace package resolves to its sources without an install or build.
|
|
77
78
|
# Older pickles are treated as cache misses and re-run.
|
|
78
|
-
_TAG_VERSION = "
|
|
79
|
+
_TAG_VERSION = "v13"
|
|
79
80
|
|
|
80
81
|
|
|
81
82
|
class StaticAnalysisCache:
|
{codeboarding-0.15.0 → codeboarding-0.15.1}/static_analyzer/engine/adapters/typescript_adapter.py
RENAMED
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from collections.abc import Collection
|
|
5
8
|
from pathlib import Path
|
|
6
9
|
|
|
7
10
|
from infra.repo_utils.ignore import RepoIgnoreManager
|
|
8
11
|
from static_analyzer.config import Language
|
|
9
12
|
from static_analyzer.engine.language_adapter import LanguageAdapter
|
|
13
|
+
from static_analyzer.exceptions import WorkspaceSetupError
|
|
14
|
+
from static_analyzer.typescript_workspace import PLUGIN_LOCATION, PLUGIN_NAME, workspace_members
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
class TypeScriptAdapter(LanguageAdapter):
|
|
@@ -34,7 +41,30 @@ class TypeScriptAdapter(LanguageAdapter):
|
|
|
34
41
|
def get_lsp_init_options(self, ignore_manager: RepoIgnoreManager | None = None) -> dict:
|
|
35
42
|
# Why: by default a syntax-only server answers while the project still loads, and a definition
|
|
36
43
|
# that needs an inferred type -- ``.then(t => t.serialize())`` -- comes back empty on a cold start.
|
|
37
|
-
|
|
44
|
+
# Why the plugin: a call into a sibling workspace package of an uninstalled, unbuilt checkout resolves nowhere;
|
|
45
|
+
# the plugin resolves it to the member's sources (see ``typescript_workspace``).
|
|
46
|
+
return {
|
|
47
|
+
"tsserver": {"useSyntaxServer": "never"},
|
|
48
|
+
"plugins": [{"name": PLUGIN_NAME, "location": str(PLUGIN_LOCATION)}],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def get_lsp_commands(self, project_root: Path) -> list[dict]:
|
|
52
|
+
"""Hands the workspace members to the plugin; tsserver gives the configuration to each project it loads."""
|
|
53
|
+
members = workspace_members(project_root)
|
|
54
|
+
if not members:
|
|
55
|
+
return []
|
|
56
|
+
if not (PLUGIN_LOCATION / "node_modules" / PLUGIN_NAME / "index.js").is_file():
|
|
57
|
+
raise WorkspaceSetupError(f"{PLUGIN_NAME} is missing from this install; reinstall CodeBoarding")
|
|
58
|
+
logger.info(f"tsserver resolves {len(members)} workspace members of {project_root} to their sources")
|
|
59
|
+
configuration = {"root": str(project_root), "members": {name: str(path) for name, path in members.items()}}
|
|
60
|
+
return [{"command": "_typescript.configurePlugin", "arguments": [PLUGIN_NAME, configuration]}]
|
|
61
|
+
|
|
62
|
+
def rebuilds_for(self, changed_files: Collection[Path], project_root: Path) -> bool:
|
|
63
|
+
"""Whether a change alters how imports resolve: a tsconfig, a workspace declaration, or a workspace's manifest."""
|
|
64
|
+
names = {path.name for path in changed_files}
|
|
65
|
+
if "pnpm-workspace.yaml" in names or any(re.fullmatch(r"[tj]sconfig.*\.json", name) for name in names):
|
|
66
|
+
return True
|
|
67
|
+
return "package.json" in names and bool(workspace_members(project_root))
|
|
38
68
|
|
|
39
69
|
def get_lsp_heap_options(self, heap_bytes: int) -> dict:
|
|
40
70
|
# Why: at Node's default heap tsserver runs out of memory on a large tsconfig project and answers nothing.
|
|
@@ -271,6 +271,14 @@ class LanguageAdapter(ABC):
|
|
|
271
271
|
"""
|
|
272
272
|
return {}
|
|
273
273
|
|
|
274
|
+
def get_lsp_commands(self, project_root: Path) -> list[dict]:
|
|
275
|
+
"""``workspace/executeCommand`` params the server is sent once initialized, and again on a restart."""
|
|
276
|
+
return []
|
|
277
|
+
|
|
278
|
+
def rebuilds_for(self, changed_files: Collection[Path], project_root: Path) -> bool:
|
|
279
|
+
"""Whether a warm start over *changed_files* must re-analyse the whole project on a fresh server."""
|
|
280
|
+
return False
|
|
281
|
+
|
|
274
282
|
def workspace_folders(self, project_root: Path, source_files: Sequence[Path]) -> list[Path]:
|
|
275
283
|
"""Workspace folders to announce to the server in place of the project, or none."""
|
|
276
284
|
return []
|
|
@@ -21,7 +21,7 @@ from pathlib import Path
|
|
|
21
21
|
from static_analyzer.config import LANGUAGE_ID_BY_SUFFIX
|
|
22
22
|
from static_analyzer.engine.lsp_constants import REQUEST_BATCH_SIZE
|
|
23
23
|
from static_analyzer.engine.utils import uri_to_path
|
|
24
|
-
from static_analyzer.exceptions import LanguageServerStalledError, StaticAnalysisFatalError
|
|
24
|
+
from static_analyzer.exceptions import LanguageServerStalledError, StaticAnalysisFatalError, WorkspaceSetupError
|
|
25
25
|
from static_analyzer.lsp_client.diagnostics import FileDiagnosticsMap, LSPDiagnostic
|
|
26
26
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
@@ -113,6 +113,7 @@ class LSPClient:
|
|
|
113
113
|
extra_client_capabilities: dict | None = None,
|
|
114
114
|
error_verdict: Callable[[dict], ErrorVerdict] | None = None,
|
|
115
115
|
workspace_folders: list[Path] | None = None,
|
|
116
|
+
commands: list[dict] | None = None,
|
|
116
117
|
) -> None:
|
|
117
118
|
self._command = command
|
|
118
119
|
self._project_root = project_root
|
|
@@ -129,6 +130,7 @@ class LSPClient:
|
|
|
129
130
|
self._error_verdict = error_verdict
|
|
130
131
|
# Announced in place of the project folder, for a server handed a workspace described apart from it.
|
|
131
132
|
self._workspace_folders = workspace_folders or []
|
|
133
|
+
self._commands = commands or []
|
|
132
134
|
self._process: subprocess.Popen | None = None # type: ignore[type-arg]
|
|
133
135
|
self._stdout_fd: int | None = None
|
|
134
136
|
self._request_id = 0
|
|
@@ -257,6 +259,8 @@ class LSPClient:
|
|
|
257
259
|
if isinstance(init_result, dict):
|
|
258
260
|
self._server_capabilities = init_result.get("capabilities") or {}
|
|
259
261
|
self._send_notification("initialized", {})
|
|
262
|
+
for command in self._commands:
|
|
263
|
+
self._send_request("workspace/executeCommand", command, timeout=self._default_timeout, required=True)
|
|
260
264
|
|
|
261
265
|
# LSP servers receive configuration through three mechanisms:
|
|
262
266
|
# 1. initializationOptions — sent in the initialize request above.
|
|
@@ -706,8 +710,10 @@ class LSPClient:
|
|
|
706
710
|
answers.append(BatchAnswer(parsed, req_id not in unserved, req_id in retryable))
|
|
707
711
|
return answers
|
|
708
712
|
|
|
709
|
-
def _send_request(
|
|
710
|
-
|
|
713
|
+
def _send_request(
|
|
714
|
+
self, method: str, params: dict | list | None, timeout: int, required: bool = False
|
|
715
|
+
) -> dict | list | None:
|
|
716
|
+
"""Send a JSON-RPC request and wait for the response; a *required* one that does not succeed is fatal."""
|
|
711
717
|
if self.server_failure:
|
|
712
718
|
self.check_alive()
|
|
713
719
|
self._request_id += 1
|
|
@@ -731,6 +737,8 @@ class LSPClient:
|
|
|
731
737
|
|
|
732
738
|
if "error" in msg:
|
|
733
739
|
error = msg["error"]
|
|
740
|
+
if required:
|
|
741
|
+
raise WorkspaceSetupError(f"The language server rejected {method} {params}: {error}")
|
|
734
742
|
if isinstance(error, dict) and error.get("code") == LSP_METHOD_NOT_FOUND:
|
|
735
743
|
raise MethodNotFoundError(error.get("message", "Method not found"))
|
|
736
744
|
logger.warning("LSP error for request %d (%s): %s", req_id, method, error)
|
|
@@ -742,6 +750,8 @@ class LSPClient:
|
|
|
742
750
|
return None
|
|
743
751
|
return msg.get("result")
|
|
744
752
|
|
|
753
|
+
if required:
|
|
754
|
+
raise WorkspaceSetupError(f"The language server did not answer {method} {params} within {timeout}s")
|
|
745
755
|
raise TimeoutError(f"Timeout waiting for LSP response to request {req_id}")
|
|
746
756
|
|
|
747
757
|
def _send_notification(self, method: str, params: dict | list | None) -> None:
|
|
@@ -5,5 +5,9 @@ class StaticAnalysisFatalError(RuntimeError):
|
|
|
5
5
|
"""Raised when continuing would produce misleading cached analysis."""
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
class WorkspaceSetupError(StaticAnalysisFatalError):
|
|
9
|
+
"""Raised when the workspace a language server must be set up with cannot be, so its answers would miss edges."""
|
|
10
|
+
|
|
11
|
+
|
|
8
12
|
class LanguageServerStalledError(StaticAnalysisFatalError):
|
|
9
13
|
"""Raised when a language server stays up but the backend that answers for it has died."""
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// tsserver plugin: a call into a sibling workspace package resolves to its sources in a checkout neither installed nor
|
|
2
|
+
// built. Core hands it {root, members} through `_typescript.configurePlugin` (static_analyzer/typescript_workspace.py).
|
|
3
|
+
// 1. Links: a lookup under <dir>/node_modules/<member> answers from the member's folder, as an install links it.
|
|
4
|
+
// 2. References: a project references the configs of each member it imports, so TypeScript's own
|
|
5
|
+
// source-of-project-reference redirect maps the member's missing build output (outDir) back to its sources.
|
|
6
|
+
// 3. An entry no config emits (a bundler builds it) is read as its source under src/, as Knip maps it.
|
|
7
|
+
"use strict";
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const HOOKED = Symbol("codeboarding-workspace-sources");
|
|
12
|
+
// <before>/node_modules/<package name><rest>, the last node_modules segment.
|
|
13
|
+
const NODE_MODULES_PATH = /^(.*)\/node_modules\/((?:@[^/]+\/)?[^/@][^/]*)(\/.*)?$/;
|
|
14
|
+
// The package a module specifier names: `from "x"`, `import "x"`, `import("x")`, `require("x")`.
|
|
15
|
+
const SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|\bimport\s+)["'`]((?:@[^/"'`]+\/)?[^./"'`@][^/"'`]*)/g;
|
|
16
|
+
const ENTRY_FIELDS = ["main", "module", "types", "typings", "exports"];
|
|
17
|
+
const SOURCE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
18
|
+
|
|
19
|
+
// Forward slashes and a lower-case drive letter, as tsserver spells a path.
|
|
20
|
+
const norm = (file) => file.replace(/\\/g, "/").replace(/^[A-Z]:/, (drive) => drive.toLowerCase());
|
|
21
|
+
|
|
22
|
+
function linkMembers(host, root, members) {
|
|
23
|
+
const linked = (file) => {
|
|
24
|
+
const match = typeof file === "string" && norm(file).startsWith(`${root}/`) && NODE_MODULES_PATH.exec(norm(file));
|
|
25
|
+
const folder = match && members.get(match[2]);
|
|
26
|
+
return folder ? folder + (match[3] || "") : null;
|
|
27
|
+
};
|
|
28
|
+
// Why: module resolution skips a node_modules folder that is not on disk; an install hoists the links to the root.
|
|
29
|
+
const scopes = [...members.keys()].filter((name) => name.startsWith("@")).map((name) => name.split("/")[0]);
|
|
30
|
+
const linkParents = new Set([`${root}/node_modules`, ...scopes.map((scope) => `${root}/node_modules/${scope}`)]);
|
|
31
|
+
const manifests = new Map([...members.values()].map((folder) => [`${folder}/package.json`, folder]));
|
|
32
|
+
for (const name of ["fileExists", "realpath"]) {
|
|
33
|
+
const original = host[name];
|
|
34
|
+
if (typeof original === "function") host[name] = (file, ...rest) => original.call(host, linked(file) || file, ...rest);
|
|
35
|
+
}
|
|
36
|
+
const { readFile, directoryExists } = host;
|
|
37
|
+
host.readFile = (file, ...rest) => {
|
|
38
|
+
const target = linked(file) || file;
|
|
39
|
+
const text = readFile.call(host, target, ...rest);
|
|
40
|
+
const folder = text !== undefined && typeof target === "string" && manifests.get(norm(target));
|
|
41
|
+
return folder ? withSources(text, folder) : text;
|
|
42
|
+
};
|
|
43
|
+
host.directoryExists = (dir) => {
|
|
44
|
+
const target = linked(dir);
|
|
45
|
+
if (target) return directoryExists.call(host, target);
|
|
46
|
+
return directoryExists.call(host, dir) || (typeof dir === "string" && linkParents.has(norm(dir)));
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// *text*, a member's package.json, with each entry missing from disk read as its source under src/ when there is one.
|
|
51
|
+
function withSources(text, folder) {
|
|
52
|
+
let manifest;
|
|
53
|
+
try {
|
|
54
|
+
manifest = JSON.parse(text);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
const source = (entry) => {
|
|
59
|
+
if (fs.existsSync(path.resolve(folder, entry))) return entry;
|
|
60
|
+
const stem = entry.replace(/^(\.\/)?[^/]+\//, "src/").replace(/(\.d)?\.[cm]?[jt]sx?$/, "");
|
|
61
|
+
const found = SOURCE_EXTENSIONS.map((extension) => stem + extension).find((file) => fs.existsSync(path.resolve(folder, file)));
|
|
62
|
+
return found ? `./${found}` : entry;
|
|
63
|
+
};
|
|
64
|
+
const replace = (value) => {
|
|
65
|
+
if (typeof value === "string") return source(value);
|
|
66
|
+
if (Array.isArray(value)) return value.map(replace);
|
|
67
|
+
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, replace(nested)]));
|
|
68
|
+
return value;
|
|
69
|
+
};
|
|
70
|
+
for (const field of ENTRY_FIELDS) if (field in manifest) manifest[field] = replace(manifest[field]);
|
|
71
|
+
return JSON.stringify(manifest);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function referenceMembers(project, host, members, configured) {
|
|
75
|
+
const own = configured ? norm(project.getProjectName()) : null;
|
|
76
|
+
const original = host.getProjectReferences;
|
|
77
|
+
const scanned = new Set();
|
|
78
|
+
const used = new Set();
|
|
79
|
+
let added = [];
|
|
80
|
+
// Why scanned as files join, not at create: tsserver enables plugins before it adds a project's root files.
|
|
81
|
+
host.getProjectReferences = () => {
|
|
82
|
+
const declared = (typeof original === "function" && original.call(host)) || [];
|
|
83
|
+
const files = project.getRootFiles().filter((file) => !scanned.has(file));
|
|
84
|
+
if (files.length) {
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
scanned.add(file);
|
|
87
|
+
try {
|
|
88
|
+
for (const match of fs.readFileSync(file, "utf8").matchAll(SPECIFIER)) used.add(match[1]);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Every tsconfig and jsconfig of each member. Why every one: the redirect then finds whichever emits an entry.
|
|
94
|
+
const have = new Set(declared.map((reference) => norm(path.resolve(reference.path))));
|
|
95
|
+
const folders = [...used].map((name) => members.get(name)).filter((folder) => folder && !have.has(folder));
|
|
96
|
+
added = folders
|
|
97
|
+
.flatMap((folder) => fs.readdirSync(folder).filter((file) => /^[tj]sconfig.*\.json$/.test(file)).sort().map((file) => `${folder}/${file}`))
|
|
98
|
+
.filter((config) => config !== own && !have.has(config))
|
|
99
|
+
.map((config) => ({ path: config }));
|
|
100
|
+
}
|
|
101
|
+
return declared.length || added.length ? [...declared, ...added] : undefined;
|
|
102
|
+
};
|
|
103
|
+
if (!configured && typeof host.getCompilerHost !== "function") {
|
|
104
|
+
// An inferred project has no references of its own, so tsserver gives it neither the redirect nor the compiler
|
|
105
|
+
// host the redirect answers module resolution through. Both as a configured project has them.
|
|
106
|
+
let compilerHost;
|
|
107
|
+
host.setCompilerHost = (created) => {
|
|
108
|
+
compilerHost = created;
|
|
109
|
+
};
|
|
110
|
+
host.getCompilerHost = () => compilerHost;
|
|
111
|
+
host.useSourceOfProjectReferenceRedirect = () => added.length > 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = function init({ typescript: ts }) {
|
|
116
|
+
return {
|
|
117
|
+
create(info) {
|
|
118
|
+
const host = info.languageServiceHost;
|
|
119
|
+
const { root, members } = info.config || {};
|
|
120
|
+
if (!root || !members || host[HOOKED]) return info.languageService;
|
|
121
|
+
host[HOOKED] = true;
|
|
122
|
+
const folders = new Map(Object.entries(members).map(([name, folder]) => [name, norm(folder)]));
|
|
123
|
+
linkMembers(host, norm(root), folders);
|
|
124
|
+
const kind = info.project.projectKind;
|
|
125
|
+
if (kind === ts.server.ProjectKind.Configured || kind === ts.server.ProjectKind.Inferred) {
|
|
126
|
+
referenceMembers(info.project, host, folders, kind === ts.server.ProjectKind.Configured);
|
|
127
|
+
}
|
|
128
|
+
info.project.projectService.logger.info(`codeboarding-workspace-sources: ${folders.size} members under ${root}`);
|
|
129
|
+
return info.languageService;
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""A JavaScript workspace's members, for the tsserver plugin (``typescript_plugin/``) that resolves calls into them.
|
|
2
|
+
|
|
3
|
+
Why: in a checkout neither installed nor built, ``import { f } from "@acme/core"`` resolves nowhere: there is no
|
|
4
|
+
``node_modules/@acme/core`` link, and the member's ``main``/``types``/``exports`` name a ``dist/`` that does not exist.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
from static_analyzer.exceptions import WorkspaceSetupError
|
|
14
|
+
|
|
15
|
+
PLUGIN_NAME = "codeboarding-workspace-sources"
|
|
16
|
+
PLUGIN_LOCATION = Path(__file__).resolve().parent / "typescript_plugin"
|
|
17
|
+
"""tsserver loads a plugin from ``<location>/node_modules/<name>``."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def workspace_members(repo: Path) -> dict[str, Path]:
|
|
21
|
+
"""Each declared member's package name and folder, the first by path winning; empty when *repo* declares none.
|
|
22
|
+
|
|
23
|
+
Why pnpm-workspace.yaml alone when present: pnpm reads no ``workspaces`` from package.json.
|
|
24
|
+
"""
|
|
25
|
+
pnpm = repo / "pnpm-workspace.yaml"
|
|
26
|
+
try:
|
|
27
|
+
if pnpm.is_file():
|
|
28
|
+
patterns = (yaml.safe_load(pnpm.read_text(encoding="utf-8")) or {}).get("packages")
|
|
29
|
+
elif (repo / "package.json").is_file():
|
|
30
|
+
patterns = json.loads((repo / "package.json").read_text(encoding="utf-8")).get("workspaces")
|
|
31
|
+
patterns = patterns.get("packages") if isinstance(patterns, dict) else patterns
|
|
32
|
+
else:
|
|
33
|
+
return {}
|
|
34
|
+
patterns = patterns or []
|
|
35
|
+
if not isinstance(patterns, list) or not all(isinstance(pattern, str) for pattern in patterns):
|
|
36
|
+
raise ValueError(f"the member patterns are no list of strings: {patterns!r}")
|
|
37
|
+
expanded = [
|
|
38
|
+
(pattern.startswith("!"), alternative.removeprefix("./").strip("/"))
|
|
39
|
+
for pattern in patterns
|
|
40
|
+
for alternative in expand_braces(pattern.removeprefix("!"))
|
|
41
|
+
]
|
|
42
|
+
excluded = [glob_regex(pattern) for negated, pattern in expanded if negated]
|
|
43
|
+
members: dict[str, Path] = {}
|
|
44
|
+
for negated, pattern in expanded:
|
|
45
|
+
if negated or pattern in ("", "."):
|
|
46
|
+
continue
|
|
47
|
+
for manifest in sorted(repo.glob(f"{pattern}/package.json")):
|
|
48
|
+
folder = manifest.parent.relative_to(repo)
|
|
49
|
+
if "node_modules" in folder.parts or any(
|
|
50
|
+
regex.fullmatch(f"{folder.as_posix()}/") for regex in excluded
|
|
51
|
+
):
|
|
52
|
+
continue
|
|
53
|
+
name = json.loads(manifest.read_text(encoding="utf-8")).get("name")
|
|
54
|
+
if isinstance(name, str) and name:
|
|
55
|
+
members.setdefault(name, manifest.parent)
|
|
56
|
+
except (OSError, ValueError, AttributeError, yaml.YAMLError) as error:
|
|
57
|
+
raise WorkspaceSetupError(f"Cannot read the workspace {repo} declares: {error}") from error
|
|
58
|
+
return members
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def expand_braces(pattern: str) -> list[str]:
|
|
62
|
+
"""*pattern* with each ``{a,b}`` group spelled out, as the package managers' globs do."""
|
|
63
|
+
group = re.search(r"\{([^{}]*,[^{}]*)\}", pattern)
|
|
64
|
+
if group is None:
|
|
65
|
+
return [pattern]
|
|
66
|
+
head, tail = pattern[: group.start()], pattern[group.end() :]
|
|
67
|
+
return [expanded for alternative in group[1].split(",") for expanded in expand_braces(head + alternative + tail)]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def glob_regex(pattern: str) -> re.Pattern[str]:
|
|
71
|
+
"""A workspace glob over a folder's path from the root plus ``/``: ``*`` within a segment, ``**`` any depth."""
|
|
72
|
+
return re.compile(
|
|
73
|
+
"".join(
|
|
74
|
+
(
|
|
75
|
+
"(?:[^/]+/)*"
|
|
76
|
+
if segment == "**"
|
|
77
|
+
else re.escape(segment).replace(r"\*", "[^/]*").replace(r"\?", "[^/]") + "/"
|
|
78
|
+
)
|
|
79
|
+
for segment in pattern.split("/")
|
|
80
|
+
)
|
|
81
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{codeboarding-0.15.0 → codeboarding-0.15.1}/codeboarding_cli/commands/incremental_analysis.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|