@angeloashmore/prismic-cli-poc 0.0.0-pr.7.1e5b475 → 0.0.0-pr.8.b80fefa
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.mjs +115 -96
- package/package.json +1 -1
- package/src/custom-type-create.ts +8 -1
- package/src/docs.ts +149 -0
- package/src/index.ts +5 -0
- package/src/page-type-create.ts +32 -2
- package/src/status.ts +113 -13
package/package.json
CHANGED
|
@@ -59,7 +59,14 @@ export async function customTypeCreate(): Promise<void> {
|
|
|
59
59
|
status: true,
|
|
60
60
|
format: "custom",
|
|
61
61
|
json: {
|
|
62
|
-
Main:
|
|
62
|
+
Main: single
|
|
63
|
+
? {}
|
|
64
|
+
: {
|
|
65
|
+
uid: {
|
|
66
|
+
type: "UID",
|
|
67
|
+
config: { label: "UID", placeholder: "" },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
63
70
|
},
|
|
64
71
|
} satisfies CustomType;
|
|
65
72
|
|
package/src/docs.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
|
|
3
|
+
const HELP = `
|
|
4
|
+
Fetch and display documentation from Prismic's docs site.
|
|
5
|
+
|
|
6
|
+
USAGE
|
|
7
|
+
prismic docs <path> [flags]
|
|
8
|
+
|
|
9
|
+
ARGUMENTS
|
|
10
|
+
path Documentation path with optional anchor (e.g., "nextjs" or "nextjs#set-up-a-prismic-client")
|
|
11
|
+
|
|
12
|
+
FLAGS
|
|
13
|
+
-h, --help Show help for command
|
|
14
|
+
|
|
15
|
+
EXAMPLES
|
|
16
|
+
prismic docs nextjs
|
|
17
|
+
prismic docs nextjs#set-up-a-prismic-client
|
|
18
|
+
|
|
19
|
+
LEARN MORE
|
|
20
|
+
Visit https://prismic.io/docs for the full documentation.
|
|
21
|
+
`.trim();
|
|
22
|
+
|
|
23
|
+
function parsePathAndAnchor(input: string): { path: string; anchor?: string } {
|
|
24
|
+
const hashIndex = input.indexOf("#");
|
|
25
|
+
if (hashIndex === -1) {
|
|
26
|
+
return { path: input };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
path: input.slice(0, hashIndex),
|
|
30
|
+
anchor: input.slice(hashIndex + 1),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function fetchMarkdown(
|
|
35
|
+
url: string,
|
|
36
|
+
): Promise<{ ok: true; content: string } | { ok: false; error: string }> {
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(url);
|
|
39
|
+
if (response.status === 404) {
|
|
40
|
+
return { ok: false, error: `Documentation not found: ${url}` };
|
|
41
|
+
}
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: `Failed to fetch documentation: ${response.status}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const content = await response.text();
|
|
49
|
+
return { ok: true, content };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
52
|
+
return { ok: false, error: `Network error: ${message}` };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function anchorToHeadingPattern(anchor: string): RegExp {
|
|
57
|
+
// Convert kebab-case anchor to a pattern that matches the heading text
|
|
58
|
+
// Each hyphen/space becomes a flexible match for hyphens or spaces
|
|
59
|
+
const pattern = anchor
|
|
60
|
+
.split("-")
|
|
61
|
+
.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
62
|
+
.join("[\\s-]+");
|
|
63
|
+
return new RegExp(`^(#{1,6})\\s+${pattern}\\s*$`, "im");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function extractSection(
|
|
67
|
+
markdown: string,
|
|
68
|
+
anchor: string,
|
|
69
|
+
): { ok: true; content: string } | { ok: false; error: string } {
|
|
70
|
+
const lines = markdown.split("\n");
|
|
71
|
+
const headingPattern = anchorToHeadingPattern(anchor);
|
|
72
|
+
|
|
73
|
+
let startIndex = -1;
|
|
74
|
+
let headingLevel = 0;
|
|
75
|
+
|
|
76
|
+
// Find the matching heading
|
|
77
|
+
for (let i = 0; i < lines.length; i++) {
|
|
78
|
+
const match = lines[i].match(headingPattern);
|
|
79
|
+
if (match) {
|
|
80
|
+
startIndex = i;
|
|
81
|
+
headingLevel = match[1].length;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (startIndex === -1) {
|
|
87
|
+
return { ok: false, error: `Anchor not found: #${anchor}` };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Find the end of this section (next heading of equal or lower level number)
|
|
91
|
+
let endIndex = lines.length;
|
|
92
|
+
for (let i = startIndex + 1; i < lines.length; i++) {
|
|
93
|
+
const headingMatch = lines[i].match(/^(#{1,6})\s/);
|
|
94
|
+
if (headingMatch && headingMatch[1].length <= headingLevel) {
|
|
95
|
+
endIndex = i;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const content = lines.slice(startIndex, endIndex).join("\n").trim();
|
|
101
|
+
return { ok: true, content };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function docs(): Promise<void> {
|
|
105
|
+
const {
|
|
106
|
+
positionals: [pathArg],
|
|
107
|
+
values: { help },
|
|
108
|
+
} = parseArgs({
|
|
109
|
+
args: process.argv.slice(3),
|
|
110
|
+
options: {
|
|
111
|
+
help: { type: "boolean", short: "h" },
|
|
112
|
+
},
|
|
113
|
+
allowPositionals: true,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (help) {
|
|
117
|
+
console.info(HELP);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!pathArg) {
|
|
122
|
+
console.info(HELP);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const { path, anchor } = parsePathAndAnchor(pathArg);
|
|
127
|
+
const url = `https://prismic.io/docs/${path}.md`;
|
|
128
|
+
|
|
129
|
+
const fetchResult = await fetchMarkdown(url);
|
|
130
|
+
if (!fetchResult.ok) {
|
|
131
|
+
console.error(fetchResult.error);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let output = fetchResult.content;
|
|
137
|
+
|
|
138
|
+
if (anchor) {
|
|
139
|
+
const extractResult = extractSection(output, anchor);
|
|
140
|
+
if (!extractResult.ok) {
|
|
141
|
+
console.error(extractResult.error);
|
|
142
|
+
process.exitCode = 1;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
output = extractResult.content;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.info(output);
|
|
149
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { parseArgs } from "node:util";
|
|
|
5
5
|
import packageJson from "../package.json" with { type: "json" };
|
|
6
6
|
import { codegen } from "./codegen";
|
|
7
7
|
import { customType } from "./custom-type";
|
|
8
|
+
import { docs } from "./docs";
|
|
8
9
|
import { init } from "./init";
|
|
9
10
|
import { locale } from "./locale";
|
|
10
11
|
import { login } from "./login";
|
|
@@ -40,6 +41,7 @@ COMMANDS
|
|
|
40
41
|
pull Pull types and slices from Prismic
|
|
41
42
|
push Push types and slices to Prismic
|
|
42
43
|
codegen Generate code from Prismic models
|
|
44
|
+
docs Fetch documentation from Prismic
|
|
43
45
|
preview Manage preview configurations
|
|
44
46
|
token Manage API tokens in a repository
|
|
45
47
|
webhook Manage webhooks in a repository
|
|
@@ -107,6 +109,9 @@ if (version) {
|
|
|
107
109
|
case "codegen":
|
|
108
110
|
await codegen();
|
|
109
111
|
break;
|
|
112
|
+
case "docs":
|
|
113
|
+
await docs();
|
|
114
|
+
break;
|
|
110
115
|
case "preview":
|
|
111
116
|
await preview();
|
|
112
117
|
break;
|
package/src/page-type-create.ts
CHANGED
|
@@ -59,8 +59,38 @@ export async function pageTypeCreate(): Promise<void> {
|
|
|
59
59
|
status: true,
|
|
60
60
|
format: "page",
|
|
61
61
|
json: {
|
|
62
|
-
Main:
|
|
63
|
-
|
|
62
|
+
Main: single
|
|
63
|
+
? {}
|
|
64
|
+
: {
|
|
65
|
+
uid: {
|
|
66
|
+
type: "UID",
|
|
67
|
+
config: { label: "UID", placeholder: "" },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
"SEO & Metadata": {
|
|
71
|
+
meta_title: {
|
|
72
|
+
type: "Text",
|
|
73
|
+
config: {
|
|
74
|
+
label: "Meta Title",
|
|
75
|
+
placeholder: "A title of the page used for social media and search engines",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
meta_description: {
|
|
79
|
+
type: "Text",
|
|
80
|
+
config: {
|
|
81
|
+
label: "Meta Description",
|
|
82
|
+
placeholder: "A brief summary of the page",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
meta_image: {
|
|
86
|
+
type: "Image",
|
|
87
|
+
config: {
|
|
88
|
+
label: "Meta Image",
|
|
89
|
+
constraint: { width: 2400, height: 1260 },
|
|
90
|
+
thumbnails: [],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
64
94
|
},
|
|
65
95
|
} satisfies CustomType;
|
|
66
96
|
|
package/src/status.ts
CHANGED
|
@@ -66,16 +66,53 @@ type NextStep = {
|
|
|
66
66
|
message: string;
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
function
|
|
69
|
+
function getDocsPath(framework: Framework | undefined): string {
|
|
70
70
|
switch (framework) {
|
|
71
71
|
case "next":
|
|
72
|
-
return "
|
|
72
|
+
return "nextjs/with-cli";
|
|
73
73
|
case "nuxt":
|
|
74
|
-
return "
|
|
74
|
+
return "nuxt/with-cli";
|
|
75
75
|
case "sveltekit":
|
|
76
|
-
return "
|
|
76
|
+
return "sveltekit/with-cli";
|
|
77
77
|
default:
|
|
78
|
-
return "
|
|
78
|
+
return "";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getDocsRef(docsPath: string, anchor?: string): string {
|
|
83
|
+
if (!docsPath) return "";
|
|
84
|
+
const fullPath = anchor ? `${docsPath}${anchor}` : docsPath;
|
|
85
|
+
return ` (run \`prismic docs ${fullPath}\`)`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getClientSetupAnchor(framework: Framework | undefined): string {
|
|
89
|
+
switch (framework) {
|
|
90
|
+
case "nuxt":
|
|
91
|
+
return "#configure-the-modules-prismic-client";
|
|
92
|
+
default:
|
|
93
|
+
return "#set-up-a-prismic-client";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getPreviewSetupAnchor(framework: Framework | undefined): string {
|
|
98
|
+
switch (framework) {
|
|
99
|
+
case "next":
|
|
100
|
+
return "#set-up-previews-in-next-js";
|
|
101
|
+
case "sveltekit":
|
|
102
|
+
return "#set-up-previews-in-sveltekit";
|
|
103
|
+
default:
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getWriteComponentsAnchor(framework: Framework | undefined): string {
|
|
109
|
+
switch (framework) {
|
|
110
|
+
case "nuxt":
|
|
111
|
+
return "#write-vue-components";
|
|
112
|
+
case "sveltekit":
|
|
113
|
+
return "#write-svelte-components";
|
|
114
|
+
default:
|
|
115
|
+
return "#write-react-components";
|
|
79
116
|
}
|
|
80
117
|
}
|
|
81
118
|
|
|
@@ -85,8 +122,9 @@ function computeNextStep(
|
|
|
85
122
|
typeStatuses: TypeWithStatus[],
|
|
86
123
|
sliceStatuses: TypeWithStatus[],
|
|
87
124
|
slicesWithMissingComponents: string[],
|
|
125
|
+
slicesReadyToConnect: string[],
|
|
88
126
|
): NextStep | undefined {
|
|
89
|
-
const
|
|
127
|
+
const docsPath = getDocsPath(frameworkInfo.framework);
|
|
90
128
|
|
|
91
129
|
// 1. Setup - missing dependencies
|
|
92
130
|
const setupSection = sections.find((s) => s.title === "Setup");
|
|
@@ -99,7 +137,7 @@ function computeNextStep(
|
|
|
99
137
|
// 2. Setup - missing client file
|
|
100
138
|
const missingClientFile = setupSection?.items.find((i) => !i.done && i.hint?.includes("client"));
|
|
101
139
|
if (missingClientFile) {
|
|
102
|
-
return { message: `Create a ${missingClientFile.label} file (
|
|
140
|
+
return { message: `Create a ${missingClientFile.label} file${getDocsRef(docsPath, getClientSetupAnchor(frameworkInfo.framework))}` };
|
|
103
141
|
}
|
|
104
142
|
|
|
105
143
|
// 3-7. Preview section (in order: local files, then remote config)
|
|
@@ -110,21 +148,21 @@ function computeNextStep(
|
|
|
110
148
|
(i) => i.label === "/slice-simulator route" && !i.done,
|
|
111
149
|
);
|
|
112
150
|
if (sliceSimRoute) {
|
|
113
|
-
return { message: `Create the /slice-simulator route
|
|
151
|
+
return { message: `Create the /slice-simulator route${getDocsRef(docsPath, "#set-up-live-previewing")}` };
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
const apiPreview = previewSection.items.find(
|
|
117
155
|
(i) => i.label === "/api/preview endpoint" && !i.done,
|
|
118
156
|
);
|
|
119
157
|
if (apiPreview) {
|
|
120
|
-
return { message: `Create the /api/preview route (
|
|
158
|
+
return { message: `Create the /api/preview route${getDocsRef(docsPath, getPreviewSetupAnchor(frameworkInfo.framework))}` };
|
|
121
159
|
}
|
|
122
160
|
|
|
123
161
|
const exitPreview = previewSection.items.find(
|
|
124
162
|
(i) => i.label === "/api/exit-preview endpoint" && !i.done,
|
|
125
163
|
);
|
|
126
164
|
if (exitPreview) {
|
|
127
|
-
return { message: `Create the /api/exit-preview route (
|
|
165
|
+
return { message: `Create the /api/exit-preview route${getDocsRef(docsPath, getPreviewSetupAnchor(frameworkInfo.framework))}` };
|
|
128
166
|
}
|
|
129
167
|
|
|
130
168
|
// Remote config
|
|
@@ -151,6 +189,15 @@ function computeNextStep(
|
|
|
151
189
|
return { message: `Pull remote models with 'prismic pull'` };
|
|
152
190
|
}
|
|
153
191
|
|
|
192
|
+
// 8.5 Slices ready to connect to a page type (before pushing)
|
|
193
|
+
if (slicesReadyToConnect.length > 0) {
|
|
194
|
+
const sorted = [...slicesReadyToConnect].sort();
|
|
195
|
+
const sliceName = sorted[0];
|
|
196
|
+
return {
|
|
197
|
+
message: `Connect ${sliceName} to a page type with 'prismic page-type connect-slice <type-id> ${sliceName}'`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
154
201
|
// 9. Models to push
|
|
155
202
|
const hasToPush =
|
|
156
203
|
typeStatuses.some((t) => t.status === "to_push") ||
|
|
@@ -166,7 +213,7 @@ function computeNextStep(
|
|
|
166
213
|
const slicesDir = getSlicesDirectory(frameworkInfo);
|
|
167
214
|
const ext = getSliceComponentExtensions(frameworkInfo.framework)[0];
|
|
168
215
|
const path = `${slicesDir}${sliceName}/index${ext}`;
|
|
169
|
-
return { message: `Implement the ${sliceName} slice component at ${path} (
|
|
216
|
+
return { message: `Implement the ${sliceName} slice component at ${path}${getDocsRef(docsPath, getWriteComponentsAnchor(frameworkInfo.framework))}` };
|
|
170
217
|
}
|
|
171
218
|
|
|
172
219
|
// 11-12. Deployment (Next.js only)
|
|
@@ -176,7 +223,7 @@ function computeNextStep(
|
|
|
176
223
|
(i) => i.label === "/api/revalidate endpoint" && !i.done,
|
|
177
224
|
);
|
|
178
225
|
if (revalidateEndpoint) {
|
|
179
|
-
return { message: `Create the /api/revalidate route for ISR
|
|
226
|
+
return { message: `Create the /api/revalidate route for ISR${getDocsRef(docsPath, "#handle-content-changes")}` };
|
|
180
227
|
}
|
|
181
228
|
|
|
182
229
|
const webhook = deploymentSection.items.find(
|
|
@@ -261,6 +308,7 @@ export async function status(): Promise<void> {
|
|
|
261
308
|
let typeStatuses: TypeWithStatus[] = [];
|
|
262
309
|
let sliceStatuses: TypeWithStatus[] = [];
|
|
263
310
|
let slicesWithMissingComponents: string[] = [];
|
|
311
|
+
let slicesReadyToConnect: string[] = [];
|
|
264
312
|
|
|
265
313
|
// Setup section
|
|
266
314
|
const setupSection = await buildSetupSection(frameworkInfo, installedDeps);
|
|
@@ -283,10 +331,17 @@ export async function status(): Promise<void> {
|
|
|
283
331
|
section: slicesSection,
|
|
284
332
|
statuses,
|
|
285
333
|
missingComponents,
|
|
286
|
-
|
|
334
|
+
slicesReadyToConnect: readyToConnect,
|
|
335
|
+
} = await buildSlicesSection(
|
|
336
|
+
localSlicesResult.value,
|
|
337
|
+
remoteSlicesResult.value,
|
|
338
|
+
frameworkInfo,
|
|
339
|
+
localTypesResult.ok ? localTypesResult.value : [],
|
|
340
|
+
);
|
|
287
341
|
sections.push(slicesSection);
|
|
288
342
|
sliceStatuses = statuses;
|
|
289
343
|
slicesWithMissingComponents = missingComponents;
|
|
344
|
+
slicesReadyToConnect = readyToConnect;
|
|
290
345
|
}
|
|
291
346
|
|
|
292
347
|
// Preview section
|
|
@@ -318,6 +373,7 @@ export async function status(): Promise<void> {
|
|
|
318
373
|
typeStatuses,
|
|
319
374
|
sliceStatuses,
|
|
320
375
|
slicesWithMissingComponents,
|
|
376
|
+
slicesReadyToConnect,
|
|
321
377
|
);
|
|
322
378
|
if (nextStep) {
|
|
323
379
|
console.info(`Next: ${nextStep.message}`);
|
|
@@ -554,23 +610,66 @@ function statusToHint(status: TypeStatus): string | undefined {
|
|
|
554
610
|
}
|
|
555
611
|
|
|
556
612
|
// Slices Section
|
|
613
|
+
function sliceHasFields(slice: SharedSlice): boolean {
|
|
614
|
+
for (const variation of slice.variations) {
|
|
615
|
+
const primaryFields = Object.keys(variation.primary ?? {});
|
|
616
|
+
const itemFields = Object.keys(variation.items ?? {});
|
|
617
|
+
if (primaryFields.length > 0 || itemFields.length > 0) {
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function isSliceConnectedToAnyType(sliceId: string, localTypes: CustomType[]): boolean {
|
|
625
|
+
for (const type of localTypes) {
|
|
626
|
+
for (const tabFields of Object.values(type.json)) {
|
|
627
|
+
for (const field of Object.values(tabFields as Record<string, unknown>)) {
|
|
628
|
+
const typedField = field as {
|
|
629
|
+
type?: string;
|
|
630
|
+
config?: { choices?: Record<string, unknown> };
|
|
631
|
+
};
|
|
632
|
+
if (typedField.type === "Slices" && typedField.config?.choices?.[sliceId]) {
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
|
|
557
641
|
async function buildSlicesSection(
|
|
558
642
|
localSlices: SharedSlice[],
|
|
559
643
|
remoteSlices: SharedSlice[],
|
|
560
644
|
info: FrameworkInfo,
|
|
645
|
+
localTypes: CustomType[],
|
|
561
646
|
): Promise<{
|
|
562
647
|
section: StatusSection;
|
|
563
648
|
statuses: TypeWithStatus[];
|
|
564
649
|
missingComponents: string[];
|
|
650
|
+
slicesReadyToConnect: string[];
|
|
565
651
|
}> {
|
|
566
652
|
const sliceStatuses = computeTypeStatus(localSlices, remoteSlices);
|
|
567
653
|
const items: StatusItem[] = [];
|
|
568
654
|
const missingComponents: string[] = [];
|
|
655
|
+
const slicesReadyToConnect: string[] = [];
|
|
569
656
|
|
|
570
657
|
const slicesDir = getSlicesDirectory(info);
|
|
571
658
|
const extensions = getSliceComponentExtensions(info.framework);
|
|
572
659
|
|
|
573
660
|
for (const slice of sliceStatuses) {
|
|
661
|
+
const localSlice = localSlices.find((s) => s.id === slice.id);
|
|
662
|
+
|
|
663
|
+
// Track slices that have fields but aren't connected to any type
|
|
664
|
+
// These should be connected before pushing
|
|
665
|
+
if (localSlice) {
|
|
666
|
+
const hasFields = sliceHasFields(localSlice);
|
|
667
|
+
const isConnected = isSliceConnectedToAnyType(slice.id, localTypes);
|
|
668
|
+
|
|
669
|
+
if (hasFields && !isConnected) {
|
|
670
|
+
slicesReadyToConnect.push(slice.label);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
574
673
|
// Check if component is implemented
|
|
575
674
|
const componentExists = await checkSliceComponent(info, slicesDir, slice.id, extensions);
|
|
576
675
|
|
|
@@ -600,6 +699,7 @@ async function buildSlicesSection(
|
|
|
600
699
|
section: { title: "Slices", items },
|
|
601
700
|
statuses: sliceStatuses,
|
|
602
701
|
missingComponents,
|
|
702
|
+
slicesReadyToConnect,
|
|
603
703
|
};
|
|
604
704
|
}
|
|
605
705
|
|