@a16njs/engine 0.4.0 → 0.5.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/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -4
- package/dist/index.js.map +1 -1
- package/dist/path-rewriter.d.ts +68 -0
- package/dist/path-rewriter.d.ts.map +1 -0
- package/dist/path-rewriter.js +144 -0
- package/dist/path-rewriter.js.map +1 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,25 @@ export interface ConversionOptions {
|
|
|
7
7
|
source: string;
|
|
8
8
|
/** Target plugin ID */
|
|
9
9
|
target: string;
|
|
10
|
-
/** Project root directory */
|
|
10
|
+
/** Project root directory (used as default for both source and target) */
|
|
11
11
|
root: string;
|
|
12
12
|
/** If true, only discover without writing */
|
|
13
13
|
dryRun?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Override root directory for discovery (reading).
|
|
16
|
+
* When set, discover() uses this instead of `root`.
|
|
17
|
+
*/
|
|
18
|
+
sourceRoot?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Override root directory for emission (writing).
|
|
21
|
+
* When set, emit() uses this instead of `root`.
|
|
22
|
+
*/
|
|
23
|
+
targetRoot?: string;
|
|
24
|
+
/**
|
|
25
|
+
* If true, rewrite file path references in content so they point
|
|
26
|
+
* to the target-format paths instead of source-format paths.
|
|
27
|
+
*/
|
|
28
|
+
rewritePathRefs?: boolean;
|
|
14
29
|
}
|
|
15
30
|
/**
|
|
16
31
|
* Git-ignore change information.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,OAAO,EACP,WAAW,EACX,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAsBxB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,8BAA8B;IAC9B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;CACjC;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAsC;IAErD;;;OAGG;gBACS,OAAO,GAAE,UAAU,EAAO;IAMtC;;;OAGG;IACH,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIxC;;;OAGG;IACH,WAAW,IAAI,UAAU,EAAE;IAS3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI7C;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAQxE;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAmFrE"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import { buildMapping, rewriteContent, detectOrphans } from './path-rewriter.js';
|
|
2
|
+
/**
|
|
3
|
+
* Well-known directory prefixes and file extensions for each plugin.
|
|
4
|
+
* Used by detectOrphans() to identify source-format path references.
|
|
5
|
+
*/
|
|
6
|
+
const PLUGIN_PATH_PATTERNS = {
|
|
7
|
+
cursor: {
|
|
8
|
+
prefixes: ['.cursor/rules/', '.cursor/skills/', '.cursor/commands/'],
|
|
9
|
+
extensions: ['.mdc', '.md'],
|
|
10
|
+
},
|
|
11
|
+
claude: {
|
|
12
|
+
prefixes: ['.claude/rules/', '.claude/skills/'],
|
|
13
|
+
extensions: ['.md'],
|
|
14
|
+
},
|
|
15
|
+
a16n: {
|
|
16
|
+
prefixes: ['.a16n/'],
|
|
17
|
+
extensions: ['.md', '.json'],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
1
20
|
/**
|
|
2
21
|
* The a16n conversion engine.
|
|
3
22
|
* Orchestrates plugins to discover and emit agent customizations.
|
|
@@ -67,16 +86,55 @@ export class A16nEngine {
|
|
|
67
86
|
if (!targetPlugin) {
|
|
68
87
|
throw new Error(`Unknown target: ${options.target}`);
|
|
69
88
|
}
|
|
89
|
+
// Resolve split roots: sourceRoot for discover, targetRoot for emit
|
|
90
|
+
const effectiveSourceRoot = options.sourceRoot ?? options.root;
|
|
91
|
+
const effectiveTargetRoot = options.targetRoot ?? options.root;
|
|
70
92
|
// Discover from source
|
|
71
|
-
const discovery = await sourcePlugin.discover(
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
const discovery = await sourcePlugin.discover(effectiveSourceRoot);
|
|
94
|
+
const allWarnings = [...discovery.warnings];
|
|
95
|
+
if (options.rewritePathRefs && discovery.items.length > 0) {
|
|
96
|
+
// Two-pass emit approach:
|
|
97
|
+
// Pass 1: Dry-run emit to get target paths for building the mapping
|
|
98
|
+
const dryEmission = await targetPlugin.emit(discovery.items, effectiveTargetRoot, {
|
|
99
|
+
dryRun: true,
|
|
100
|
+
});
|
|
101
|
+
allWarnings.push(...dryEmission.warnings);
|
|
102
|
+
// Build source→target path mapping from the dry-run results
|
|
103
|
+
const mapping = buildMapping(discovery.items, dryEmission.written, effectiveSourceRoot, effectiveTargetRoot);
|
|
104
|
+
// Rewrite content in discovered items
|
|
105
|
+
const rewriteResult = rewriteContent(discovery.items, mapping);
|
|
106
|
+
// Detect orphan references
|
|
107
|
+
const sourcePatterns = PLUGIN_PATH_PATTERNS[options.source];
|
|
108
|
+
if (sourcePatterns) {
|
|
109
|
+
const orphanWarnings = detectOrphans(rewriteResult.items, mapping, sourcePatterns.prefixes, sourcePatterns.extensions);
|
|
110
|
+
allWarnings.push(...orphanWarnings);
|
|
111
|
+
}
|
|
112
|
+
// Pass 2: Real emit with rewritten items
|
|
113
|
+
const emission = await targetPlugin.emit(rewriteResult.items, effectiveTargetRoot, {
|
|
114
|
+
dryRun: options.dryRun,
|
|
115
|
+
});
|
|
116
|
+
// Deduplicate: add only emission warnings not already present from dry-run
|
|
117
|
+
const existingMessages = new Set(allWarnings.map((w) => w.message));
|
|
118
|
+
for (const w of emission.warnings) {
|
|
119
|
+
if (!existingMessages.has(w.message)) {
|
|
120
|
+
allWarnings.push(w);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
discovered: discovery.items,
|
|
125
|
+
written: emission.written,
|
|
126
|
+
warnings: allWarnings,
|
|
127
|
+
unsupported: emission.unsupported,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Standard single-pass emit (no rewriting)
|
|
131
|
+
const emission = await targetPlugin.emit(discovery.items, effectiveTargetRoot, {
|
|
74
132
|
dryRun: options.dryRun,
|
|
75
133
|
});
|
|
76
134
|
return {
|
|
77
135
|
discovered: discovery.items,
|
|
78
136
|
written: emission.written,
|
|
79
|
-
warnings: [...
|
|
137
|
+
warnings: [...allWarnings, ...emission.warnings],
|
|
80
138
|
unsupported: emission.unsupported,
|
|
81
139
|
};
|
|
82
140
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { AgentCustomization, Warning } from '@a16njs/models';
|
|
2
|
+
import type { WrittenFile } from '@a16njs/models';
|
|
3
|
+
/**
|
|
4
|
+
* A mapping from source-relative paths to target-relative paths.
|
|
5
|
+
* Used to rewrite path references in content during format conversion.
|
|
6
|
+
*/
|
|
7
|
+
export type PathMapping = Map<string, string>;
|
|
8
|
+
/**
|
|
9
|
+
* Result of a path rewriting operation.
|
|
10
|
+
*/
|
|
11
|
+
export interface RewriteResult {
|
|
12
|
+
/** The items with rewritten content (cloned; originals are not mutated) */
|
|
13
|
+
items: AgentCustomization[];
|
|
14
|
+
/** Count of replacements made across all items */
|
|
15
|
+
replacementCount: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build a mapping from source-relative paths to target-relative paths.
|
|
19
|
+
*
|
|
20
|
+
* Given the discovered items (with sourcePaths relative to sourceRoot)
|
|
21
|
+
* and the written files (with absolute paths under targetRoot), produces
|
|
22
|
+
* a Map where keys are source-relative paths and values are target-relative paths.
|
|
23
|
+
*
|
|
24
|
+
* The mapping is derived from the `sourceItems` array on each WrittenFile,
|
|
25
|
+
* which links each output file back to the source AgentCustomization(s) that
|
|
26
|
+
* produced it. This handles merges, extension changes, and directory flattening
|
|
27
|
+
* naturally because the mapping is derived from actual emit output.
|
|
28
|
+
*
|
|
29
|
+
* @param _discovered - Items discovered from the source plugin (unused directly; mapping comes from written.sourceItems)
|
|
30
|
+
* @param written - Files written (or planned) by the target plugin
|
|
31
|
+
* @param _sourceRoot - Root directory used for discovery (unused; sourcePaths are already relative)
|
|
32
|
+
* @param targetRoot - Root directory used for emission
|
|
33
|
+
* @returns A map of source-relative path to target-relative path
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildMapping(_discovered: AgentCustomization[], written: WrittenFile[], _sourceRoot: string, targetRoot: string): PathMapping;
|
|
36
|
+
/**
|
|
37
|
+
* Rewrite file path references in item content.
|
|
38
|
+
*
|
|
39
|
+
* For each item, every occurrence of a mapped source path in the content
|
|
40
|
+
* is replaced with the corresponding target path. Replacements are applied
|
|
41
|
+
* longest-first to prevent partial match corruption (e.g., replacing
|
|
42
|
+
* "foo/bar.mdc.bak" before "foo/bar.mdc").
|
|
43
|
+
*
|
|
44
|
+
* Items are cloned before modification; originals are not mutated.
|
|
45
|
+
*
|
|
46
|
+
* @param items - The items whose content should be rewritten
|
|
47
|
+
* @param mapping - The source-to-target path mapping
|
|
48
|
+
* @returns Cloned items with rewritten content and replacement count
|
|
49
|
+
*/
|
|
50
|
+
export declare function rewriteContent(items: AgentCustomization[], mapping: PathMapping): RewriteResult;
|
|
51
|
+
/**
|
|
52
|
+
* Detect orphan path references in item content.
|
|
53
|
+
*
|
|
54
|
+
* An "orphan" is a string in content that looks like it could be a
|
|
55
|
+
* source-format file path (matches known source plugin path prefixes
|
|
56
|
+
* and file extensions) but is NOT present in the mapping (i.e., it
|
|
57
|
+
* wasn't converted).
|
|
58
|
+
*
|
|
59
|
+
* @param items - The items to scan for orphan references
|
|
60
|
+
* @param mapping - The source-to-target path mapping
|
|
61
|
+
* @param sourcePluginPrefixes - Known directory prefixes for the source format
|
|
62
|
+
* (e.g., ['.cursor/rules/', '.cursor/skills/'] for cursor plugin)
|
|
63
|
+
* @param sourceExtensions - Known file extensions for the source format
|
|
64
|
+
* (e.g., ['.mdc', '.md'] for cursor plugin)
|
|
65
|
+
* @returns Warnings about orphan references found
|
|
66
|
+
*/
|
|
67
|
+
export declare function detectOrphans(items: AgentCustomization[], mapping: PathMapping, sourcePluginPrefixes: string[], sourceExtensions: string[]): Warning[];
|
|
68
|
+
//# sourceMappingURL=path-rewriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-rewriter.d.ts","sourceRoot":"","sources":["../src/path-rewriter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,kDAAkD;IAClD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,kBAAkB,EAAE,EACjC,OAAO,EAAE,WAAW,EAAE,EACtB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,WAAW,CAoBb;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,OAAO,EAAE,WAAW,GACnB,aAAa,CAgDf;AASD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,OAAO,EAAE,WAAW,EACpB,oBAAoB,EAAE,MAAM,EAAE,EAC9B,gBAAgB,EAAE,MAAM,EAAE,GACzB,OAAO,EAAE,CAmCX"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { WarningCode } from '@a16njs/models';
|
|
3
|
+
/**
|
|
4
|
+
* Build a mapping from source-relative paths to target-relative paths.
|
|
5
|
+
*
|
|
6
|
+
* Given the discovered items (with sourcePaths relative to sourceRoot)
|
|
7
|
+
* and the written files (with absolute paths under targetRoot), produces
|
|
8
|
+
* a Map where keys are source-relative paths and values are target-relative paths.
|
|
9
|
+
*
|
|
10
|
+
* The mapping is derived from the `sourceItems` array on each WrittenFile,
|
|
11
|
+
* which links each output file back to the source AgentCustomization(s) that
|
|
12
|
+
* produced it. This handles merges, extension changes, and directory flattening
|
|
13
|
+
* naturally because the mapping is derived from actual emit output.
|
|
14
|
+
*
|
|
15
|
+
* @param _discovered - Items discovered from the source plugin (unused directly; mapping comes from written.sourceItems)
|
|
16
|
+
* @param written - Files written (or planned) by the target plugin
|
|
17
|
+
* @param _sourceRoot - Root directory used for discovery (unused; sourcePaths are already relative)
|
|
18
|
+
* @param targetRoot - Root directory used for emission
|
|
19
|
+
* @returns A map of source-relative path to target-relative path
|
|
20
|
+
*/
|
|
21
|
+
export function buildMapping(_discovered, written, _sourceRoot, targetRoot) {
|
|
22
|
+
const mapping = new Map();
|
|
23
|
+
for (const file of written) {
|
|
24
|
+
// Compute target-relative path using POSIX separators for consistency
|
|
25
|
+
const targetRelative = path.relative(targetRoot, file.path).split(path.sep).join('/');
|
|
26
|
+
// Map each source item's sourcePath to this target path
|
|
27
|
+
if (file.sourceItems) {
|
|
28
|
+
for (const sourceItem of file.sourceItems) {
|
|
29
|
+
if (sourceItem.sourcePath) {
|
|
30
|
+
// Normalize to POSIX separators
|
|
31
|
+
const normalizedSourcePath = sourceItem.sourcePath.split(path.sep).join('/');
|
|
32
|
+
mapping.set(normalizedSourcePath, targetRelative);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return mapping;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Rewrite file path references in item content.
|
|
41
|
+
*
|
|
42
|
+
* For each item, every occurrence of a mapped source path in the content
|
|
43
|
+
* is replaced with the corresponding target path. Replacements are applied
|
|
44
|
+
* longest-first to prevent partial match corruption (e.g., replacing
|
|
45
|
+
* "foo/bar.mdc.bak" before "foo/bar.mdc").
|
|
46
|
+
*
|
|
47
|
+
* Items are cloned before modification; originals are not mutated.
|
|
48
|
+
*
|
|
49
|
+
* @param items - The items whose content should be rewritten
|
|
50
|
+
* @param mapping - The source-to-target path mapping
|
|
51
|
+
* @returns Cloned items with rewritten content and replacement count
|
|
52
|
+
*/
|
|
53
|
+
export function rewriteContent(items, mapping) {
|
|
54
|
+
if (mapping.size === 0) {
|
|
55
|
+
// No mapping → return clones with no changes
|
|
56
|
+
return {
|
|
57
|
+
items: items.map((item) => ({ ...item })),
|
|
58
|
+
replacementCount: 0,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Sort replacements longest-first to prevent partial match corruption
|
|
62
|
+
const sortedEntries = Array.from(mapping.entries()).sort((a, b) => b[0].length - a[0].length);
|
|
63
|
+
let totalReplacements = 0;
|
|
64
|
+
const rewrittenItems = items.map((item) => {
|
|
65
|
+
// Clone the item (shallow clone is sufficient since we only modify content)
|
|
66
|
+
const clone = { ...item };
|
|
67
|
+
let content = clone.content;
|
|
68
|
+
// NOTE: Replacements are applied sequentially. This is safe because source
|
|
69
|
+
// and target paths use different plugin directory prefixes, so a target path
|
|
70
|
+
// won't match a subsequent source key.
|
|
71
|
+
for (const [sourcePath, targetPath] of sortedEntries) {
|
|
72
|
+
// Count occurrences before replacement
|
|
73
|
+
let count = 0;
|
|
74
|
+
let idx = 0;
|
|
75
|
+
while ((idx = content.indexOf(sourcePath, idx)) !== -1) {
|
|
76
|
+
count++;
|
|
77
|
+
idx += sourcePath.length;
|
|
78
|
+
}
|
|
79
|
+
if (count > 0) {
|
|
80
|
+
// Use split+join for exact string replacement (no regex escaping needed)
|
|
81
|
+
content = content.split(sourcePath).join(targetPath);
|
|
82
|
+
totalReplacements += count;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
clone.content = content;
|
|
86
|
+
return clone;
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
items: rewrittenItems,
|
|
90
|
+
replacementCount: totalReplacements,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Escape a string for use in a RegExp.
|
|
95
|
+
*/
|
|
96
|
+
function escapeRegExp(str) {
|
|
97
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Detect orphan path references in item content.
|
|
101
|
+
*
|
|
102
|
+
* An "orphan" is a string in content that looks like it could be a
|
|
103
|
+
* source-format file path (matches known source plugin path prefixes
|
|
104
|
+
* and file extensions) but is NOT present in the mapping (i.e., it
|
|
105
|
+
* wasn't converted).
|
|
106
|
+
*
|
|
107
|
+
* @param items - The items to scan for orphan references
|
|
108
|
+
* @param mapping - The source-to-target path mapping
|
|
109
|
+
* @param sourcePluginPrefixes - Known directory prefixes for the source format
|
|
110
|
+
* (e.g., ['.cursor/rules/', '.cursor/skills/'] for cursor plugin)
|
|
111
|
+
* @param sourceExtensions - Known file extensions for the source format
|
|
112
|
+
* (e.g., ['.mdc', '.md'] for cursor plugin)
|
|
113
|
+
* @returns Warnings about orphan references found
|
|
114
|
+
*/
|
|
115
|
+
export function detectOrphans(items, mapping, sourcePluginPrefixes, sourceExtensions) {
|
|
116
|
+
if (sourcePluginPrefixes.length === 0 || sourceExtensions.length === 0) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
const warnings = [];
|
|
120
|
+
const mappedPaths = new Set(mapping.keys());
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
// Build a regex that matches: <prefix><non-whitespace-path><extension>
|
|
123
|
+
// e.g., .cursor/rules/some-file.mdc
|
|
124
|
+
const escapedPrefixes = sourcePluginPrefixes.map(escapeRegExp).join('|');
|
|
125
|
+
const escapedExtensions = sourceExtensions.map(escapeRegExp).join('|');
|
|
126
|
+
const pattern = new RegExp(`(?:${escapedPrefixes})[^\\s)\\]}>,"']+(?:${escapedExtensions})`, 'g');
|
|
127
|
+
for (const item of items) {
|
|
128
|
+
const matches = item.content.matchAll(pattern);
|
|
129
|
+
for (const match of matches) {
|
|
130
|
+
const foundPath = match[0];
|
|
131
|
+
const key = `${item.sourcePath ?? ''}::${foundPath}`;
|
|
132
|
+
if (!mappedPaths.has(foundPath) && !seen.has(key)) {
|
|
133
|
+
seen.add(key);
|
|
134
|
+
warnings.push({
|
|
135
|
+
code: WarningCode.OrphanPathRef,
|
|
136
|
+
message: `Orphan path reference: '${foundPath}' is not in the conversion set`,
|
|
137
|
+
sources: item.sourcePath ? [item.sourcePath] : undefined,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return warnings;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=path-rewriter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-rewriter.js","sourceRoot":"","sources":["../src/path-rewriter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a16njs/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Conversion engine for a16n",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"author": "Texarkanine",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@a16njs/models": "0.
|
|
34
|
+
"@a16njs/models": "0.9.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^20.0.0",
|
|
38
38
|
"typescript": "^5.4.0",
|
|
39
39
|
"vitest": "^2.0.0",
|
|
40
|
-
"@a16njs/plugin-cursor": "0.
|
|
41
|
-
"@a16njs/plugin-claude": "0.
|
|
40
|
+
"@a16njs/plugin-cursor": "0.9.0",
|
|
41
|
+
"@a16njs/plugin-claude": "0.9.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc",
|