NAME

Perl::CodeMap - Analyze Perl packages and generate interactive dependency and source maps

VERSION

Version 0.3.2.

SYNOPSIS

The command-line interface is the recommended starting point:

cpanm Perl::CodeMap
cpanm PPI
perl-codemap --root ./lib --parser ppi --output map.html
perl-codemap --help
perl-codemap --man

The module also exposes the analyzer and report generator programmatically:

use strict;
use warnings;
use Perl::CodeMap;

my $codemap = Perl::CodeMap->new(
    root           => './lib',
    parser         => 'auto',
    embed_source   => 1,
    include_tests  => 0,
);

my $analysis = $codemap->analyze;
my $html_path = $codemap->write_report(
    $analysis,
    output => 'map.html',
    json   => 'map.json',
);

printf "Mapped %d packages to %s\n", scalar @{$analysis->{pkgs}}, $html_path;

DESCRIPTION

Perl::CodeMap provides a static analyzer for Perl codebases. It discovers package declarations, subroutines, file locations, and statically identifiable local package dependencies without loading or executing the input source. Its primary output is a self-contained HTML report featuring an interactive package dependency graph, searchable symbols, and a syntax-highlighted source viewer. It can also export the analysis as JSON for later use.

This module is the library underpinning the perl-codemap command installed by the same distribution. For most users, run perl-codemap --help or perl-codemap --man; no Perl API programming is required.

The preferred analysis engine is PPI. A limited heuristic fallback parser is available when PPI is not installed. This tool does not implement Perl's runtime semantics or a complete function-level call graph.

INSTALLATION

Install the application and recommended parser from CPAN:

cpanm Perl::CodeMap
cpanm PPI

Perl 5.20 or newer is required. PPI is recommended but is not required by the distribution; parser auto falls back to lite if PPI is unavailable.

For a standalone usage example, visit the public sample.

CONSTRUCTOR

new

my $codemap = Perl::CodeMap->new(%options);

Constructs an analyzer object. The source root must exist and must be a directory. Invalid parser modes, invalid per-file size limits, or an explicit request for an unavailable PPI parser raise an exception with die.

Accepted options are:

root

Filesystem directory to scan. Defaults to the current working directory (.). The path is resolved to an absolute path during construction.

parser

One of auto (default), ppi, or lite. auto selects PPI when installed, otherwise lite. ppi fails if PPI cannot be loaded. lite is a heuristic parser and may miss complex Perl syntax.

include_tests

Boolean; default false. When enabled, include .t test files and common t/, xt/, test/, and tests/ directories.

embed_source

Boolean; default true. When enabled, place decoded source-file text in the sources field of the analysis, making the offline report's source browser available. Reports may contain private information when this is enabled.

max_source_bytes

Positive integer limiting the number of bytes embedded from any one source file. Default 2_000_000. Oversized files are analyzed for symbols but not embedded in sources; a warning is recorded.

source_base

Optional URL prefix used for links to hosted source files. If omitted, a GitHub source URL may be inferred from the local Git checkout's origin remote and current revision. Set it explicitly for GitLab or other hosts.

name

Optional display name for the generated report. Defaults to the basename of the resolved source root.

The constructor returns a Perl::CodeMap object. It does not scan files; call "analyze" to perform the scan.

PUBLIC METHODS

analyze

my $analysis = $codemap->analyze;

Statically reads candidate Perl files beneath the constructor's root and returns a hash reference describing packages, files, functions, dependencies, source text (when enabled), and warnings.

The analyzer recognizes .pm, .pl, .psgi, optionally .t, and extensionless files with a Perl shebang. It skips hidden directories, symlinks, and common version-control, vendor, and build directories. It creates edges only for local packages that are discoverable in the same scan.

The analysis does not modify the source tree and does not execute source files. Individual unreadable files are noted in the warnings array. Unexpected I/O or parser failures may raise an exception.

See "ANALYSIS DATA FORMAT" for the returned structure.

hydrate_sources

my $loaded = $codemap->hydrate_sources($analysis, './original-source');

Adds decoded source contents to an existing analysis hash reference, including a previously imported JSON export. The source root is the second positional argument; if omitted, the analyzer's own root is used. Paths are taken from the analysis's package/file list and interpreted relative to that directory.

The method skips missing, oversized, symlinked, or unsafe paths; it prevents source reads outside the resolved root. It modifies $analysis->{sources} in place and returns the number of files loaded. The root must exist or an exception is raised. It does not remove previously embedded source entries that are unrelated to successfully loaded files.

Be careful when using data imported from untrusted JSON: the report will also retain text already present in that JSON, including any embedded source.

write_report

my $output_path = $codemap->write_report(
    $analysis,
    output => 'architecture.html',
    json   => 'architecture.json',
);

Serializes the analysis data into a single, self-contained HTML file with embedded CSS, JavaScript, syntax highlighting, and data. The HTML report requires only a modern browser with JavaScript enabled; it does not require a web server or CDN. Optionally writes a machine-readable JSON file.

Arguments:

$analysis

A hash reference containing at least a pkgs array, normally returned by "analyze" or decoded from an earlier CodeMap JSON export.

output

Optional HTML destination path. Defaults to perl-code-map.html.

json

Optional JSON export destination path. If omitted, no separate JSON file is written.

Returns the HTML output path. Invalid data or I/O failures raise an exception. Existing output files at these paths are overwritten.

ANALYSIS DATA FORMAT

The analysis is a hash reference with the following top-level members:

format

Numeric CodeMap data format version (currently 1).

name

Report display name.

module

Absolute path of the scanned root. Treat this as potentially sensitive metadata if exporting analysis outside your organization.

parser

Parser selected for the analysis: ppi or lite.

scanned_files

Number of recognized Perl source files read for the analysis.

rev, source

Detected Git revision and remote source prefix, or empty strings when unknown.

pkgs

Array reference of package objects. Each package contains path (fully qualified package name), dir, doc, imports (array of local package names), and files (array of file objects). Each file contains path, name, lines, and funcs. Each function object contains the keys n (name), l (starting line), s (approximate body length), k (kind), e (public-name flag), and d (preceding documentation comment).

sources

Hash reference mapping relative file paths to source text. Populated only when source embedding is enabled and the source file does not exceed the configured limit.

warnings

Array reference of nonfatal scan and source-embedding warnings.

This is an analysis/export structure, not a complete Perl abstract syntax tree. The current format number is intended to support versioned consumers; minor internal fields may evolve across versions. When processing imports, validate format and the expected types rather than assuming all fields are always present.

ERROR HANDLING

These public methods use die for invalid configuration, invalid report data, and unrecoverable read/write errors. Callers may wrap them in eval:

my $data = eval { $codemap->analyze };
if ($@) {
    warn "Unable to analyze source: $@";
}

Per-file read failures may be returned as warnings instead of being fatal. The CLI maps failures to documented exit statuses; run perl-codemap --man for its exit-status reference.

LIMITATIONS

Dependency edges represent statically detectable local package references, not runtime method calls. Dynamic require, AUTOLOAD, monkey-patching, Moose/Moo method modifiers, Catalyst dispatch, and DBIx::Class runtime relationships are not fully modeled. Multiple package declarations per file are supported; temporary block-scoped declarations are best effort. For complicated syntax, use PPI and verify the result against the source.

SECURITY AND PRIVACY

Source embedding is enabled by default. HTML/JSON results can contain entire proprietary source files, secrets, internal names, and filesystem paths. Keep reports from private repositories private. The option embed_source => 0 excludes source text but does not remove potentially sensitive package metadata. Use a dedicated secrets scanner if you need one.

The source analyzer does not execute scanned files. It is not a security sandbox or a secrets detector.

SEE ALSO

The executable's complete manual: perldoc perl-codemap or perl-codemap --man after installation. The online Perl CodeMap user guide and source repository provide additional examples.

PPI, Pod::Usage, Perl CodeMap on MetaCPAN, Live demo.

AUTHOR

Scott Muller <scott@21h.io>

COPYRIGHT AND LICENSE

Copyright (c) 2026 Scott Muller. This program is distributed under the MIT License. See LICENSE and NOTICE.md for attribution and third-party notices.