@asyncapi/cli 3.0.1 → 3.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/lib/commands/start/preview.d.ts +16 -0
- package/lib/commands/start/preview.js +38 -0
- package/lib/core/flags/start/preview.flags.d.ts +8 -0
- package/lib/core/flags/start/preview.flags.js +15 -0
- package/lib/core/models/Preview.d.ts +2 -0
- package/lib/core/models/Preview.js +195 -0
- package/oclif.manifest.json +73 -1
- package/package.json +2 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Command from '../../core/base';
|
|
2
|
+
export default class PreviewStudio extends Command {
|
|
3
|
+
static readonly description = "starts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing allowed.";
|
|
4
|
+
static readonly flags: {
|
|
5
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
6
|
+
port: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
base: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
baseDir: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
xOrigin: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
suppressLogs: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
static readonly args: {
|
|
13
|
+
'spec-file': import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const base_1 = tslib_1.__importDefault(require("../../core/base"));
|
|
6
|
+
const preview_flags_1 = require("../../core/flags/start/preview.flags");
|
|
7
|
+
const SpecificationFile_1 = require("../../core/models/SpecificationFile");
|
|
8
|
+
const Preview_1 = require("../../core/models/Preview");
|
|
9
|
+
class PreviewStudio extends base_1.default {
|
|
10
|
+
run() {
|
|
11
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
var _a;
|
|
13
|
+
const { args, flags } = yield this.parse(PreviewStudio);
|
|
14
|
+
let filePath = (_a = args['spec-file']) !== null && _a !== void 0 ? _a : flags.file;
|
|
15
|
+
const previewPort = flags.port;
|
|
16
|
+
if (!filePath) {
|
|
17
|
+
filePath = ((yield (0, SpecificationFile_1.load)()).getFilePath());
|
|
18
|
+
this.log(`Loaded the specification from: ${filePath}`);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
this.specFile = yield (0, SpecificationFile_1.load)(filePath);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (filePath) {
|
|
25
|
+
this.error(error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this.metricsMetadata.port = previewPort;
|
|
29
|
+
(0, Preview_1.startPreview)(filePath, flags.base, flags.baseDir, flags.xOrigin, flags.suppressLogs, previewPort);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
PreviewStudio.description = 'starts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing allowed.';
|
|
34
|
+
PreviewStudio.flags = (0, preview_flags_1.previewFlags)();
|
|
35
|
+
PreviewStudio.args = {
|
|
36
|
+
'spec-file': core_1.Args.string({ description: 'the path to the file to be opened with studio or context name', required: true }),
|
|
37
|
+
};
|
|
38
|
+
exports.default = PreviewStudio;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const previewFlags: () => {
|
|
2
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
3
|
+
port: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
|
+
base: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
5
|
+
baseDir: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
xOrigin: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
suppressLogs: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.previewFlags = void 0;
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const previewFlags = () => {
|
|
6
|
+
return {
|
|
7
|
+
help: core_1.Flags.help({ char: 'h' }),
|
|
8
|
+
port: core_1.Flags.integer({ char: 'p', description: 'port in which to start Studio in the preview mode' }),
|
|
9
|
+
base: core_1.Flags.string({ char: 'b', description: 'Path to the file which will act as a base. This is required when some properties need to be overwritten while bundling with the file.' }),
|
|
10
|
+
baseDir: core_1.Flags.string({ char: 'd', description: 'One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.' }),
|
|
11
|
+
xOrigin: core_1.Flags.boolean({ char: 'x', description: 'Pass this switch to generate properties "x-origin" that will contain historical values of dereferenced "$ref"s.' }),
|
|
12
|
+
suppressLogs: core_1.Flags.boolean({ char: 'l', description: 'Pass this to suppress the detiled error logs.', default: false })
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
exports.previewFlags = previewFlags;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_PORT = void 0;
|
|
4
|
+
exports.startPreview = startPreview;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const specification_file_1 = require("../errors/specification-file");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const bundler_1 = tslib_1.__importDefault(require("@asyncapi/bundler"));
|
|
9
|
+
const http_1 = require("http");
|
|
10
|
+
const ws_1 = require("ws");
|
|
11
|
+
const chokidar_1 = tslib_1.__importDefault(require("chokidar"));
|
|
12
|
+
const open_1 = tslib_1.__importDefault(require("open"));
|
|
13
|
+
const next_1 = tslib_1.__importDefault(require("next"));
|
|
14
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
|
+
const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
|
|
16
|
+
const picocolors_1 = require("picocolors");
|
|
17
|
+
const package_json_1 = require("@asyncapi/studio/package.json");
|
|
18
|
+
const sockets = [];
|
|
19
|
+
const messageQueue = [];
|
|
20
|
+
const filePathsToWatch = new Set();
|
|
21
|
+
const defaultErrorMessage = 'error occured while bundling files. use --detailedLog or -l flag to get more details.';
|
|
22
|
+
let bundleError = true;
|
|
23
|
+
exports.DEFAULT_PORT = 3210;
|
|
24
|
+
function isValidFilePath(filePath) {
|
|
25
|
+
return (0, fs_1.existsSync)(filePath);
|
|
26
|
+
}
|
|
27
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
28
|
+
function startPreview(filePath, base, baseDirectory, xOrigin, suppressLogs, port = exports.DEFAULT_PORT) {
|
|
29
|
+
if (filePath && !isValidFilePath(filePath)) {
|
|
30
|
+
throw new specification_file_1.SpecificationFileNotFound(filePath);
|
|
31
|
+
}
|
|
32
|
+
const baseDir = path_1.default.dirname(path_1.default.resolve(filePath));
|
|
33
|
+
(0, bundler_1.default)(filePath).then((doc) => {
|
|
34
|
+
if (doc) {
|
|
35
|
+
bundleError = false;
|
|
36
|
+
}
|
|
37
|
+
}).catch((err) => {
|
|
38
|
+
if (suppressLogs) {
|
|
39
|
+
console.log(defaultErrorMessage);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.log(err);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const studioPath = path_1.default.dirname(require.resolve('@asyncapi/studio/package.json'));
|
|
46
|
+
const app = (0, next_1.default)({
|
|
47
|
+
dev: false,
|
|
48
|
+
dir: studioPath,
|
|
49
|
+
conf: {
|
|
50
|
+
distDir: 'build',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const handle = app.getRequestHandler();
|
|
54
|
+
const wsServer = new ws_1.WebSocketServer({ noServer: true });
|
|
55
|
+
wsServer.on('connection', (socket) => {
|
|
56
|
+
sockets.push(socket);
|
|
57
|
+
sendQueuedMessages();
|
|
58
|
+
});
|
|
59
|
+
wsServer.on('close', (socket) => {
|
|
60
|
+
sockets.splice(sockets.findIndex(s => s === socket));
|
|
61
|
+
});
|
|
62
|
+
app.prepare().then(() => {
|
|
63
|
+
if (filePath && !bundleError) {
|
|
64
|
+
messageQueue.push(JSON.stringify({
|
|
65
|
+
type: 'preview:connected',
|
|
66
|
+
code: 'Preview server connected'
|
|
67
|
+
}));
|
|
68
|
+
sendQueuedMessages();
|
|
69
|
+
findPathsToWatchFromSchemaRef(filePath, baseDir);
|
|
70
|
+
filePathsToWatch.add(path_1.default.resolve(baseDir, filePath));
|
|
71
|
+
chokidar_1.default.watch([...filePathsToWatch]).on('all', (event) => {
|
|
72
|
+
switch (event) {
|
|
73
|
+
case 'add':
|
|
74
|
+
(0, bundler_1.default)([filePath], {
|
|
75
|
+
base,
|
|
76
|
+
baseDir: baseDirectory,
|
|
77
|
+
xOrigin,
|
|
78
|
+
}).then((intitalDocument) => {
|
|
79
|
+
messageQueue.push(JSON.stringify({
|
|
80
|
+
type: 'preview:file:added',
|
|
81
|
+
code: (path_1.default.extname(filePath) === '.yaml' || path_1.default.extname(filePath) === '.yml') ?
|
|
82
|
+
intitalDocument.yml() : intitalDocument.string()
|
|
83
|
+
}));
|
|
84
|
+
sendQueuedMessages();
|
|
85
|
+
}).catch((e) => {
|
|
86
|
+
if (suppressLogs) {
|
|
87
|
+
console.log(defaultErrorMessage);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log(e);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
break;
|
|
94
|
+
case 'change':
|
|
95
|
+
(0, bundler_1.default)([filePath], {
|
|
96
|
+
base,
|
|
97
|
+
baseDir: baseDirectory,
|
|
98
|
+
xOrigin,
|
|
99
|
+
}).then((modifiedDocument) => {
|
|
100
|
+
messageQueue.push(JSON.stringify({
|
|
101
|
+
type: 'preview:file:changed',
|
|
102
|
+
code: (path_1.default.extname(filePath) === '.yaml' || path_1.default.extname(filePath) === '.yml') ?
|
|
103
|
+
modifiedDocument.yml() : modifiedDocument.string()
|
|
104
|
+
}));
|
|
105
|
+
sendQueuedMessages();
|
|
106
|
+
}).catch((error) => {
|
|
107
|
+
if (suppressLogs) {
|
|
108
|
+
console.log(defaultErrorMessage);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(error);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
break;
|
|
115
|
+
case 'unlink':
|
|
116
|
+
messageQueue.push(JSON.stringify({
|
|
117
|
+
type: 'preview:file:deleted',
|
|
118
|
+
filePath,
|
|
119
|
+
}));
|
|
120
|
+
sendQueuedMessages();
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const server = (0, http_1.createServer)((req, res) => handle(req, res));
|
|
126
|
+
server.on('upgrade', (request, socket, head) => {
|
|
127
|
+
if (request.url === '/preview-server' && request.headers['origin'] === `http://localhost:${port}`) {
|
|
128
|
+
console.log('🔗 WebSocket connection established for the preview.');
|
|
129
|
+
wsServer.handleUpgrade(request, socket, head, (sock) => {
|
|
130
|
+
wsServer.emit('connection', sock, request);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.log('🔗 WebSocket connection not established.');
|
|
135
|
+
socket.destroy();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
if (!bundleError) {
|
|
139
|
+
server.listen(port, () => {
|
|
140
|
+
const url = `http://localhost:${port}?previewServer=${port}&studio-version=${package_json_1.version}`;
|
|
141
|
+
console.log(`🎉 Connected to Preview Server running at ${(0, picocolors_1.blueBright)(url)}.`);
|
|
142
|
+
console.log(`🌐 Open this URL in your web browser: ${(0, picocolors_1.blueBright)(url)}`);
|
|
143
|
+
console.log(`🛑 If needed, press ${(0, picocolors_1.redBright)('Ctrl + C')} to stop the server.`);
|
|
144
|
+
if (filePath) {
|
|
145
|
+
for (const entry of filePathsToWatch) {
|
|
146
|
+
console.log(`👁️ Watching changes on file ${(0, picocolors_1.blueBright)(entry)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.warn('Warning: No file was provided, and we couldn\'t find a default file (like "asyncapi.yaml" or "asyncapi.json") in the current folder. Starting Studio with a blank workspace.');
|
|
151
|
+
}
|
|
152
|
+
if (!bundleError) {
|
|
153
|
+
(0, open_1.default)(url);
|
|
154
|
+
}
|
|
155
|
+
}).on('error', (error) => {
|
|
156
|
+
console.error(`Failed to start server on port ${port}:`, error.message);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function sendQueuedMessages() {
|
|
162
|
+
while (messageQueue.length && sockets.length) {
|
|
163
|
+
const nextMessage = messageQueue.shift();
|
|
164
|
+
for (const socket of sockets) {
|
|
165
|
+
socket.send(nextMessage);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function isLocalRefAPath(key, value) {
|
|
170
|
+
return (typeof value === 'string' && key === '$ref' &&
|
|
171
|
+
(value.startsWith('.') || value.startsWith('./') ||
|
|
172
|
+
value.startsWith('../') || !value.startsWith('#')));
|
|
173
|
+
}
|
|
174
|
+
function findPathsToWatchFromSchemaRef(filePath, baseDir) {
|
|
175
|
+
if (filePath && !isValidFilePath(filePath)) {
|
|
176
|
+
throw new specification_file_1.SpecificationFileNotFound(filePath);
|
|
177
|
+
}
|
|
178
|
+
const document = js_yaml_1.default.load((0, fs_1.readFileSync)(filePath, 'utf-8'));
|
|
179
|
+
const stack = [document];
|
|
180
|
+
while (stack.length > 0) {
|
|
181
|
+
const current = stack.pop();
|
|
182
|
+
if (current === null || typeof current !== 'object') {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
for (const [key, value] of Object.entries(current)) {
|
|
186
|
+
if (isLocalRefAPath(key, value)) {
|
|
187
|
+
const absolutePath = path_1.default.resolve(baseDir, value);
|
|
188
|
+
filePathsToWatch.add(absolutePath);
|
|
189
|
+
}
|
|
190
|
+
if (value !== null && typeof value === 'object') {
|
|
191
|
+
stack.push(value);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1451,6 +1451,78 @@
|
|
|
1451
1451
|
"index.js"
|
|
1452
1452
|
]
|
|
1453
1453
|
},
|
|
1454
|
+
"start:preview": {
|
|
1455
|
+
"aliases": [],
|
|
1456
|
+
"args": {
|
|
1457
|
+
"spec-file": {
|
|
1458
|
+
"description": "the path to the file to be opened with studio or context name",
|
|
1459
|
+
"name": "spec-file",
|
|
1460
|
+
"required": true
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
"description": "starts a new local instance of Studio in minimal state bundling all the refs of the schema file and with no editing allowed.",
|
|
1464
|
+
"flags": {
|
|
1465
|
+
"help": {
|
|
1466
|
+
"char": "h",
|
|
1467
|
+
"description": "Show CLI help.",
|
|
1468
|
+
"name": "help",
|
|
1469
|
+
"allowNo": false,
|
|
1470
|
+
"type": "boolean"
|
|
1471
|
+
},
|
|
1472
|
+
"port": {
|
|
1473
|
+
"char": "p",
|
|
1474
|
+
"description": "port in which to start Studio in the preview mode",
|
|
1475
|
+
"name": "port",
|
|
1476
|
+
"hasDynamicHelp": false,
|
|
1477
|
+
"multiple": false,
|
|
1478
|
+
"type": "option"
|
|
1479
|
+
},
|
|
1480
|
+
"base": {
|
|
1481
|
+
"char": "b",
|
|
1482
|
+
"description": "Path to the file which will act as a base. This is required when some properties need to be overwritten while bundling with the file.",
|
|
1483
|
+
"name": "base",
|
|
1484
|
+
"hasDynamicHelp": false,
|
|
1485
|
+
"multiple": false,
|
|
1486
|
+
"type": "option"
|
|
1487
|
+
},
|
|
1488
|
+
"baseDir": {
|
|
1489
|
+
"char": "d",
|
|
1490
|
+
"description": "One relative/absolute path to directory relative to which paths to AsyncAPI Documents that should be bundled will be resolved.",
|
|
1491
|
+
"name": "baseDir",
|
|
1492
|
+
"hasDynamicHelp": false,
|
|
1493
|
+
"multiple": false,
|
|
1494
|
+
"type": "option"
|
|
1495
|
+
},
|
|
1496
|
+
"xOrigin": {
|
|
1497
|
+
"char": "x",
|
|
1498
|
+
"description": "Pass this switch to generate properties \"x-origin\" that will contain historical values of dereferenced \"$ref\"s.",
|
|
1499
|
+
"name": "xOrigin",
|
|
1500
|
+
"allowNo": false,
|
|
1501
|
+
"type": "boolean"
|
|
1502
|
+
},
|
|
1503
|
+
"suppressLogs": {
|
|
1504
|
+
"char": "l",
|
|
1505
|
+
"description": "Pass this to suppress the detiled error logs.",
|
|
1506
|
+
"name": "suppressLogs",
|
|
1507
|
+
"allowNo": false,
|
|
1508
|
+
"type": "boolean"
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
"hasDynamicHelp": false,
|
|
1512
|
+
"hiddenAliases": [],
|
|
1513
|
+
"id": "start:preview",
|
|
1514
|
+
"pluginAlias": "@asyncapi/cli",
|
|
1515
|
+
"pluginName": "@asyncapi/cli",
|
|
1516
|
+
"pluginType": "core",
|
|
1517
|
+
"strict": true,
|
|
1518
|
+
"isESM": false,
|
|
1519
|
+
"relativePath": [
|
|
1520
|
+
"lib",
|
|
1521
|
+
"commands",
|
|
1522
|
+
"start",
|
|
1523
|
+
"preview.js"
|
|
1524
|
+
]
|
|
1525
|
+
},
|
|
1454
1526
|
"start:studio": {
|
|
1455
1527
|
"aliases": [],
|
|
1456
1528
|
"args": {
|
|
@@ -1776,5 +1848,5 @@
|
|
|
1776
1848
|
]
|
|
1777
1849
|
}
|
|
1778
1850
|
},
|
|
1779
|
-
"version": "3.0
|
|
1851
|
+
"version": "3.1.0"
|
|
1780
1852
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asyncapi/cli",
|
|
3
3
|
"description": "All in one CLI for all AsyncAPI tools",
|
|
4
|
-
"version": "3.0
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"author": "@asyncapi",
|
|
6
6
|
"bin": {
|
|
7
7
|
"asyncapi": "./bin/run_bin"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@asyncapi/parser": "^3.3.0",
|
|
20
20
|
"@asyncapi/protobuf-schema-parser": "^3.5.1",
|
|
21
21
|
"@asyncapi/raml-dt-schema-parser": "^4.0.24",
|
|
22
|
-
"@asyncapi/studio": "^0.
|
|
22
|
+
"@asyncapi/studio": "^0.24.2",
|
|
23
23
|
"@changesets/changelog-git": "^0.2.0",
|
|
24
24
|
"@clack/prompts": "^0.7.0",
|
|
25
25
|
"@oclif/core": "^4.2.9",
|