@oa-sdk/spec-bundler 0.1.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/LICENSE +201 -0
- package/README.md +240 -0
- package/dist/archiver.d.ts +22 -0
- package/dist/archiver.js +155 -0
- package/dist/category-parser.d.ts +15 -0
- package/dist/category-parser.js +115 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +760 -0
- package/dist/context-generator.d.ts +14 -0
- package/dist/context-generator.js +297 -0
- package/dist/freshness.d.ts +41 -0
- package/dist/freshness.js +365 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +705 -0
- package/dist/link-rewriter.d.ts +65 -0
- package/dist/link-rewriter.js +777 -0
- package/dist/resolver.d.ts +27 -0
- package/dist/resolver.js +276 -0
- package/dist/schema.d.ts +563 -0
- package/dist/schema.js +670 -0
- package/dist/section-slicer.d.ts +12 -0
- package/dist/section-slicer.js +114 -0
- package/dist/topic.d.ts +62 -0
- package/dist/topic.js +262 -0
- package/dist/tree-shaker.d.ts +40 -0
- package/dist/tree-shaker.js +523 -0
- package/dist/types.d.ts +369 -0
- package/dist/types.js +2 -0
- package/package.json +29 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SectionSliceResult {
|
|
2
|
+
sliced: string;
|
|
3
|
+
matchedSections: string[];
|
|
4
|
+
isSliced: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Extracts and slices markdown documents by section heading patterns,
|
|
8
|
+
* preserving document frontmatter and root header while eliminating unreferenced sections.
|
|
9
|
+
* Automatically retains child sub-headings of matched sections.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sliceMarkdownBySections(content: string, sectionPatterns: string[]): SectionSliceResult;
|
|
12
|
+
//# sourceMappingURL=section-slicer.d.ts.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { matchesAnyPattern } from "./resolver.js";
|
|
2
|
+
function parseMarkdownTree(body) {
|
|
3
|
+
const lines = body.split(/\r?\n/);
|
|
4
|
+
const leadingLines = [];
|
|
5
|
+
const rootNodes = [];
|
|
6
|
+
const stack = [];
|
|
7
|
+
let isFirstHeading = true;
|
|
8
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9
|
+
const line = lines[i];
|
|
10
|
+
const match = line.match(/^(#{1,6})\s+(.+)$/);
|
|
11
|
+
if (match) {
|
|
12
|
+
const level = match[1].length;
|
|
13
|
+
const rawTitle = match[2].trim();
|
|
14
|
+
const cleanTitle = rawTitle.replace(/\s*\{#[^}]+\}\s*$/, "").trim();
|
|
15
|
+
// Top-level document title (# Title) at the start is preserved as preamble
|
|
16
|
+
if (level === 1 && isFirstHeading) {
|
|
17
|
+
leadingLines.push(line);
|
|
18
|
+
isFirstHeading = false;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
isFirstHeading = false;
|
|
22
|
+
const newNode = {
|
|
23
|
+
level,
|
|
24
|
+
title: cleanTitle,
|
|
25
|
+
lines: [line],
|
|
26
|
+
children: [],
|
|
27
|
+
};
|
|
28
|
+
// Pop stack until parent has level < newNode.level
|
|
29
|
+
while (stack.length > 0 && stack[stack.length - 1].level >= level) {
|
|
30
|
+
stack.pop();
|
|
31
|
+
}
|
|
32
|
+
if (stack.length === 0) {
|
|
33
|
+
rootNodes.push(newNode);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
stack[stack.length - 1].children.push(newNode);
|
|
37
|
+
}
|
|
38
|
+
stack.push(newNode);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
if (stack.length > 0) {
|
|
42
|
+
stack[stack.length - 1].lines.push(line);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
leadingLines.push(line);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { preamble: leadingLines.join("\n").trim(), rootNodes };
|
|
50
|
+
}
|
|
51
|
+
function renderSectionNode(node) {
|
|
52
|
+
const parts = [node.lines.join("\n")];
|
|
53
|
+
for (const child of node.children) {
|
|
54
|
+
parts.push(renderSectionNode(child));
|
|
55
|
+
}
|
|
56
|
+
return parts.join("\n\n");
|
|
57
|
+
}
|
|
58
|
+
function filterMatchingSections(nodes, patterns, matchedSections) {
|
|
59
|
+
const result = [];
|
|
60
|
+
for (const node of nodes) {
|
|
61
|
+
const isMatched = matchesAnyPattern(node.title, patterns) ||
|
|
62
|
+
patterns.some((p) => node.title.toLowerCase() === p.toLowerCase() ||
|
|
63
|
+
node.title.toLowerCase().startsWith(p.toLowerCase().replace(/\*+$/, "")));
|
|
64
|
+
if (isMatched) {
|
|
65
|
+
matchedSections.push(node.title);
|
|
66
|
+
result.push(renderSectionNode(node));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Check if any child matches
|
|
70
|
+
const childResults = filterMatchingSections(node.children, patterns, matchedSections);
|
|
71
|
+
result.push(...childResults);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Extracts and slices markdown documents by section heading patterns,
|
|
78
|
+
* preserving document frontmatter and root header while eliminating unreferenced sections.
|
|
79
|
+
* Automatically retains child sub-headings of matched sections.
|
|
80
|
+
*/
|
|
81
|
+
export function sliceMarkdownBySections(content, sectionPatterns) {
|
|
82
|
+
if (!sectionPatterns || sectionPatterns.length === 0) {
|
|
83
|
+
return { sliced: content, matchedSections: [], isSliced: false };
|
|
84
|
+
}
|
|
85
|
+
// 1. Separate frontmatter
|
|
86
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
87
|
+
const frontmatter = frontmatterMatch ? frontmatterMatch[0] : "";
|
|
88
|
+
const body = content.slice(frontmatter.length);
|
|
89
|
+
// 2. Parse hierarchical heading tree
|
|
90
|
+
const { preamble, rootNodes } = parseMarkdownTree(body);
|
|
91
|
+
// 3. Filter sections that match patterns
|
|
92
|
+
const matchedSections = [];
|
|
93
|
+
const keptSections = filterMatchingSections(rootNodes, sectionPatterns, matchedSections);
|
|
94
|
+
// If no sections matched patterns, preserve original document intact
|
|
95
|
+
if (matchedSections.length === 0) {
|
|
96
|
+
return { sliced: content, matchedSections: [], isSliced: false };
|
|
97
|
+
}
|
|
98
|
+
const parts = [];
|
|
99
|
+
if (frontmatter) {
|
|
100
|
+
parts.push(frontmatter.trim());
|
|
101
|
+
}
|
|
102
|
+
if (preamble) {
|
|
103
|
+
parts.push(preamble);
|
|
104
|
+
}
|
|
105
|
+
if (keptSections.length > 0) {
|
|
106
|
+
parts.push(keptSections.join("\n\n"));
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
sliced: parts.join("\n\n") + "\n",
|
|
110
|
+
matchedSections,
|
|
111
|
+
isSliced: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=section-slicer.js.map
|
package/dist/topic.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { BundleManifest, TopicProfile, TargetMapping, TraversalDirection, TopicWorkPhase, TopicScopeConfig } from "./types.js";
|
|
2
|
+
export interface TopicSummary {
|
|
3
|
+
name: string;
|
|
4
|
+
phase?: TopicWorkPhase;
|
|
5
|
+
author?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
output: string;
|
|
8
|
+
format: "zip" | "directory";
|
|
9
|
+
targetCount: number;
|
|
10
|
+
domain?: string;
|
|
11
|
+
issue?: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
contextDocsCount?: number;
|
|
14
|
+
hasScope?: boolean;
|
|
15
|
+
direction?: TraversalDirection;
|
|
16
|
+
}
|
|
17
|
+
export interface PhasePreset {
|
|
18
|
+
phase: TopicWorkPhase;
|
|
19
|
+
defaultDescription: string;
|
|
20
|
+
scope: TopicScopeConfig;
|
|
21
|
+
defaultPrompt?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const PHASE_PRESETS: Record<TopicWorkPhase, PhasePreset>;
|
|
24
|
+
export interface CreateTopicOptions {
|
|
25
|
+
name: string;
|
|
26
|
+
phase?: TopicWorkPhase;
|
|
27
|
+
author?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
entrypoints?: string[];
|
|
30
|
+
targets?: TargetMapping[];
|
|
31
|
+
domain?: string;
|
|
32
|
+
issue?: string;
|
|
33
|
+
prompt?: string;
|
|
34
|
+
output?: string;
|
|
35
|
+
format?: "zip" | "directory";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a standardized TopicProfile configured with lifecycle phase defaults and worker metadata.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createTopicProfile(options: CreateTopicOptions): TopicProfile;
|
|
41
|
+
/**
|
|
42
|
+
* Persists a newly created topic profile directly into the manifest file on disk.
|
|
43
|
+
*/
|
|
44
|
+
export declare function addTopicToManifest(manifestPath: string, topicName: string, profile: TopicProfile): {
|
|
45
|
+
updated: boolean;
|
|
46
|
+
topicCount: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Computes the default output path for a topic profile if none is explicitly declared.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getDefaultTopicOutput(manifestOutput: string, manifestName: string, topicName: string, format?: "zip" | "directory"): string;
|
|
52
|
+
/**
|
|
53
|
+
* Lists all declarative topic profiles present in a bundle manifest.
|
|
54
|
+
*/
|
|
55
|
+
export declare function listManifestTopics(manifest: BundleManifest): TopicSummary[];
|
|
56
|
+
/**
|
|
57
|
+
* Resolves a topic profile from a bundle manifest into an effective standalone BundleManifest.
|
|
58
|
+
* Integrates extraction scope boundaries, ambient background context documents,
|
|
59
|
+
* and directional graph reachability rules.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveTopicManifest(manifest: BundleManifest, topicName: string): BundleManifest;
|
|
62
|
+
//# sourceMappingURL=topic.d.ts.map
|
package/dist/topic.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const PHASE_PRESETS = {
|
|
4
|
+
develop: {
|
|
5
|
+
phase: "develop",
|
|
6
|
+
defaultDescription: "Feature development context focusing on specification and implementation mechanisms",
|
|
7
|
+
scope: {
|
|
8
|
+
direction: "downstream",
|
|
9
|
+
includeCategories: ["feature", "mechanism", "convention"],
|
|
10
|
+
categoryFidelity: {
|
|
11
|
+
mechanism: "full",
|
|
12
|
+
convention: "full",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultPrompt: "Implement the feature specification following designated mechanisms and architectural conventions.",
|
|
16
|
+
},
|
|
17
|
+
fix: {
|
|
18
|
+
phase: "fix",
|
|
19
|
+
defaultDescription: "Bugfix and refactoring context with upstream impact coordination and error catalogs",
|
|
20
|
+
scope: {
|
|
21
|
+
direction: "bidirectional",
|
|
22
|
+
upstreamDepth: 1,
|
|
23
|
+
includeCategories: ["feature", "error", "issue"],
|
|
24
|
+
categoryFidelity: {
|
|
25
|
+
upstream: "contract-only",
|
|
26
|
+
issue: "summary",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultPrompt: "Resolve the issue and verify error handling while preserving compatibility with upstream caller contracts.",
|
|
30
|
+
},
|
|
31
|
+
test: {
|
|
32
|
+
phase: "test",
|
|
33
|
+
defaultDescription: "Verification and QA context with error catalogs and contract guarantees",
|
|
34
|
+
scope: {
|
|
35
|
+
direction: "downstream",
|
|
36
|
+
includeCategories: ["feature", "error", "mechanism"],
|
|
37
|
+
categoryFidelity: {
|
|
38
|
+
mechanism: "contract-only",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultPrompt: "Verify that implementation satisfies all acceptance criteria, contract guarantees, and error recovery scenarios.",
|
|
42
|
+
},
|
|
43
|
+
review: {
|
|
44
|
+
phase: "review",
|
|
45
|
+
defaultDescription: "Architecture and peer review context with compact summaries across all categories",
|
|
46
|
+
scope: {
|
|
47
|
+
direction: "bidirectional",
|
|
48
|
+
upstreamDepth: 1,
|
|
49
|
+
includeCategories: ["feature", "mechanism", "convention", "error", "issue"],
|
|
50
|
+
categoryFidelity: {
|
|
51
|
+
upstream: "contract-only",
|
|
52
|
+
default: "summary",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
defaultPrompt: "Review architectural invariants, contract interfaces, and issue background without unnecessary implementation bloat.",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Creates a standardized TopicProfile configured with lifecycle phase defaults and worker metadata.
|
|
60
|
+
*/
|
|
61
|
+
export function createTopicProfile(options) {
|
|
62
|
+
const phase = options.phase ?? "develop";
|
|
63
|
+
const preset = PHASE_PRESETS[phase];
|
|
64
|
+
const entrypoints = options.entrypoints ?? [];
|
|
65
|
+
const targets = options.targets && options.targets.length > 0
|
|
66
|
+
? options.targets
|
|
67
|
+
: [{ source: "specs/**", dest: "specs/" }];
|
|
68
|
+
const scope = {
|
|
69
|
+
...preset.scope,
|
|
70
|
+
entrypoints: entrypoints.length > 0 ? entrypoints : undefined,
|
|
71
|
+
};
|
|
72
|
+
const context = {
|
|
73
|
+
domain: options.domain,
|
|
74
|
+
issue: options.issue,
|
|
75
|
+
prompt: options.prompt ?? preset.defaultPrompt,
|
|
76
|
+
generateContextDoc: true,
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
phase,
|
|
80
|
+
author: options.author,
|
|
81
|
+
description: options.description ?? `${options.name} (${preset.defaultDescription})`,
|
|
82
|
+
output: options.output,
|
|
83
|
+
format: options.format ?? "zip",
|
|
84
|
+
targets,
|
|
85
|
+
scope,
|
|
86
|
+
context,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Persists a newly created topic profile directly into the manifest file on disk.
|
|
91
|
+
*/
|
|
92
|
+
export function addTopicToManifest(manifestPath, topicName, profile) {
|
|
93
|
+
const absPath = path.resolve(process.cwd(), manifestPath);
|
|
94
|
+
if (!fs.existsSync(absPath)) {
|
|
95
|
+
throw new Error(`Manifest file not found: ${manifestPath}`);
|
|
96
|
+
}
|
|
97
|
+
const raw = fs.readFileSync(absPath, "utf8");
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = JSON.parse(raw);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new Error(`Failed to parse manifest JSON at ${manifestPath}: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
if (!parsed.topics) {
|
|
106
|
+
parsed.topics = {};
|
|
107
|
+
}
|
|
108
|
+
parsed.topics[topicName] = profile;
|
|
109
|
+
fs.writeFileSync(absPath, JSON.stringify(parsed, null, 2) + "\n", "utf8");
|
|
110
|
+
return { updated: true, topicCount: Object.keys(parsed.topics).length };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Computes the default output path for a topic profile if none is explicitly declared.
|
|
114
|
+
*/
|
|
115
|
+
export function getDefaultTopicOutput(manifestOutput, manifestName, topicName, format = "zip") {
|
|
116
|
+
const dir = path.dirname(manifestOutput);
|
|
117
|
+
const ext = format === "directory" ? "" : path.extname(manifestOutput) || ".zip";
|
|
118
|
+
const sanitizedTopic = topicName.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
119
|
+
return path.join(dir, `${manifestName}-${sanitizedTopic}${ext}`).replace(/\\/g, "/");
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Lists all declarative topic profiles present in a bundle manifest.
|
|
123
|
+
*/
|
|
124
|
+
export function listManifestTopics(manifest) {
|
|
125
|
+
if (!manifest.topics || typeof manifest.topics !== "object") {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const summaries = [];
|
|
129
|
+
for (const [name, profile] of Object.entries(manifest.topics)) {
|
|
130
|
+
const format = profile.format ?? manifest.format ?? "zip";
|
|
131
|
+
const output = profile.output ??
|
|
132
|
+
getDefaultTopicOutput(manifest.output, manifest.name, name, format);
|
|
133
|
+
const contextDocsCount = Array.isArray(profile.context?.docs)
|
|
134
|
+
? profile.context.docs.length
|
|
135
|
+
: 0;
|
|
136
|
+
const hasScope = Boolean(profile.scope &&
|
|
137
|
+
((profile.scope.entrypoints && profile.scope.entrypoints.length > 0) ||
|
|
138
|
+
(profile.scope.include && profile.scope.include.length > 0) ||
|
|
139
|
+
(profile.scope.sections && profile.scope.sections.length > 0)));
|
|
140
|
+
summaries.push({
|
|
141
|
+
name,
|
|
142
|
+
phase: profile.phase,
|
|
143
|
+
author: profile.author,
|
|
144
|
+
description: profile.description,
|
|
145
|
+
output,
|
|
146
|
+
format,
|
|
147
|
+
targetCount: Array.isArray(profile.targets) ? profile.targets.length : 0,
|
|
148
|
+
domain: profile.context?.domain,
|
|
149
|
+
issue: profile.context?.issue,
|
|
150
|
+
tags: profile.context?.tags,
|
|
151
|
+
contextDocsCount,
|
|
152
|
+
hasScope,
|
|
153
|
+
direction: profile.scope?.direction,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return summaries;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Resolves a topic profile from a bundle manifest into an effective standalone BundleManifest.
|
|
160
|
+
* Integrates extraction scope boundaries, ambient background context documents,
|
|
161
|
+
* and directional graph reachability rules.
|
|
162
|
+
*/
|
|
163
|
+
export function resolveTopicManifest(manifest, topicName) {
|
|
164
|
+
const topics = manifest.topics;
|
|
165
|
+
if (!topics || !topics[topicName]) {
|
|
166
|
+
const available = Object.keys(topics ?? {});
|
|
167
|
+
const hint = available.length > 0
|
|
168
|
+
? ` Available topics in manifest: ${available.map((t) => `'${t}'`).join(", ")}`
|
|
169
|
+
: ` Manifest declares 0 topics under 'topics'.`;
|
|
170
|
+
throw new Error(`Topic '${topicName}' not found in manifest.${hint}`);
|
|
171
|
+
}
|
|
172
|
+
const topicProfile = topics[topicName];
|
|
173
|
+
const format = topicProfile.format ?? manifest.format ?? "zip";
|
|
174
|
+
const output = topicProfile.output ??
|
|
175
|
+
getDefaultTopicOutput(manifest.output, manifest.name, topicName, format);
|
|
176
|
+
// 1. Process context ambient docs into targets
|
|
177
|
+
const contextDocs = topicProfile.context?.docs ?? [];
|
|
178
|
+
const contextTargets = contextDocs.map((doc) => ({
|
|
179
|
+
source: doc.source,
|
|
180
|
+
dest: doc.dest ? doc.dest : "context/",
|
|
181
|
+
exclude: doc.exclude,
|
|
182
|
+
}));
|
|
183
|
+
const effectiveTargets = [...topicProfile.targets, ...contextTargets];
|
|
184
|
+
// 2. Combine topic-level, global, and scope exclusions
|
|
185
|
+
const combinedExclude = [
|
|
186
|
+
...(topicProfile.exclude ?? manifest.exclude ?? []),
|
|
187
|
+
...(topicProfile.scope?.exclude ?? []),
|
|
188
|
+
];
|
|
189
|
+
// 3. Resolve effective tree-shaking configuration from scope and treeShake
|
|
190
|
+
const preservePatterns = contextDocs.length > 0 ? ["context/**", "_context/**"] : [];
|
|
191
|
+
let effectiveTreeShake;
|
|
192
|
+
if (topicProfile.treeShake) {
|
|
193
|
+
effectiveTreeShake = {
|
|
194
|
+
enabled: topicProfile.treeShake.enabled ?? true,
|
|
195
|
+
entrypoints: topicProfile.scope?.entrypoints ?? topicProfile.treeShake.entrypoints ?? [],
|
|
196
|
+
scope: topicProfile.scope?.include ?? topicProfile.treeShake.scope,
|
|
197
|
+
stopAt: topicProfile.scope?.stopAt ?? topicProfile.treeShake.stopAt,
|
|
198
|
+
maxDepth: topicProfile.scope?.maxDepth ?? topicProfile.treeShake.maxDepth,
|
|
199
|
+
direction: topicProfile.scope?.direction ?? topicProfile.treeShake.direction ?? "downstream",
|
|
200
|
+
upstreamDepth: topicProfile.scope?.upstreamDepth ?? topicProfile.treeShake.upstreamDepth ?? 1,
|
|
201
|
+
includeCategories: topicProfile.scope?.includeCategories ?? topicProfile.treeShake.includeCategories,
|
|
202
|
+
excludeCategories: topicProfile.scope?.excludeCategories ?? topicProfile.treeShake.excludeCategories,
|
|
203
|
+
categoryFidelity: topicProfile.scope?.categoryFidelity ?? topicProfile.treeShake.categoryFidelity,
|
|
204
|
+
preserve: [
|
|
205
|
+
...(topicProfile.treeShake.preserve ?? []),
|
|
206
|
+
...preservePatterns,
|
|
207
|
+
],
|
|
208
|
+
reportEliminated: topicProfile.treeShake.reportEliminated ?? true,
|
|
209
|
+
traversalMode: topicProfile.treeShake.traversalMode ??
|
|
210
|
+
topicProfile.scope?.traversalMode,
|
|
211
|
+
preserveTargets: topicProfile.treeShake.preserveTargets ??
|
|
212
|
+
topicProfile.scope?.preserveTargets,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
else if (topicProfile.scope &&
|
|
216
|
+
(topicProfile.scope.entrypoints ||
|
|
217
|
+
topicProfile.scope.include ||
|
|
218
|
+
topicProfile.scope.stopAt ||
|
|
219
|
+
topicProfile.scope.maxDepth !== undefined ||
|
|
220
|
+
topicProfile.scope.direction !== undefined ||
|
|
221
|
+
topicProfile.scope.includeCategories ||
|
|
222
|
+
topicProfile.scope.excludeCategories ||
|
|
223
|
+
topicProfile.scope.categoryFidelity)) {
|
|
224
|
+
effectiveTreeShake = {
|
|
225
|
+
enabled: true,
|
|
226
|
+
entrypoints: topicProfile.scope.entrypoints ?? [],
|
|
227
|
+
scope: topicProfile.scope.include,
|
|
228
|
+
stopAt: topicProfile.scope.stopAt,
|
|
229
|
+
maxDepth: topicProfile.scope.maxDepth,
|
|
230
|
+
direction: topicProfile.scope.direction ?? "downstream",
|
|
231
|
+
upstreamDepth: topicProfile.scope.upstreamDepth ?? 1,
|
|
232
|
+
includeCategories: topicProfile.scope.includeCategories,
|
|
233
|
+
excludeCategories: topicProfile.scope.excludeCategories,
|
|
234
|
+
categoryFidelity: topicProfile.scope.categoryFidelity,
|
|
235
|
+
preserve: preservePatterns,
|
|
236
|
+
reportEliminated: true,
|
|
237
|
+
traversalMode: topicProfile.scope.traversalMode ?? "hybrid",
|
|
238
|
+
preserveTargets: topicProfile.scope.preserveTargets ?? true,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
effectiveTreeShake = { enabled: false, entrypoints: [] };
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
...manifest,
|
|
246
|
+
name: `${manifest.name}:${topicName}`,
|
|
247
|
+
description: topicProfile.description ?? manifest.description,
|
|
248
|
+
output,
|
|
249
|
+
format,
|
|
250
|
+
targets: effectiveTargets,
|
|
251
|
+
exclude: combinedExclude.length > 0 ? combinedExclude : undefined,
|
|
252
|
+
deduplicate: topicProfile.deduplicate ?? manifest.deduplicate,
|
|
253
|
+
linkResolution: topicProfile.linkResolution ?? manifest.linkResolution,
|
|
254
|
+
treeShake: effectiveTreeShake,
|
|
255
|
+
scope: topicProfile.scope,
|
|
256
|
+
context: topicProfile.context,
|
|
257
|
+
phase: topicProfile.phase,
|
|
258
|
+
author: topicProfile.author,
|
|
259
|
+
lockfileMode: manifest.lockfileMode,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=topic.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ResolvedFileEntry, TreeShakeConfig, ReachabilityHop, DependencyTreeNode, DocumentCategory } from "./types.js";
|
|
2
|
+
export interface TreeShakeResult {
|
|
3
|
+
reachableEntries: Map<string, ResolvedFileEntry>;
|
|
4
|
+
eliminatedEntries: ResolvedFileEntry[];
|
|
5
|
+
warnings: string[];
|
|
6
|
+
lineage: Map<string, ReachabilityHop>;
|
|
7
|
+
boundaryTerminals: string[];
|
|
8
|
+
boundaryEdges: Map<string, Array<{
|
|
9
|
+
target: string;
|
|
10
|
+
rawRef: string;
|
|
11
|
+
}>>;
|
|
12
|
+
upstreamNodes?: string[];
|
|
13
|
+
downstreamNodes?: string[];
|
|
14
|
+
nodeCategories?: Map<string, DocumentCategory>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Performs reachability traversal (downstream, upstream, or bidirectional)
|
|
18
|
+
* starting from declarative entrypoints with category filtering and boundary enforcement.
|
|
19
|
+
*/
|
|
20
|
+
export declare function pruneUnreachableEntries(entries: Map<string, ResolvedFileEntry>, config: TreeShakeConfig, workspaceRoot: string): TreeShakeResult;
|
|
21
|
+
/**
|
|
22
|
+
* Traces the provenance chain from an entrypoint down to the given target destination.
|
|
23
|
+
*/
|
|
24
|
+
export declare function explainReachability(targetDest: string, lineage: Map<string, ReachabilityHop>, entrypoints: string[], boundaryEdges?: Map<string, Array<{
|
|
25
|
+
target: string;
|
|
26
|
+
rawRef: string;
|
|
27
|
+
}>>): Array<{
|
|
28
|
+
from: string;
|
|
29
|
+
to: string;
|
|
30
|
+
ref: string;
|
|
31
|
+
}> | null;
|
|
32
|
+
/**
|
|
33
|
+
* Constructs a visual hierarchical dependency tree for bundle inspection.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildDependencyTree(entries: Map<string, ResolvedFileEntry>, config: TreeShakeConfig, workspaceRoot: string): DependencyTreeNode[];
|
|
36
|
+
/**
|
|
37
|
+
* Formats a hierarchical dependency tree as human-readable ASCII text.
|
|
38
|
+
*/
|
|
39
|
+
export declare function renderAsciiTree(nodes: DependencyTreeNode[], prefix?: string): string;
|
|
40
|
+
//# sourceMappingURL=tree-shaker.d.ts.map
|