@hexaijs/plugin-contracts-generator 0.2.0 → 0.2.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/README.md +33 -25
- package/dist/{cli-CPg-O4OY.d.ts → cli-DajurpEQ.d.ts} +70 -7
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +95 -101
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +5 -77
- package/dist/index.js +96 -123
- package/dist/index.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/package.json +3 -7
- package/dist/decorators/index.d.ts +0 -144
- package/dist/decorators/index.js +0 -28
- package/dist/decorators/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -30,14 +30,14 @@ npm install @hexaijs/plugin-contracts-generator
|
|
|
30
30
|
The package provides three decorators that mark messages for extraction. These decorators have **no runtime overhead** - they simply tag classes for discovery during the build process.
|
|
31
31
|
|
|
32
32
|
```typescript
|
|
33
|
-
import { PublicEvent, PublicCommand, PublicQuery } from "@hexaijs/
|
|
33
|
+
import { PublicEvent, PublicCommand, PublicQuery } from "@hexaijs/contracts/decorators";
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
**@PublicEvent()** - Marks a domain event as part of the public contract:
|
|
37
37
|
|
|
38
38
|
```typescript
|
|
39
39
|
import { DomainEvent } from "@hexaijs/core";
|
|
40
|
-
import { PublicEvent } from "@hexaijs/
|
|
40
|
+
import { PublicEvent } from "@hexaijs/contracts/decorators";
|
|
41
41
|
|
|
42
42
|
@PublicEvent()
|
|
43
43
|
export class OrderPlaced extends DomainEvent<{
|
|
@@ -52,7 +52,7 @@ export class OrderPlaced extends DomainEvent<{
|
|
|
52
52
|
**@PublicCommand()** - Marks a command as part of the public contract:
|
|
53
53
|
|
|
54
54
|
```typescript
|
|
55
|
-
import { PublicCommand } from "@hexaijs/
|
|
55
|
+
import { PublicCommand } from "@hexaijs/contracts/decorators";
|
|
56
56
|
|
|
57
57
|
@PublicCommand()
|
|
58
58
|
export class CreateOrderRequest extends BaseRequest<{
|
|
@@ -70,7 +70,7 @@ export type CreateOrderResponse = {
|
|
|
70
70
|
**@PublicQuery()** - Marks a query as part of the public contract:
|
|
71
71
|
|
|
72
72
|
```typescript
|
|
73
|
-
import { PublicQuery } from "@hexaijs/
|
|
73
|
+
import { PublicQuery } from "@hexaijs/contracts/decorators";
|
|
74
74
|
|
|
75
75
|
@PublicQuery({ response: "OrderDetails" })
|
|
76
76
|
export class GetOrderQuery extends BaseRequest<{
|
|
@@ -101,21 +101,16 @@ export default {
|
|
|
101
101
|
contexts: [
|
|
102
102
|
{
|
|
103
103
|
name: "order",
|
|
104
|
-
|
|
105
|
-
tsconfigPath: "
|
|
104
|
+
path: "packages/order",
|
|
105
|
+
tsconfigPath: "tsconfig.json", // optional, relative to path
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
name: "inventory",
|
|
109
|
-
|
|
109
|
+
path: "packages/inventory",
|
|
110
|
+
sourceDir: "lib", // optional, defaults to "src"
|
|
110
111
|
},
|
|
111
112
|
],
|
|
112
113
|
|
|
113
|
-
// Output package configuration (required)
|
|
114
|
-
outputPackage: {
|
|
115
|
-
name: "@myorg/contracts",
|
|
116
|
-
dir: "packages/contracts",
|
|
117
|
-
},
|
|
118
|
-
|
|
119
114
|
// Path alias rewrite rules (optional)
|
|
120
115
|
pathAliasRewrites: {
|
|
121
116
|
"@myorg/": "@/",
|
|
@@ -132,20 +127,30 @@ export default {
|
|
|
132
127
|
{ messageSuffix: "Query", responseSuffix: "QueryResult" },
|
|
133
128
|
{ messageSuffix: "Request", responseSuffix: "Response" },
|
|
134
129
|
],
|
|
130
|
+
|
|
131
|
+
// Custom decorator names (optional, defaults shown)
|
|
132
|
+
decoratorNames: {
|
|
133
|
+
event: "PublicEvent",
|
|
134
|
+
command: "PublicCommand",
|
|
135
|
+
query: "PublicQuery",
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
// Strip decorators from generated output (optional, default: true)
|
|
139
|
+
removeDecorators: true,
|
|
135
140
|
},
|
|
136
141
|
};
|
|
137
142
|
```
|
|
138
143
|
|
|
144
|
+
Each context requires `name` and `path`. The `path` is the base directory of the context (relative to the config file). Within that directory:
|
|
145
|
+
- `sourceDir` defaults to `"src"` (resolved relative to `path`)
|
|
146
|
+
- `tsconfigPath` defaults to `"tsconfig.json"` (resolved relative to `path`)
|
|
147
|
+
|
|
139
148
|
For monorepos with many packages, use glob patterns to auto-discover contexts:
|
|
140
149
|
|
|
141
150
|
```typescript
|
|
142
151
|
export default {
|
|
143
152
|
contracts: {
|
|
144
153
|
contexts: ["packages/*"], // Matches all directories under packages/
|
|
145
|
-
outputPackage: {
|
|
146
|
-
name: "@myorg/contracts",
|
|
147
|
-
dir: "packages/contracts",
|
|
148
|
-
},
|
|
149
154
|
},
|
|
150
155
|
};
|
|
151
156
|
```
|
|
@@ -208,17 +213,19 @@ The generator handles two types of files differently:
|
|
|
208
213
|
Run the generator from your monorepo root:
|
|
209
214
|
|
|
210
215
|
```bash
|
|
211
|
-
#
|
|
212
|
-
npx contracts-generator
|
|
216
|
+
# Required: --output-dir (-o) specifies where contracts are generated
|
|
217
|
+
npx contracts-generator --output-dir packages/contracts/src
|
|
213
218
|
|
|
214
|
-
# Specify config file path
|
|
215
|
-
npx contracts-generator --config ./
|
|
219
|
+
# Specify config file path (default: application.config.ts)
|
|
220
|
+
npx contracts-generator -o packages/contracts/src --config ./app.config.ts
|
|
216
221
|
|
|
217
222
|
# Filter by message types
|
|
218
|
-
npx contracts-generator -m event # Extract only events
|
|
219
|
-
npx contracts-generator -m command # Extract only commands
|
|
220
|
-
npx contracts-generator -m
|
|
221
|
-
|
|
223
|
+
npx contracts-generator -o packages/contracts/src -m event # Extract only events
|
|
224
|
+
npx contracts-generator -o packages/contracts/src -m command # Extract only commands
|
|
225
|
+
npx contracts-generator -o packages/contracts/src -m event,command # Extract events and commands
|
|
226
|
+
|
|
227
|
+
# Generate with message registry (index.ts)
|
|
228
|
+
npx contracts-generator -o packages/contracts/src --generate-message-registry
|
|
222
229
|
```
|
|
223
230
|
|
|
224
231
|
### Programmatic API
|
|
@@ -255,6 +262,7 @@ contracts/
|
|
|
255
262
|
│ ├── {context}/
|
|
256
263
|
│ │ ├── events.ts
|
|
257
264
|
│ │ ├── commands.ts
|
|
265
|
+
│ │ ├── queries.ts
|
|
258
266
|
│ │ ├── types.ts # Dependent types + Response types
|
|
259
267
|
│ │ └── index.ts # Barrel exports
|
|
260
268
|
│ └── index.ts # Namespace exports + MessageRegistry
|
|
@@ -176,6 +176,74 @@ declare function isFunctionType(type: TypeRef): type is FunctionType;
|
|
|
176
176
|
declare function isDomainEvent(message: Message): message is DomainEvent;
|
|
177
177
|
declare function isCommand(message: Message): message is Command;
|
|
178
178
|
|
|
179
|
+
interface FileStats {
|
|
180
|
+
isDirectory(): boolean;
|
|
181
|
+
isFile(): boolean;
|
|
182
|
+
}
|
|
183
|
+
interface FileSystem {
|
|
184
|
+
readFile(path: string): Promise<string>;
|
|
185
|
+
readdir(path: string): Promise<string[]>;
|
|
186
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
187
|
+
mkdir(path: string, options?: {
|
|
188
|
+
recursive?: boolean;
|
|
189
|
+
}): Promise<void>;
|
|
190
|
+
exists(path: string): Promise<boolean>;
|
|
191
|
+
stat(path: string): Promise<FileStats>;
|
|
192
|
+
}
|
|
193
|
+
declare class NodeFileSystem implements FileSystem {
|
|
194
|
+
readFile(path: string): Promise<string>;
|
|
195
|
+
readdir(path: string): Promise<string[]>;
|
|
196
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
197
|
+
mkdir(path: string, options?: {
|
|
198
|
+
recursive?: boolean;
|
|
199
|
+
}): Promise<void>;
|
|
200
|
+
exists(path: string): Promise<boolean>;
|
|
201
|
+
stat(path: string): Promise<FileStats>;
|
|
202
|
+
}
|
|
203
|
+
declare const nodeFileSystem: NodeFileSystem;
|
|
204
|
+
|
|
205
|
+
interface InputContextConfig {
|
|
206
|
+
readonly name: string;
|
|
207
|
+
readonly path: string;
|
|
208
|
+
readonly sourceDir?: string;
|
|
209
|
+
readonly tsconfigPath?: string;
|
|
210
|
+
readonly responseNamingConventions?: readonly ResponseNamingConvention[];
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Encapsulates context configuration with path resolution capabilities.
|
|
214
|
+
* Created via factory method to ensure proper initialization.
|
|
215
|
+
*/
|
|
216
|
+
declare class ContextConfig {
|
|
217
|
+
private readonly fs;
|
|
218
|
+
private readonly tsconfig;
|
|
219
|
+
readonly name: string;
|
|
220
|
+
readonly sourceDir: string;
|
|
221
|
+
readonly responseNamingConventions?: readonly ResponseNamingConvention[];
|
|
222
|
+
private constructor();
|
|
223
|
+
/**
|
|
224
|
+
* Factory method to create ContextConfig with properly loaded tsconfig.
|
|
225
|
+
*/
|
|
226
|
+
static create(input: InputContextConfig, configDir: string, fs?: FileSystem): Promise<ContextConfig>;
|
|
227
|
+
private static loadTsconfig;
|
|
228
|
+
/**
|
|
229
|
+
* Creates a ContextConfig without async loading (for cases where tsconfig is not needed
|
|
230
|
+
* or already handled externally).
|
|
231
|
+
*/
|
|
232
|
+
static createSync(name: string, sourceDir: string, fs?: FileSystem, responseNamingConventions?: readonly ResponseNamingConvention[]): ContextConfig;
|
|
233
|
+
/**
|
|
234
|
+
* Resolves a module specifier (path alias) to actual file path.
|
|
235
|
+
* Only handles non-relative imports (path aliases).
|
|
236
|
+
*
|
|
237
|
+
* @param moduleSpecifier - The import path to resolve (e.g., "@/utils/helper")
|
|
238
|
+
* @returns Object with resolvedPath (null if external) and isExternal flag
|
|
239
|
+
*/
|
|
240
|
+
resolvePath(moduleSpecifier: string): Promise<{
|
|
241
|
+
resolvedPath: string | null;
|
|
242
|
+
isExternal: boolean;
|
|
243
|
+
}>;
|
|
244
|
+
private tryResolveWithExtensions;
|
|
245
|
+
}
|
|
246
|
+
|
|
179
247
|
/**
|
|
180
248
|
* Options for runWithConfig when config is provided directly.
|
|
181
249
|
*/
|
|
@@ -189,12 +257,7 @@ interface RunWithConfigOptions {
|
|
|
189
257
|
* This is the config passed from hexai.config.ts.
|
|
190
258
|
*/
|
|
191
259
|
interface ContractsPluginConfig {
|
|
192
|
-
contexts: Array<
|
|
193
|
-
name: string;
|
|
194
|
-
path: string;
|
|
195
|
-
sourceDir?: string;
|
|
196
|
-
tsconfigPath?: string;
|
|
197
|
-
}>;
|
|
260
|
+
contexts: Array<string | InputContextConfig>;
|
|
198
261
|
pathAliasRewrites?: Record<string, string>;
|
|
199
262
|
externalDependencies?: Record<string, string>;
|
|
200
263
|
decoratorNames?: DecoratorNames;
|
|
@@ -211,4 +274,4 @@ declare function run(args: string[]): Promise<void>;
|
|
|
211
274
|
*/
|
|
212
275
|
declare function runWithConfig(options: RunWithConfigOptions, pluginConfig: ContractsPluginConfig): Promise<void>;
|
|
213
276
|
|
|
214
|
-
export { type ArrayType as A,
|
|
277
|
+
export { runWithConfig as $, type ArrayType as A, isDomainEvent as B, type Command as C, type DecoratorNames as D, type EnumDefinition as E, type FileSystem as F, isFunctionType as G, isIntersectionType as H, type InputContextConfig as I, isLiteralType as J, isObjectType as K, type LiteralType as L, type MessageType as M, isPrimitiveType as N, type ObjectType as O, type PrimitiveType as P, type Query as Q, type ResponseNamingConvention as R, type SourceFile as S, type TypeDefinition as T, type UnionType as U, isReferenceType as V, isTupleType as W, isUnionType as X, nodeFileSystem as Y, type RunWithConfigOptions as Z, run as _, type DomainEvent as a, ContextConfig as b, type ContractsPluginConfig as c, type ClassDefinition as d, type ClassImport as e, type Config as f, type Dependency as g, type DependencyKind as h, type EnumMember as i, type ExtractionError as j, type ExtractionResult as k, type ExtractionWarning as l, type Field as m, type FileStats as n, type FunctionParameter as o, type FunctionType as p, type ImportSource as q, type IntersectionType as r, type Message as s, type MessageBase as t, type ReferenceType as u, type TupleType as v, type TypeDefinitionKind as w, type TypeRef as x, isArrayType as y, isCommand as z };
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export {
|
|
2
|
+
export { c as ContractsPluginConfig, Z as RunWithConfigOptions, _ as run, $ as runWithConfig } from './cli-DajurpEQ.js';
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import path__default, { resolve, dirname,
|
|
4
|
+
import path__default, { resolve, dirname, join, relative, basename } from 'path';
|
|
5
5
|
import * as ts8 from 'typescript';
|
|
6
6
|
import ts8__default from 'typescript';
|
|
7
7
|
import { readFile, readdir, writeFile, mkdir, access, stat } from 'fs/promises';
|
|
8
8
|
import { constants } from 'fs';
|
|
9
|
-
import '
|
|
9
|
+
import '@hexaijs/contracts/decorators';
|
|
10
10
|
import { glob } from 'glob';
|
|
11
11
|
import { minimatch } from 'minimatch';
|
|
12
12
|
|
|
@@ -233,6 +233,90 @@ var ContextConfig = class _ContextConfig {
|
|
|
233
233
|
|
|
234
234
|
// src/config-loader.ts
|
|
235
235
|
var SUPPORTED_GLOB_PARTS_COUNT = 2;
|
|
236
|
+
async function resolveContextEntries(entries, configDir, fs = nodeFileSystem) {
|
|
237
|
+
const contexts = [];
|
|
238
|
+
for (let i = 0; i < entries.length; i++) {
|
|
239
|
+
const item = entries[i];
|
|
240
|
+
if (typeof item === "string") {
|
|
241
|
+
const resolved = await resolveStringEntry(item, configDir, fs);
|
|
242
|
+
contexts.push(...resolved);
|
|
243
|
+
} else {
|
|
244
|
+
const contextConfig = await createObjectContext(item, i, configDir, fs);
|
|
245
|
+
contexts.push(contextConfig);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return contexts;
|
|
249
|
+
}
|
|
250
|
+
async function resolveStringEntry(contextPath, configDir, fs) {
|
|
251
|
+
if (contextPath.includes("*")) {
|
|
252
|
+
return expandGlobPattern(contextPath, configDir, fs);
|
|
253
|
+
}
|
|
254
|
+
const basePath = resolve(configDir, contextPath);
|
|
255
|
+
const name = basename(basePath);
|
|
256
|
+
return [await ContextConfig.create(
|
|
257
|
+
{ name, path: contextPath },
|
|
258
|
+
configDir,
|
|
259
|
+
fs
|
|
260
|
+
)];
|
|
261
|
+
}
|
|
262
|
+
async function expandGlobPattern(pattern, configDir, fs) {
|
|
263
|
+
const packageDirs = await matchGlobPattern(pattern, configDir, fs);
|
|
264
|
+
return Promise.all(
|
|
265
|
+
packageDirs.map((dir) => {
|
|
266
|
+
const name = basename(dir);
|
|
267
|
+
const relativePath = relative(configDir, dir);
|
|
268
|
+
return ContextConfig.create(
|
|
269
|
+
{ name, path: relativePath },
|
|
270
|
+
configDir,
|
|
271
|
+
fs
|
|
272
|
+
);
|
|
273
|
+
})
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
async function createObjectContext(ctx, index, configDir, fs) {
|
|
277
|
+
if (!ctx.name || typeof ctx.name !== "string") {
|
|
278
|
+
throw new ConfigLoadError(
|
|
279
|
+
`Invalid context at index ${index}: missing 'name'`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (!ctx.path || typeof ctx.path !== "string") {
|
|
283
|
+
throw new ConfigLoadError(
|
|
284
|
+
`Invalid context at index ${index}: missing 'path'`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return ContextConfig.create(ctx, configDir, fs);
|
|
288
|
+
}
|
|
289
|
+
async function matchGlobPattern(pattern, configDir, fs) {
|
|
290
|
+
const globParts = pattern.split("*");
|
|
291
|
+
if (globParts.length !== SUPPORTED_GLOB_PARTS_COUNT) {
|
|
292
|
+
throw new ConfigLoadError(
|
|
293
|
+
`Invalid glob pattern: "${pattern}". Only single wildcard patterns like "packages/*" are supported.`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const [prefix, suffix] = globParts;
|
|
297
|
+
const baseDir = resolve(configDir, prefix);
|
|
298
|
+
if (!await fs.exists(baseDir)) {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
const entries = await fs.readdir(baseDir);
|
|
302
|
+
const matchedDirs = [];
|
|
303
|
+
for (const entry of entries) {
|
|
304
|
+
const fullPath = resolve(baseDir, entry);
|
|
305
|
+
const stats = await fs.stat(fullPath);
|
|
306
|
+
if (!stats.isDirectory()) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (suffix) {
|
|
310
|
+
const suffixPath = resolve(fullPath, suffix.replace(/^\//, ""));
|
|
311
|
+
if (await fs.exists(suffixPath)) {
|
|
312
|
+
matchedDirs.push(fullPath);
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
matchedDirs.push(fullPath);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return matchedDirs.sort();
|
|
319
|
+
}
|
|
236
320
|
var ConfigLoader = class {
|
|
237
321
|
fs;
|
|
238
322
|
constructor(options = {}) {
|
|
@@ -265,7 +349,7 @@ var ConfigLoader = class {
|
|
|
265
349
|
if (!contracts.contexts || !Array.isArray(contracts.contexts)) {
|
|
266
350
|
throw new ConfigLoadError("Missing 'contracts.contexts' in config");
|
|
267
351
|
}
|
|
268
|
-
const contexts = await
|
|
352
|
+
const contexts = await resolveContextEntries(contracts.contexts, configDir, this.fs);
|
|
269
353
|
if (contexts.length === 0) {
|
|
270
354
|
throw new ConfigLoadError("No contexts found from 'contexts'");
|
|
271
355
|
}
|
|
@@ -279,90 +363,6 @@ var ConfigLoader = class {
|
|
|
279
363
|
removeDecorators: contracts.removeDecorators ?? true
|
|
280
364
|
};
|
|
281
365
|
}
|
|
282
|
-
async resolveContexts(contextsConfig, configDir) {
|
|
283
|
-
const contexts = [];
|
|
284
|
-
for (let i = 0; i < contextsConfig.length; i++) {
|
|
285
|
-
const item = contextsConfig[i];
|
|
286
|
-
if (typeof item === "string") {
|
|
287
|
-
const resolvedContexts = await this.resolveStringContext(item, configDir);
|
|
288
|
-
contexts.push(...resolvedContexts);
|
|
289
|
-
} else {
|
|
290
|
-
const contextConfig = await this.createObjectContext(item, i, configDir);
|
|
291
|
-
contexts.push(contextConfig);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return contexts;
|
|
295
|
-
}
|
|
296
|
-
async resolveStringContext(contextPath, configDir) {
|
|
297
|
-
if (contextPath.includes("*")) {
|
|
298
|
-
return this.expandGlobPattern(contextPath, configDir);
|
|
299
|
-
}
|
|
300
|
-
const basePath = resolve(configDir, contextPath);
|
|
301
|
-
const name = basename(basePath);
|
|
302
|
-
return [await ContextConfig.create(
|
|
303
|
-
{ name, path: contextPath },
|
|
304
|
-
configDir,
|
|
305
|
-
this.fs
|
|
306
|
-
)];
|
|
307
|
-
}
|
|
308
|
-
async expandGlobPattern(pattern, configDir) {
|
|
309
|
-
const packageDirs = await this.matchGlobPattern(pattern, configDir);
|
|
310
|
-
return Promise.all(
|
|
311
|
-
packageDirs.map((dir) => {
|
|
312
|
-
const name = basename(dir);
|
|
313
|
-
const relativePath = relative(configDir, dir);
|
|
314
|
-
return ContextConfig.create(
|
|
315
|
-
{ name, path: relativePath },
|
|
316
|
-
configDir,
|
|
317
|
-
this.fs
|
|
318
|
-
);
|
|
319
|
-
})
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
async createObjectContext(ctx, index, configDir) {
|
|
323
|
-
if (!ctx.name || typeof ctx.name !== "string") {
|
|
324
|
-
throw new ConfigLoadError(
|
|
325
|
-
`Invalid context at index ${index}: missing 'name'`
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
if (!ctx.path || typeof ctx.path !== "string") {
|
|
329
|
-
throw new ConfigLoadError(
|
|
330
|
-
`Invalid context at index ${index}: missing 'path'`
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
return ContextConfig.create(ctx, configDir, this.fs);
|
|
334
|
-
}
|
|
335
|
-
async matchGlobPattern(pattern, configDir) {
|
|
336
|
-
const globParts = pattern.split("*");
|
|
337
|
-
if (globParts.length !== SUPPORTED_GLOB_PARTS_COUNT) {
|
|
338
|
-
throw new ConfigLoadError(
|
|
339
|
-
`Invalid glob pattern: "${pattern}". Only single wildcard patterns like "packages/*" are supported.`
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
const [prefix, suffix] = globParts;
|
|
343
|
-
const baseDir = resolve(configDir, prefix);
|
|
344
|
-
if (!await this.fs.exists(baseDir)) {
|
|
345
|
-
return [];
|
|
346
|
-
}
|
|
347
|
-
const entries = await this.fs.readdir(baseDir);
|
|
348
|
-
const matchedDirs = [];
|
|
349
|
-
for (const entry of entries) {
|
|
350
|
-
const fullPath = resolve(baseDir, entry);
|
|
351
|
-
const stats = await this.fs.stat(fullPath);
|
|
352
|
-
if (!stats.isDirectory()) {
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
if (suffix) {
|
|
356
|
-
const suffixPath = resolve(fullPath, suffix.replace(/^\//, ""));
|
|
357
|
-
if (await this.fs.exists(suffixPath)) {
|
|
358
|
-
matchedDirs.push(fullPath);
|
|
359
|
-
}
|
|
360
|
-
} else {
|
|
361
|
-
matchedDirs.push(fullPath);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return matchedDirs.sort();
|
|
365
|
-
}
|
|
366
366
|
};
|
|
367
367
|
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
368
368
|
"**/node_modules/**",
|
|
@@ -2360,9 +2360,12 @@ Config file format:
|
|
|
2360
2360
|
export default {
|
|
2361
2361
|
contracts: {
|
|
2362
2362
|
contexts: [
|
|
2363
|
+
"packages/*", // glob: auto-discover all contexts
|
|
2364
|
+
"packages/auth", // string: name inferred from directory
|
|
2363
2365
|
{
|
|
2364
2366
|
name: "lecture",
|
|
2365
|
-
|
|
2367
|
+
path: "packages/lecture", // required: base directory
|
|
2368
|
+
sourceDir: "src", // optional, default: "src"
|
|
2366
2369
|
},
|
|
2367
2370
|
],
|
|
2368
2371
|
pathAliasRewrites: {
|
|
@@ -2448,19 +2451,10 @@ async function run(args) {
|
|
|
2448
2451
|
}
|
|
2449
2452
|
}
|
|
2450
2453
|
async function toContractsConfig(pluginConfig) {
|
|
2451
|
-
const contexts = await
|
|
2452
|
-
pluginConfig.contexts
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
name: ctx.name,
|
|
2456
|
-
path: ctx.path,
|
|
2457
|
-
sourceDir: ctx.sourceDir,
|
|
2458
|
-
tsconfigPath: ctx.tsconfigPath
|
|
2459
|
-
},
|
|
2460
|
-
process.cwd(),
|
|
2461
|
-
nodeFileSystem
|
|
2462
|
-
)
|
|
2463
|
-
)
|
|
2454
|
+
const contexts = await resolveContextEntries(
|
|
2455
|
+
pluginConfig.contexts,
|
|
2456
|
+
process.cwd(),
|
|
2457
|
+
nodeFileSystem
|
|
2464
2458
|
);
|
|
2465
2459
|
return {
|
|
2466
2460
|
contexts,
|