@principal-ai/principal-view-cli 0.1.25 → 0.1.27
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/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +3 -2
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +63 -0
- package/dist/index.cjs +256 -1
- package/dist/index.cjs.map +4 -4
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2PpC,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2PpC,wBAAgB,iBAAiB,IAAI,OAAO,CA4L3C"}
|
package/dist/commands/lint.js
CHANGED
|
@@ -282,13 +282,14 @@ export function createLintCommand() {
|
|
|
282
282
|
ignore: privuConfig.exclude || ['**/node_modules/**'],
|
|
283
283
|
expandDirectories: false,
|
|
284
284
|
});
|
|
285
|
-
// Filter out library files, config files,
|
|
285
|
+
// Filter out library files, config files, canvas files, and execution artifacts
|
|
286
286
|
const configFiles = matchedFiles.filter((f) => {
|
|
287
287
|
const name = basename(f).toLowerCase();
|
|
288
288
|
const isLibraryFile = name.startsWith('library.');
|
|
289
289
|
const isConfigFile = name.startsWith('.privurc');
|
|
290
290
|
const isCanvasFile = f.toLowerCase().endsWith('.canvas');
|
|
291
|
-
|
|
291
|
+
const isExecutionArtifact = f.includes('__executions__/');
|
|
292
|
+
return !isLibraryFile && !isConfigFile && !isCanvasFile && !isExecutionArtifact;
|
|
292
293
|
});
|
|
293
294
|
if (configFiles.length === 0) {
|
|
294
295
|
if (options.json) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4nCpC,wBAAgB,qBAAqB,IAAI,OAAO,CAsH/C"}
|
|
@@ -229,6 +229,7 @@ const ALLOWED_CANVAS_FIELDS = {
|
|
|
229
229
|
'name',
|
|
230
230
|
'description',
|
|
231
231
|
'otel',
|
|
232
|
+
'events',
|
|
232
233
|
'shape',
|
|
233
234
|
'icon',
|
|
234
235
|
'fill',
|
|
@@ -345,6 +346,51 @@ function findSimilarField(field, allowedFields) {
|
|
|
345
346
|
}
|
|
346
347
|
return null;
|
|
347
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* Check if a canvas has OTEL-related features
|
|
351
|
+
* Returns true if the canvas contains any of:
|
|
352
|
+
* 1. Nodes with pv.otel extension (kind, category)
|
|
353
|
+
* 2. Event schemas (pv.events with validation)
|
|
354
|
+
* 3. Canvas scope/audit config (OTEL log routing)
|
|
355
|
+
* 4. Resource matching for OTEL logs
|
|
356
|
+
*/
|
|
357
|
+
function hasOtelFeatures(canvas) {
|
|
358
|
+
if (!canvas || typeof canvas !== 'object') {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
const c = canvas;
|
|
362
|
+
// Check for canvas-level scope or audit config
|
|
363
|
+
if (c.pv && typeof c.pv === 'object') {
|
|
364
|
+
const pv = c.pv;
|
|
365
|
+
if (pv.scope !== undefined || pv.audit !== undefined) {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// Check nodes for OTEL features
|
|
370
|
+
if (Array.isArray(c.nodes)) {
|
|
371
|
+
for (const node of c.nodes) {
|
|
372
|
+
if (node && typeof node === 'object') {
|
|
373
|
+
const n = node;
|
|
374
|
+
if (n.pv && typeof n.pv === 'object') {
|
|
375
|
+
const nodePv = n.pv;
|
|
376
|
+
// Check for pv.otel extension
|
|
377
|
+
if (nodePv.otel !== undefined) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
// Check for event schemas (pv.events)
|
|
381
|
+
if (nodePv.events !== undefined) {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
// Check for resourceMatch (OTEL log routing)
|
|
385
|
+
if (nodePv.resourceMatch !== undefined) {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
348
394
|
/**
|
|
349
395
|
* Validate an ExtendedCanvas object with strict validation
|
|
350
396
|
*
|
|
@@ -791,6 +837,23 @@ function validateCanvas(canvas, filePath, library) {
|
|
|
791
837
|
}
|
|
792
838
|
});
|
|
793
839
|
}
|
|
840
|
+
// Validate OTEL canvas naming convention
|
|
841
|
+
const hasOtel = hasOtelFeatures(canvas);
|
|
842
|
+
const isOtelCanvas = filePath.endsWith('.otel.canvas');
|
|
843
|
+
if (hasOtel && !isOtelCanvas) {
|
|
844
|
+
issues.push({
|
|
845
|
+
type: 'error',
|
|
846
|
+
message: 'Canvas contains OTEL features but does not use .otel.canvas naming convention',
|
|
847
|
+
suggestion: 'Rename file to use .otel.canvas extension (e.g., "graph-name.otel.canvas")',
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
else if (!hasOtel && isOtelCanvas) {
|
|
851
|
+
issues.push({
|
|
852
|
+
type: 'warning',
|
|
853
|
+
message: 'Canvas uses .otel.canvas naming but does not contain any OTEL features',
|
|
854
|
+
suggestion: 'Either add OTEL features (pv.otel, pv.events, pv.scope, pv.audit, resourceMatch) or rename to .canvas',
|
|
855
|
+
});
|
|
856
|
+
}
|
|
794
857
|
return issues;
|
|
795
858
|
}
|
|
796
859
|
/**
|
package/dist/index.cjs
CHANGED
|
@@ -13688,6 +13688,7 @@ var ALLOWED_CANVAS_FIELDS = {
|
|
|
13688
13688
|
"name",
|
|
13689
13689
|
"description",
|
|
13690
13690
|
"otel",
|
|
13691
|
+
"events",
|
|
13691
13692
|
"shape",
|
|
13692
13693
|
"icon",
|
|
13693
13694
|
"fill",
|
|
@@ -13790,6 +13791,38 @@ function findSimilarField(field, allowedFields) {
|
|
|
13790
13791
|
}
|
|
13791
13792
|
return null;
|
|
13792
13793
|
}
|
|
13794
|
+
function hasOtelFeatures(canvas) {
|
|
13795
|
+
if (!canvas || typeof canvas !== "object") {
|
|
13796
|
+
return false;
|
|
13797
|
+
}
|
|
13798
|
+
const c = canvas;
|
|
13799
|
+
if (c.pv && typeof c.pv === "object") {
|
|
13800
|
+
const pv = c.pv;
|
|
13801
|
+
if (pv.scope !== void 0 || pv.audit !== void 0) {
|
|
13802
|
+
return true;
|
|
13803
|
+
}
|
|
13804
|
+
}
|
|
13805
|
+
if (Array.isArray(c.nodes)) {
|
|
13806
|
+
for (const node of c.nodes) {
|
|
13807
|
+
if (node && typeof node === "object") {
|
|
13808
|
+
const n = node;
|
|
13809
|
+
if (n.pv && typeof n.pv === "object") {
|
|
13810
|
+
const nodePv = n.pv;
|
|
13811
|
+
if (nodePv.otel !== void 0) {
|
|
13812
|
+
return true;
|
|
13813
|
+
}
|
|
13814
|
+
if (nodePv.events !== void 0) {
|
|
13815
|
+
return true;
|
|
13816
|
+
}
|
|
13817
|
+
if (nodePv.resourceMatch !== void 0) {
|
|
13818
|
+
return true;
|
|
13819
|
+
}
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13822
|
+
}
|
|
13823
|
+
}
|
|
13824
|
+
return false;
|
|
13825
|
+
}
|
|
13793
13826
|
function validateCanvas(canvas, filePath, library) {
|
|
13794
13827
|
const issues = [];
|
|
13795
13828
|
if (!canvas || typeof canvas !== "object") {
|
|
@@ -14256,6 +14289,21 @@ function validateCanvas(canvas, filePath, library) {
|
|
|
14256
14289
|
}
|
|
14257
14290
|
});
|
|
14258
14291
|
}
|
|
14292
|
+
const hasOtel = hasOtelFeatures(canvas);
|
|
14293
|
+
const isOtelCanvas = filePath.endsWith(".otel.canvas");
|
|
14294
|
+
if (hasOtel && !isOtelCanvas) {
|
|
14295
|
+
issues.push({
|
|
14296
|
+
type: "error",
|
|
14297
|
+
message: "Canvas contains OTEL features but does not use .otel.canvas naming convention",
|
|
14298
|
+
suggestion: 'Rename file to use .otel.canvas extension (e.g., "graph-name.otel.canvas")'
|
|
14299
|
+
});
|
|
14300
|
+
} else if (!hasOtel && isOtelCanvas) {
|
|
14301
|
+
issues.push({
|
|
14302
|
+
type: "warning",
|
|
14303
|
+
message: "Canvas uses .otel.canvas naming but does not contain any OTEL features",
|
|
14304
|
+
suggestion: "Either add OTEL features (pv.otel, pv.events, pv.scope, pv.audit, resourceMatch) or rename to .canvas"
|
|
14305
|
+
});
|
|
14306
|
+
}
|
|
14259
14307
|
return issues;
|
|
14260
14308
|
}
|
|
14261
14309
|
function validateFile(filePath, library) {
|
|
@@ -15547,6 +15595,212 @@ function createCreateCommand() {
|
|
|
15547
15595
|
var import_node_fs10 = require("node:fs");
|
|
15548
15596
|
var import_node_path11 = require("node:path");
|
|
15549
15597
|
|
|
15598
|
+
// ../core/dist/codegen/type-generator.js
|
|
15599
|
+
var TypeScriptGenerator = class {
|
|
15600
|
+
constructor() {
|
|
15601
|
+
this.language = "typescript";
|
|
15602
|
+
}
|
|
15603
|
+
generate(canvas, options) {
|
|
15604
|
+
const opts = {
|
|
15605
|
+
readonly: options?.style?.readonly ?? false,
|
|
15606
|
+
strictNullChecks: options?.style?.strictNullChecks ?? true,
|
|
15607
|
+
includeDocComments: options?.style?.includeDocComments ?? true,
|
|
15608
|
+
namespace: options?.namespace
|
|
15609
|
+
};
|
|
15610
|
+
const lines = [];
|
|
15611
|
+
lines.push("/**");
|
|
15612
|
+
lines.push(` * Generated types from canvas: ${canvas.pv?.name || "Untitled"}`);
|
|
15613
|
+
lines.push(" *");
|
|
15614
|
+
lines.push(" * DO NOT EDIT MANUALLY - This file is auto-generated");
|
|
15615
|
+
lines.push(" * Generated by @principal-ai/principal-view-core");
|
|
15616
|
+
lines.push(" *");
|
|
15617
|
+
lines.push(` * Canvas: ${canvas.pv?.description || "No description"}`);
|
|
15618
|
+
lines.push(` * Version: ${canvas.pv?.version || "1.0.0"}`);
|
|
15619
|
+
lines.push(" */");
|
|
15620
|
+
lines.push("");
|
|
15621
|
+
if (opts.namespace) {
|
|
15622
|
+
lines.push(`export namespace ${opts.namespace} {`);
|
|
15623
|
+
}
|
|
15624
|
+
if (canvas.nodes) {
|
|
15625
|
+
for (const node of canvas.nodes) {
|
|
15626
|
+
if (node.pv?.events) {
|
|
15627
|
+
lines.push(...this.generateNodeTypes(node.id, node.pv.events, opts));
|
|
15628
|
+
lines.push("");
|
|
15629
|
+
}
|
|
15630
|
+
}
|
|
15631
|
+
}
|
|
15632
|
+
lines.push(...this.generateEventNameUnion(canvas, opts));
|
|
15633
|
+
lines.push("");
|
|
15634
|
+
lines.push(...this.generateEmitterTypes(canvas, opts));
|
|
15635
|
+
if (opts.namespace) {
|
|
15636
|
+
lines.push("}");
|
|
15637
|
+
}
|
|
15638
|
+
const code = lines.join("\n");
|
|
15639
|
+
const filename = this.generateFilename(canvas);
|
|
15640
|
+
return {
|
|
15641
|
+
code,
|
|
15642
|
+
extension: "ts",
|
|
15643
|
+
filename
|
|
15644
|
+
};
|
|
15645
|
+
}
|
|
15646
|
+
generateNodeTypes(nodeId, events, opts) {
|
|
15647
|
+
const lines = [];
|
|
15648
|
+
const typeName = this.nodeIdToTypeName(nodeId);
|
|
15649
|
+
if (opts.includeDocComments) {
|
|
15650
|
+
lines.push(`/**`);
|
|
15651
|
+
lines.push(` * Event types for node: ${nodeId}`);
|
|
15652
|
+
lines.push(` */`);
|
|
15653
|
+
}
|
|
15654
|
+
lines.push(`export namespace ${typeName} {`);
|
|
15655
|
+
for (const [eventName, eventSchema] of Object.entries(events)) {
|
|
15656
|
+
lines.push(...this.generateEventInterface(eventName, eventSchema, opts));
|
|
15657
|
+
lines.push("");
|
|
15658
|
+
}
|
|
15659
|
+
const eventNames = Object.keys(events);
|
|
15660
|
+
const interfaceNames = eventNames.map((name) => this.eventNameToTypeName(name));
|
|
15661
|
+
lines.push(" /**");
|
|
15662
|
+
lines.push(` * Union of all event types for ${nodeId}`);
|
|
15663
|
+
lines.push(" */");
|
|
15664
|
+
lines.push(` export type Event = ${interfaceNames.join(" | ")};`);
|
|
15665
|
+
lines.push("");
|
|
15666
|
+
lines.push(" /**");
|
|
15667
|
+
lines.push(` * Literal union of event names for ${nodeId}`);
|
|
15668
|
+
lines.push(" */");
|
|
15669
|
+
lines.push(` export type EventName = ${eventNames.map((n) => `'${n}'`).join(" | ")};`);
|
|
15670
|
+
lines.push("}");
|
|
15671
|
+
return lines;
|
|
15672
|
+
}
|
|
15673
|
+
generateEventInterface(eventName, schema2, opts) {
|
|
15674
|
+
const lines = [];
|
|
15675
|
+
const interfaceName = this.eventNameToTypeName(eventName);
|
|
15676
|
+
const readonly = opts.readonly ? "readonly " : "";
|
|
15677
|
+
if (opts.includeDocComments) {
|
|
15678
|
+
lines.push(" /**");
|
|
15679
|
+
lines.push(` * ${schema2.description}`);
|
|
15680
|
+
lines.push(" */");
|
|
15681
|
+
}
|
|
15682
|
+
lines.push(` export interface ${interfaceName} {`);
|
|
15683
|
+
lines.push(` ${readonly}name: '${eventName}';`);
|
|
15684
|
+
lines.push(` ${readonly}attributes: {`);
|
|
15685
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema2.attributes)) {
|
|
15686
|
+
const optional = !fieldSchema.required && opts.strictNullChecks ? "?" : "";
|
|
15687
|
+
const type2 = this.fieldTypeToTsType(fieldSchema);
|
|
15688
|
+
if (opts.includeDocComments && fieldSchema.description) {
|
|
15689
|
+
lines.push(` /** ${fieldSchema.description} */`);
|
|
15690
|
+
}
|
|
15691
|
+
lines.push(` ${readonly}'${fieldName}'${optional}: ${type2};`);
|
|
15692
|
+
}
|
|
15693
|
+
lines.push(" };");
|
|
15694
|
+
lines.push(" }");
|
|
15695
|
+
return lines;
|
|
15696
|
+
}
|
|
15697
|
+
generateEventNameUnion(canvas, opts) {
|
|
15698
|
+
const lines = [];
|
|
15699
|
+
const allEventNames = /* @__PURE__ */ new Set();
|
|
15700
|
+
if (canvas.nodes) {
|
|
15701
|
+
for (const node of canvas.nodes) {
|
|
15702
|
+
if (node.pv?.events) {
|
|
15703
|
+
Object.keys(node.pv.events).forEach((name) => allEventNames.add(name));
|
|
15704
|
+
}
|
|
15705
|
+
}
|
|
15706
|
+
}
|
|
15707
|
+
if (allEventNames.size === 0) {
|
|
15708
|
+
return lines;
|
|
15709
|
+
}
|
|
15710
|
+
if (opts.includeDocComments) {
|
|
15711
|
+
lines.push("/**");
|
|
15712
|
+
lines.push(" * Union of all event names in the canvas");
|
|
15713
|
+
lines.push(" */");
|
|
15714
|
+
}
|
|
15715
|
+
lines.push(`export type AllEventNames = ${Array.from(allEventNames).map((n) => `'${n}'`).join(" | ")};`);
|
|
15716
|
+
return lines;
|
|
15717
|
+
}
|
|
15718
|
+
generateEmitterTypes(canvas, opts) {
|
|
15719
|
+
const lines = [];
|
|
15720
|
+
if (opts.includeDocComments) {
|
|
15721
|
+
lines.push("/**");
|
|
15722
|
+
lines.push(" * Type-safe event emitter for a specific node");
|
|
15723
|
+
lines.push(" *");
|
|
15724
|
+
lines.push(" * @example");
|
|
15725
|
+
lines.push(" * ```typescript");
|
|
15726
|
+
lines.push(" * const emit: NodeEmitter<GraphConverter.EventName, GraphConverter.Event> = ...");
|
|
15727
|
+
lines.push(" * emit('conversion.started', {");
|
|
15728
|
+
lines.push(" * name: 'conversion.started',");
|
|
15729
|
+
lines.push(" * attributes: { ... }");
|
|
15730
|
+
lines.push(" * });");
|
|
15731
|
+
lines.push(" * ```");
|
|
15732
|
+
lines.push(" */");
|
|
15733
|
+
}
|
|
15734
|
+
lines.push("export type NodeEmitter<");
|
|
15735
|
+
lines.push(" TEventName extends string,");
|
|
15736
|
+
lines.push(" TEvent extends { name: TEventName; attributes: Record<string, any> }");
|
|
15737
|
+
lines.push("> = (event: TEvent) => void;");
|
|
15738
|
+
lines.push("");
|
|
15739
|
+
if (opts.includeDocComments) {
|
|
15740
|
+
lines.push("/**");
|
|
15741
|
+
lines.push(" * Type-safe event emitter by event name");
|
|
15742
|
+
lines.push(" *");
|
|
15743
|
+
lines.push(" * @example");
|
|
15744
|
+
lines.push(" * ```typescript");
|
|
15745
|
+
lines.push(" * const emit: NodeEmitterByName<GraphConverter.Event> = ...");
|
|
15746
|
+
lines.push(" * emit('conversion.started', {");
|
|
15747
|
+
lines.push(" * 'config.nodeTypes': 2,");
|
|
15748
|
+
lines.push(" * 'config.edgeTypes': 1");
|
|
15749
|
+
lines.push(" * });");
|
|
15750
|
+
lines.push(" * ```");
|
|
15751
|
+
lines.push(" */");
|
|
15752
|
+
}
|
|
15753
|
+
lines.push("export type NodeEmitterByName<");
|
|
15754
|
+
lines.push(" TEvent extends { name: string; attributes: Record<string, any> }");
|
|
15755
|
+
lines.push("> = <TName extends TEvent['name']>(");
|
|
15756
|
+
lines.push(" eventName: TName,");
|
|
15757
|
+
lines.push(" attributes: Extract<TEvent, { name: TName }>['attributes']");
|
|
15758
|
+
lines.push(") => void;");
|
|
15759
|
+
return lines;
|
|
15760
|
+
}
|
|
15761
|
+
nodeIdToTypeName(nodeId) {
|
|
15762
|
+
return nodeId.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
15763
|
+
}
|
|
15764
|
+
eventNameToTypeName(eventName) {
|
|
15765
|
+
return eventName.split(".").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
15766
|
+
}
|
|
15767
|
+
fieldTypeToTsType(fieldSchema) {
|
|
15768
|
+
switch (fieldSchema.type) {
|
|
15769
|
+
case "string":
|
|
15770
|
+
return "string";
|
|
15771
|
+
case "number":
|
|
15772
|
+
return "number";
|
|
15773
|
+
case "boolean":
|
|
15774
|
+
return "boolean";
|
|
15775
|
+
case "array":
|
|
15776
|
+
return "any[]";
|
|
15777
|
+
case "object":
|
|
15778
|
+
return "Record<string, any>";
|
|
15779
|
+
default:
|
|
15780
|
+
return "any";
|
|
15781
|
+
}
|
|
15782
|
+
}
|
|
15783
|
+
generateFilename(canvas) {
|
|
15784
|
+
const name = canvas.pv?.name || "canvas";
|
|
15785
|
+
const kebab = name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
15786
|
+
return `${kebab}.types.ts`;
|
|
15787
|
+
}
|
|
15788
|
+
};
|
|
15789
|
+
var generatorRegistry = {
|
|
15790
|
+
generators: /* @__PURE__ */ new Map([
|
|
15791
|
+
["typescript", new TypeScriptGenerator()]
|
|
15792
|
+
]),
|
|
15793
|
+
register(generator) {
|
|
15794
|
+
this.generators.set(generator.language, generator);
|
|
15795
|
+
},
|
|
15796
|
+
get(language) {
|
|
15797
|
+
return this.generators.get(language);
|
|
15798
|
+
},
|
|
15799
|
+
list() {
|
|
15800
|
+
return Array.from(this.generators.keys());
|
|
15801
|
+
}
|
|
15802
|
+
};
|
|
15803
|
+
|
|
15550
15804
|
// ../core/dist/utils/YamlParser.js
|
|
15551
15805
|
function parseYaml(content, filename) {
|
|
15552
15806
|
try {
|
|
@@ -17832,7 +18086,8 @@ function createLintCommand() {
|
|
|
17832
18086
|
const isLibraryFile = name.startsWith("library.");
|
|
17833
18087
|
const isConfigFile = name.startsWith(".privurc");
|
|
17834
18088
|
const isCanvasFile = f.toLowerCase().endsWith(".canvas");
|
|
17835
|
-
|
|
18089
|
+
const isExecutionArtifact = f.includes("__executions__/");
|
|
18090
|
+
return !isLibraryFile && !isConfigFile && !isCanvasFile && !isExecutionArtifact;
|
|
17836
18091
|
});
|
|
17837
18092
|
if (configFiles.length === 0) {
|
|
17838
18093
|
if (options.json) {
|