@a16njs/engine 0.5.0 → 0.6.1
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 +41 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -75
- package/dist/index.js.map +1 -1
- package/dist/plugin-discovery.d.ts +78 -0
- package/dist/plugin-discovery.d.ts.map +1 -0
- package/dist/plugin-discovery.js +203 -0
- package/dist/plugin-discovery.js.map +1 -0
- package/dist/plugin-loader.d.ts +96 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +112 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/plugin-registry.d.ts +94 -0
- package/dist/plugin-registry.d.ts.map +1 -0
- package/dist/plugin-registry.js +89 -0
- package/dist/plugin-registry.js.map +1 -0
- package/dist/transformation.d.ts +93 -0
- package/dist/transformation.d.ts.map +1 -0
- package/dist/transformation.js +56 -0
- package/dist/transformation.js.map +1 -0
- package/dist/workspace.d.ts +73 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +178 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType } from '@a16njs/models';
|
|
1
|
+
import type { A16nPlugin, AgentCustomization, DiscoveryResult, Warning, WrittenFile, CustomizationType, Workspace } from '@a16njs/models';
|
|
2
|
+
import { type PluginDiscoveryOptions } from './plugin-discovery.js';
|
|
3
|
+
import { type ContentTransformation } from './transformation.js';
|
|
2
4
|
/**
|
|
3
5
|
* Options for a conversion operation.
|
|
4
6
|
*/
|
|
@@ -7,25 +9,25 @@ export interface ConversionOptions {
|
|
|
7
9
|
source: string;
|
|
8
10
|
/** Target plugin ID */
|
|
9
11
|
target: string;
|
|
10
|
-
/** Project root directory
|
|
12
|
+
/** Project root directory */
|
|
11
13
|
root: string;
|
|
12
14
|
/** If true, only discover without writing */
|
|
13
15
|
dryRun?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Override root directory for discovery (reading).
|
|
16
|
-
* When set, discover() uses this instead of `root`.
|
|
17
|
-
*/
|
|
16
|
+
/** Override root for discovery (source plugin) */
|
|
18
17
|
sourceRoot?: string;
|
|
19
|
-
/**
|
|
20
|
-
* Override root directory for emission (writing).
|
|
21
|
-
* When set, emit() uses this instead of `root`.
|
|
22
|
-
*/
|
|
18
|
+
/** Override root for emission (target plugin) */
|
|
23
19
|
targetRoot?: string;
|
|
20
|
+
/** Workspace for source discovery (takes precedence over sourceRoot/root) */
|
|
21
|
+
sourceWorkspace?: Workspace;
|
|
22
|
+
/** Workspace for target emission (takes precedence over targetRoot/root) */
|
|
23
|
+
targetWorkspace?: Workspace;
|
|
24
24
|
/**
|
|
25
|
-
* If true, rewrite
|
|
26
|
-
*
|
|
25
|
+
* If true, rewrite path references in content during conversion.
|
|
26
|
+
* @deprecated Use `transformations: [new PathRewritingTransformation()]` instead.
|
|
27
27
|
*/
|
|
28
28
|
rewritePathRefs?: boolean;
|
|
29
|
+
/** Content transformations to apply between discovery and emission */
|
|
30
|
+
transformations?: ContentTransformation[];
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
31
33
|
* Git-ignore change information.
|
|
@@ -62,22 +64,44 @@ export interface PluginInfo {
|
|
|
62
64
|
supports: CustomizationType[];
|
|
63
65
|
source: 'bundled' | 'installed';
|
|
64
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Result of discovering and registering plugins.
|
|
69
|
+
*/
|
|
70
|
+
export interface DiscoverAndRegisterResult {
|
|
71
|
+
/** Plugin IDs that were successfully registered */
|
|
72
|
+
registered: string[];
|
|
73
|
+
/** Plugin IDs that were skipped (already registered) */
|
|
74
|
+
skipped: string[];
|
|
75
|
+
/** Errors encountered during discovery */
|
|
76
|
+
errors: Array<{
|
|
77
|
+
packageName: string;
|
|
78
|
+
error: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
65
81
|
/**
|
|
66
82
|
* The a16n conversion engine.
|
|
67
83
|
* Orchestrates plugins to discover and emit agent customizations.
|
|
68
84
|
*/
|
|
69
85
|
export declare class A16nEngine {
|
|
70
|
-
private
|
|
86
|
+
private registry;
|
|
87
|
+
private loader;
|
|
71
88
|
/**
|
|
72
89
|
* Create a new engine with the given plugins.
|
|
73
|
-
* @param plugins - Plugins to register
|
|
90
|
+
* @param plugins - Plugins to register (registered as 'bundled')
|
|
74
91
|
*/
|
|
75
92
|
constructor(plugins?: A16nPlugin[]);
|
|
76
93
|
/**
|
|
77
94
|
* Register a plugin with the engine.
|
|
78
95
|
* @param plugin - The plugin to register
|
|
96
|
+
* @param source - Whether the plugin is bundled or installed
|
|
97
|
+
*/
|
|
98
|
+
registerPlugin(plugin: A16nPlugin, source?: 'bundled' | 'installed'): void;
|
|
99
|
+
/**
|
|
100
|
+
* Discover and register installed plugins from node_modules.
|
|
101
|
+
* @param options - Plugin discovery options
|
|
102
|
+
* @returns Result with registered, skipped, and error info
|
|
79
103
|
*/
|
|
80
|
-
|
|
104
|
+
discoverAndRegisterPlugins(options?: PluginDiscoveryOptions): Promise<DiscoverAndRegisterResult>;
|
|
81
105
|
/**
|
|
82
106
|
* List all registered plugins.
|
|
83
107
|
* @returns Array of plugin info
|
|
@@ -92,10 +116,10 @@ export declare class A16nEngine {
|
|
|
92
116
|
/**
|
|
93
117
|
* Discover customizations using a specific plugin.
|
|
94
118
|
* @param pluginId - The plugin to use for discovery
|
|
95
|
-
* @param
|
|
119
|
+
* @param rootOrWorkspace - The project root path or Workspace to scan
|
|
96
120
|
* @returns Discovery result with items and warnings
|
|
97
121
|
*/
|
|
98
|
-
discover(pluginId: string,
|
|
122
|
+
discover(pluginId: string, rootOrWorkspace: string | Workspace): Promise<DiscoveryResult>;
|
|
99
123
|
/**
|
|
100
124
|
* Convert customizations from one format to another.
|
|
101
125
|
* @param options - Conversion options
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,54 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
import { PluginRegistry } from './plugin-registry.js';
|
|
2
|
+
import { PluginLoader, PluginConflictStrategy } from './plugin-loader.js';
|
|
3
|
+
import { LocalWorkspace } from './workspace.js';
|
|
4
|
+
import { PathRewritingTransformation, } from './transformation.js';
|
|
20
5
|
/**
|
|
21
6
|
* The a16n conversion engine.
|
|
22
7
|
* Orchestrates plugins to discover and emit agent customizations.
|
|
23
8
|
*/
|
|
24
9
|
export class A16nEngine {
|
|
25
|
-
|
|
10
|
+
registry = new PluginRegistry();
|
|
11
|
+
loader;
|
|
26
12
|
/**
|
|
27
13
|
* Create a new engine with the given plugins.
|
|
28
|
-
* @param plugins - Plugins to register
|
|
14
|
+
* @param plugins - Plugins to register (registered as 'bundled')
|
|
29
15
|
*/
|
|
30
16
|
constructor(plugins = []) {
|
|
17
|
+
this.loader = new PluginLoader(PluginConflictStrategy.PREFER_BUNDLED);
|
|
31
18
|
for (const plugin of plugins) {
|
|
32
|
-
this.registerPlugin(plugin);
|
|
19
|
+
this.registerPlugin(plugin, 'bundled');
|
|
33
20
|
}
|
|
34
21
|
}
|
|
35
22
|
/**
|
|
36
23
|
* Register a plugin with the engine.
|
|
37
24
|
* @param plugin - The plugin to register
|
|
25
|
+
* @param source - Whether the plugin is bundled or installed
|
|
26
|
+
*/
|
|
27
|
+
registerPlugin(plugin, source = 'bundled') {
|
|
28
|
+
this.registry.register({ plugin, source });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Discover and register installed plugins from node_modules.
|
|
32
|
+
* @param options - Plugin discovery options
|
|
33
|
+
* @returns Result with registered, skipped, and error info
|
|
38
34
|
*/
|
|
39
|
-
|
|
40
|
-
this.
|
|
35
|
+
async discoverAndRegisterPlugins(options) {
|
|
36
|
+
const candidates = await this.loader.loadInstalled(options);
|
|
37
|
+
const resolved = this.loader.resolveConflicts(this.registry, candidates);
|
|
38
|
+
const registered = [];
|
|
39
|
+
for (const reg of resolved.loaded) {
|
|
40
|
+
this.registry.register(reg);
|
|
41
|
+
registered.push(reg.plugin.id);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
registered,
|
|
45
|
+
skipped: resolved.skipped.map((s) => s.plugin.id),
|
|
46
|
+
errors: resolved.errors,
|
|
47
|
+
};
|
|
41
48
|
}
|
|
42
49
|
/**
|
|
43
50
|
* List all registered plugins.
|
|
44
51
|
* @returns Array of plugin info
|
|
45
52
|
*/
|
|
46
53
|
listPlugins() {
|
|
47
|
-
return
|
|
48
|
-
id:
|
|
49
|
-
name:
|
|
50
|
-
supports:
|
|
51
|
-
source:
|
|
54
|
+
return this.registry.list().map((r) => ({
|
|
55
|
+
id: r.plugin.id,
|
|
56
|
+
name: r.plugin.name,
|
|
57
|
+
supports: r.plugin.supports,
|
|
58
|
+
source: r.source,
|
|
52
59
|
}));
|
|
53
60
|
}
|
|
54
61
|
/**
|
|
@@ -57,20 +64,23 @@ export class A16nEngine {
|
|
|
57
64
|
* @returns The plugin or undefined if not found
|
|
58
65
|
*/
|
|
59
66
|
getPlugin(id) {
|
|
60
|
-
return this.
|
|
67
|
+
return this.registry.getPlugin(id);
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
70
|
* Discover customizations using a specific plugin.
|
|
64
71
|
* @param pluginId - The plugin to use for discovery
|
|
65
|
-
* @param
|
|
72
|
+
* @param rootOrWorkspace - The project root path or Workspace to scan
|
|
66
73
|
* @returns Discovery result with items and warnings
|
|
67
74
|
*/
|
|
68
|
-
async discover(pluginId,
|
|
75
|
+
async discover(pluginId, rootOrWorkspace) {
|
|
69
76
|
const plugin = this.getPlugin(pluginId);
|
|
70
77
|
if (!plugin) {
|
|
71
78
|
throw new Error(`Unknown plugin: ${pluginId}`);
|
|
72
79
|
}
|
|
73
|
-
|
|
80
|
+
const workspace = typeof rootOrWorkspace === 'string'
|
|
81
|
+
? new LocalWorkspace('discover', rootOrWorkspace)
|
|
82
|
+
: rootOrWorkspace;
|
|
83
|
+
return plugin.discover(workspace);
|
|
74
84
|
}
|
|
75
85
|
/**
|
|
76
86
|
* Convert customizations from one format to another.
|
|
@@ -86,55 +96,52 @@ export class A16nEngine {
|
|
|
86
96
|
if (!targetPlugin) {
|
|
87
97
|
throw new Error(`Unknown target: ${options.target}`);
|
|
88
98
|
}
|
|
89
|
-
// Resolve
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
99
|
+
// Resolve source and target roots/workspaces
|
|
100
|
+
// Priority: explicit workspace > explicit root override > default root
|
|
101
|
+
const discoverRoot = options.sourceRoot ?? options.root;
|
|
102
|
+
const emitRoot = options.targetRoot ?? options.root;
|
|
103
|
+
const sourceWorkspace = options.sourceWorkspace
|
|
104
|
+
?? new LocalWorkspace('source', discoverRoot);
|
|
105
|
+
const targetWorkspace = options.targetWorkspace
|
|
106
|
+
?? new LocalWorkspace('target', emitRoot);
|
|
107
|
+
// Discover from source using workspace
|
|
108
|
+
const discovery = await sourcePlugin.discover(sourceWorkspace);
|
|
109
|
+
// Collect warnings
|
|
110
|
+
const warnings = [...discovery.warnings];
|
|
111
|
+
// Build transformations list
|
|
112
|
+
const transformations = options.transformations
|
|
113
|
+
? [...options.transformations]
|
|
114
|
+
: [];
|
|
115
|
+
// Backward compatibility: rewritePathRefs maps to PathRewritingTransformation
|
|
116
|
+
if (options.rewritePathRefs && !transformations.some((t) => t.id === 'path-rewriting')) {
|
|
117
|
+
transformations.push(new PathRewritingTransformation());
|
|
118
|
+
}
|
|
119
|
+
// Apply transformation pipeline
|
|
120
|
+
let itemsToEmit = discovery.items;
|
|
121
|
+
if (transformations.length > 0) {
|
|
122
|
+
for (const transformation of transformations) {
|
|
123
|
+
const transformContext = {
|
|
124
|
+
items: itemsToEmit,
|
|
125
|
+
sourcePlugin,
|
|
126
|
+
targetPlugin,
|
|
127
|
+
sourceRoot: sourceWorkspace.root,
|
|
128
|
+
targetRoot: targetWorkspace.root,
|
|
129
|
+
trialEmit: (items) => targetPlugin.emit(items, targetWorkspace, { dryRun: true }),
|
|
130
|
+
};
|
|
131
|
+
const result = await transformation.transform(transformContext);
|
|
132
|
+
itemsToEmit = result.items;
|
|
133
|
+
warnings.push(...result.warnings);
|
|
122
134
|
}
|
|
123
|
-
return {
|
|
124
|
-
discovered: discovery.items,
|
|
125
|
-
written: emission.written,
|
|
126
|
-
warnings: allWarnings,
|
|
127
|
-
unsupported: emission.unsupported,
|
|
128
|
-
};
|
|
129
135
|
}
|
|
130
|
-
//
|
|
131
|
-
const emission = await targetPlugin.emit(
|
|
136
|
+
// Single emission at the end using workspace
|
|
137
|
+
const emission = await targetPlugin.emit(itemsToEmit, targetWorkspace, {
|
|
132
138
|
dryRun: options.dryRun,
|
|
133
139
|
});
|
|
140
|
+
warnings.push(...emission.warnings);
|
|
134
141
|
return {
|
|
135
142
|
discovered: discovery.items,
|
|
136
143
|
written: emission.written,
|
|
137
|
-
warnings
|
|
144
|
+
warnings,
|
|
138
145
|
unsupported: emission.unsupported,
|
|
139
146
|
};
|
|
140
147
|
}
|
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,78 @@
|
|
|
1
|
+
import type { A16nPlugin } from '@a16njs/models';
|
|
2
|
+
/**
|
|
3
|
+
* Options for controlling where plugin discovery searches.
|
|
4
|
+
*/
|
|
5
|
+
export interface PluginDiscoveryOptions {
|
|
6
|
+
/** Custom search paths to scan for plugins. If omitted, uses getDefaultSearchPaths(). */
|
|
7
|
+
searchPaths?: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Result of a plugin discovery scan.
|
|
11
|
+
*/
|
|
12
|
+
export interface PluginDiscoveryResult {
|
|
13
|
+
/** Successfully loaded and validated plugins. */
|
|
14
|
+
plugins: A16nPlugin[];
|
|
15
|
+
/** Packages that were found but failed to load or validate. */
|
|
16
|
+
errors: PluginLoadError[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Describes a plugin package that was found but could not be loaded.
|
|
20
|
+
*/
|
|
21
|
+
export interface PluginLoadError {
|
|
22
|
+
/** The npm package name (e.g. "a16n-plugin-foo"). */
|
|
23
|
+
packageName: string;
|
|
24
|
+
/** Human-readable description of what went wrong. */
|
|
25
|
+
error: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Scan search paths for installed `a16n-plugin-*` packages, dynamically import
|
|
29
|
+
* each one, validate its default export, and return the valid plugins along
|
|
30
|
+
* with error info for any that failed.
|
|
31
|
+
*
|
|
32
|
+
* @param options - Optional search path overrides
|
|
33
|
+
* @returns Discovered plugins and any load errors
|
|
34
|
+
*/
|
|
35
|
+
export declare function discoverInstalledPlugins(options?: PluginDiscoveryOptions): Promise<PluginDiscoveryResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Type-guard that checks whether an unknown value satisfies the A16nPlugin interface.
|
|
38
|
+
*
|
|
39
|
+
* Validates presence and types of: id (string), name (string), supports (array),
|
|
40
|
+
* discover (function), emit (function).
|
|
41
|
+
*
|
|
42
|
+
* @param obj - The value to check
|
|
43
|
+
* @returns True if obj is a valid A16nPlugin
|
|
44
|
+
*/
|
|
45
|
+
export declare function isValidPlugin(obj: unknown): obj is A16nPlugin;
|
|
46
|
+
/**
|
|
47
|
+
* Compute the default search paths for plugin discovery.
|
|
48
|
+
*
|
|
49
|
+
* Returns paths where `a16n-plugin-*` packages might be installed:
|
|
50
|
+
* - The global npm `node_modules` directory (derived from this package's location)
|
|
51
|
+
* - The local `node_modules` in the current working directory
|
|
52
|
+
*
|
|
53
|
+
* In a global install, the engine lives inside node_modules:
|
|
54
|
+
* .../lib/node_modules/@a16njs/engine/dist/plugin-discovery.js
|
|
55
|
+
* so walking up finds the node_modules parent directly.
|
|
56
|
+
*
|
|
57
|
+
* In a monorepo (e.g. pnpm workspace), the engine lives at:
|
|
58
|
+
* <root>/packages/engine/dist/plugin-discovery.js
|
|
59
|
+
* so we also check for a node_modules child directory at each level.
|
|
60
|
+
*
|
|
61
|
+
* @returns Array of directory paths to scan
|
|
62
|
+
*/
|
|
63
|
+
export declare function getDefaultSearchPaths(): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Derive the global `node_modules` directory from `process.argv[1]`.
|
|
66
|
+
*
|
|
67
|
+
* When a CLI binary is installed globally (including via `npm link`),
|
|
68
|
+
* `process.argv[1]` points to `PREFIX/bin/<cmd>`. From that we can derive
|
|
69
|
+
* `PREFIX/lib/node_modules` (Unix) or `PREFIX/node_modules` (Windows).
|
|
70
|
+
*
|
|
71
|
+
* This handles the case where the engine is symlinked from a monorepo
|
|
72
|
+
* and `import.meta.url` resolves to the real path rather than the global
|
|
73
|
+
* `node_modules` tree.
|
|
74
|
+
*
|
|
75
|
+
* @returns The global node_modules path, or null if it cannot be determined
|
|
76
|
+
*/
|
|
77
|
+
export declare function getGlobalNodeModulesFromArgv1(): string | null;
|
|
78
|
+
//# sourceMappingURL=plugin-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-discovery.d.ts","sourceRoot":"","sources":["../src/plugin-discovery.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yFAAyF;IACzF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;CACf;AAKD;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAyDhC;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAY7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAuChD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,GAAG,IAAI,CAsB7D"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import { statSync } from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { pathToFileURL, fileURLToPath } from 'url';
|
|
5
|
+
/** Pattern used to match plugin package directory names. */
|
|
6
|
+
const PLUGIN_PREFIX = 'a16n-plugin-';
|
|
7
|
+
/**
|
|
8
|
+
* Scan search paths for installed `a16n-plugin-*` packages, dynamically import
|
|
9
|
+
* each one, validate its default export, and return the valid plugins along
|
|
10
|
+
* with error info for any that failed.
|
|
11
|
+
*
|
|
12
|
+
* @param options - Optional search path overrides
|
|
13
|
+
* @returns Discovered plugins and any load errors
|
|
14
|
+
*/
|
|
15
|
+
export async function discoverInstalledPlugins(options) {
|
|
16
|
+
const searchPaths = options?.searchPaths ?? getDefaultSearchPaths();
|
|
17
|
+
const plugins = [];
|
|
18
|
+
const errors = [];
|
|
19
|
+
const seenPluginIds = new Set();
|
|
20
|
+
for (const searchPath of searchPaths) {
|
|
21
|
+
// Read directory entries; skip if path doesn't exist
|
|
22
|
+
let entries;
|
|
23
|
+
try {
|
|
24
|
+
entries = await fs.readdir(searchPath);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Non-existent or unreadable path — skip silently
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Filter for directories matching the plugin naming convention
|
|
31
|
+
const pluginDirs = entries.filter((name) => name.startsWith(PLUGIN_PREFIX));
|
|
32
|
+
for (const dirName of pluginDirs) {
|
|
33
|
+
const pkgPath = path.join(searchPath, dirName);
|
|
34
|
+
try {
|
|
35
|
+
// Resolve entry point from package.json main field, falling back to index.js
|
|
36
|
+
const entryFile = await resolvePluginEntry(pkgPath);
|
|
37
|
+
const moduleUrl = pathToFileURL(entryFile).href;
|
|
38
|
+
const mod = await import(moduleUrl);
|
|
39
|
+
// Extract default export (handles both `export default` and CJS interop)
|
|
40
|
+
const candidate = mod.default ?? mod;
|
|
41
|
+
if (isValidPlugin(candidate)) {
|
|
42
|
+
if (seenPluginIds.has(candidate.id)) {
|
|
43
|
+
errors.push({
|
|
44
|
+
packageName: dirName,
|
|
45
|
+
error: `Duplicate plugin id: ${candidate.id} — already discovered`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
seenPluginIds.add(candidate.id);
|
|
50
|
+
plugins.push(candidate);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
errors.push({
|
|
55
|
+
packageName: dirName,
|
|
56
|
+
error: `Invalid plugin export: missing or incorrect required fields (id, name, supports, discover, emit)`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
errors.push({
|
|
62
|
+
packageName: dirName,
|
|
63
|
+
error: err instanceof Error ? err.message : String(err),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { plugins, errors };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the JavaScript entry point for a plugin package directory.
|
|
72
|
+
*
|
|
73
|
+
* Reads the `main` field from the package's `package.json` if it exists,
|
|
74
|
+
* otherwise falls back to `index.js` at the package root.
|
|
75
|
+
*
|
|
76
|
+
* @param pkgPath - Absolute path to the plugin package directory
|
|
77
|
+
* @returns Absolute path to the entry JavaScript file
|
|
78
|
+
*/
|
|
79
|
+
async function resolvePluginEntry(pkgPath) {
|
|
80
|
+
try {
|
|
81
|
+
const raw = await fs.readFile(path.join(pkgPath, 'package.json'), 'utf-8');
|
|
82
|
+
const pkg = JSON.parse(raw);
|
|
83
|
+
if (typeof pkg.main === 'string' && pkg.main.length > 0) {
|
|
84
|
+
return path.resolve(pkgPath, pkg.main);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// No package.json or unreadable — fall through to default
|
|
89
|
+
}
|
|
90
|
+
return path.join(pkgPath, 'index.js');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Type-guard that checks whether an unknown value satisfies the A16nPlugin interface.
|
|
94
|
+
*
|
|
95
|
+
* Validates presence and types of: id (string), name (string), supports (array),
|
|
96
|
+
* discover (function), emit (function).
|
|
97
|
+
*
|
|
98
|
+
* @param obj - The value to check
|
|
99
|
+
* @returns True if obj is a valid A16nPlugin
|
|
100
|
+
*/
|
|
101
|
+
export function isValidPlugin(obj) {
|
|
102
|
+
if (obj == null || typeof obj !== 'object') {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const candidate = obj;
|
|
106
|
+
return (typeof candidate.id === 'string' &&
|
|
107
|
+
typeof candidate.name === 'string' &&
|
|
108
|
+
Array.isArray(candidate.supports) &&
|
|
109
|
+
typeof candidate.discover === 'function' &&
|
|
110
|
+
typeof candidate.emit === 'function');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Compute the default search paths for plugin discovery.
|
|
114
|
+
*
|
|
115
|
+
* Returns paths where `a16n-plugin-*` packages might be installed:
|
|
116
|
+
* - The global npm `node_modules` directory (derived from this package's location)
|
|
117
|
+
* - The local `node_modules` in the current working directory
|
|
118
|
+
*
|
|
119
|
+
* In a global install, the engine lives inside node_modules:
|
|
120
|
+
* .../lib/node_modules/@a16njs/engine/dist/plugin-discovery.js
|
|
121
|
+
* so walking up finds the node_modules parent directly.
|
|
122
|
+
*
|
|
123
|
+
* In a monorepo (e.g. pnpm workspace), the engine lives at:
|
|
124
|
+
* <root>/packages/engine/dist/plugin-discovery.js
|
|
125
|
+
* so we also check for a node_modules child directory at each level.
|
|
126
|
+
*
|
|
127
|
+
* @returns Array of directory paths to scan
|
|
128
|
+
*/
|
|
129
|
+
export function getDefaultSearchPaths() {
|
|
130
|
+
const paths = [];
|
|
131
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
132
|
+
let dir = path.dirname(thisFile);
|
|
133
|
+
while (dir !== path.dirname(dir)) {
|
|
134
|
+
// Global install: this file is inside a node_modules tree
|
|
135
|
+
if (path.basename(dir) === 'node_modules') {
|
|
136
|
+
paths.push(dir);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
// Monorepo / local dev: check for a sibling node_modules directory.
|
|
140
|
+
// Don't stop — keep walking up to find all ancestor node_modules
|
|
141
|
+
// (e.g. packages/engine/node_modules AND root/node_modules).
|
|
142
|
+
const siblingNodeModules = path.join(dir, 'node_modules');
|
|
143
|
+
try {
|
|
144
|
+
const stat = statSync(siblingNodeModules);
|
|
145
|
+
if (stat.isDirectory()) {
|
|
146
|
+
paths.push(siblingNodeModules);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Directory doesn't exist, keep walking
|
|
151
|
+
}
|
|
152
|
+
dir = path.dirname(dir);
|
|
153
|
+
}
|
|
154
|
+
// Global node_modules derived from process.argv[1] (handles npm link / symlink installs)
|
|
155
|
+
const argv1Global = getGlobalNodeModulesFromArgv1();
|
|
156
|
+
if (argv1Global && !paths.includes(argv1Global)) {
|
|
157
|
+
paths.push(argv1Global);
|
|
158
|
+
}
|
|
159
|
+
// Local node_modules in cwd
|
|
160
|
+
const localNodeModules = path.join(process.cwd(), 'node_modules');
|
|
161
|
+
if (!paths.includes(localNodeModules)) {
|
|
162
|
+
paths.push(localNodeModules);
|
|
163
|
+
}
|
|
164
|
+
return paths;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Derive the global `node_modules` directory from `process.argv[1]`.
|
|
168
|
+
*
|
|
169
|
+
* When a CLI binary is installed globally (including via `npm link`),
|
|
170
|
+
* `process.argv[1]` points to `PREFIX/bin/<cmd>`. From that we can derive
|
|
171
|
+
* `PREFIX/lib/node_modules` (Unix) or `PREFIX/node_modules` (Windows).
|
|
172
|
+
*
|
|
173
|
+
* This handles the case where the engine is symlinked from a monorepo
|
|
174
|
+
* and `import.meta.url` resolves to the real path rather than the global
|
|
175
|
+
* `node_modules` tree.
|
|
176
|
+
*
|
|
177
|
+
* @returns The global node_modules path, or null if it cannot be determined
|
|
178
|
+
*/
|
|
179
|
+
export function getGlobalNodeModulesFromArgv1() {
|
|
180
|
+
const argv1 = process.argv[1];
|
|
181
|
+
if (!argv1)
|
|
182
|
+
return null;
|
|
183
|
+
const binDir = path.dirname(argv1);
|
|
184
|
+
if (path.basename(binDir) !== 'bin')
|
|
185
|
+
return null;
|
|
186
|
+
const prefixDir = path.dirname(binDir);
|
|
187
|
+
// Unix: PREFIX/lib/node_modules
|
|
188
|
+
const libNm = path.join(prefixDir, 'lib', 'node_modules');
|
|
189
|
+
try {
|
|
190
|
+
if (statSync(libNm).isDirectory())
|
|
191
|
+
return libNm;
|
|
192
|
+
}
|
|
193
|
+
catch { /* not found */ }
|
|
194
|
+
// Windows / alternate layout: PREFIX/node_modules
|
|
195
|
+
const directNm = path.join(prefixDir, 'node_modules');
|
|
196
|
+
try {
|
|
197
|
+
if (statSync(directNm).isDirectory())
|
|
198
|
+
return directNm;
|
|
199
|
+
}
|
|
200
|
+
catch { /* not found */ }
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=plugin-discovery.js.map
|