Skip to content

Commit e7e65e5

Browse files
Merge pull request #272 from contentstack/feat/DX-544-add-boilerplates
Feat/dx 544 add boilerplates
2 parents 0640d31 + f92ac85 commit e7e65e5

File tree

7 files changed

+68
-14
lines changed

7 files changed

+68
-14
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@contentstack/apps-cli",
3-
"version": "1.2.1",
3+
"version": "1.3.0",
44
"description": "App ClI",
55
"author": "Contentstack CLI",
66
"homepage": "https://github.com/contentstack/contentstack-apps-cli",

src/commands/app/create.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,19 @@ import {
2121
flags,
2222
HttpClient,
2323
configHandler,
24-
FlagInput
24+
FlagInput,
2525
} from "@contentstack/cli-utilities";
2626

2727
import { BaseCommand } from "../../base-command";
28-
import { AppManifest, AppType } from "../../types";
28+
import { AppManifest, AppType, BoilerplateAppType } from "../../types";
2929
import { appCreate, commonMsg } from "../../messages";
3030
import {
3131
getOrg,
3232
getAppName,
3333
getDirName,
3434
getOrgAppUiLocation,
35-
sanitizePath
35+
sanitizePath,
36+
selectedBoilerplate,
3637
} from "../../util";
3738

3839
export default class Create extends BaseCommand<typeof Create> {
@@ -96,7 +97,18 @@ export default class Create extends BaseCommand<typeof Create> {
9697
message: this.messages.CONFIRM_CLONE_BOILERPLATE,
9798
}))
9899
) {
99-
await this.boilerplateFlow();
100+
const boilerplate: BoilerplateAppType = await selectedBoilerplate();
101+
102+
if (boilerplate) {
103+
this.sharedConfig.boilerplateName = boilerplate.name
104+
.toLowerCase()
105+
.replace(/ /g, "-");
106+
this.sharedConfig.appBoilerplateGithubUrl = boilerplate.link;
107+
this.sharedConfig.appName = await getAppName(
108+
this.sharedConfig.boilerplateName
109+
);
110+
await this.boilerplateFlow();
111+
}
100112
} else {
101113
this.manageManifestToggeling();
102114
await this.registerTheAppOnDeveloperHub(false);
@@ -198,7 +210,11 @@ export default class Create extends BaseCommand<typeof Create> {
198210
const zip = new AdmZip(filepath);
199211
const dataDir = this.flags["data-dir"] ?? process.cwd();
200212
let targetPath = resolve(dataDir, this.sharedConfig.appName);
201-
const sourcePath = resolve(dataDir, this.sharedConfig.boilerplateName);
213+
214+
// Get the directory inside the zip file
215+
const zipEntries = zip.getEntries();
216+
const firstEntry = zipEntries[0];
217+
const sourcePath = resolve(dataDir, firstEntry.entryName.split("/")[0]);
202218

203219
if (this.flags["data-dir"] && !existsSync(this.flags["data-dir"])) {
204220
mkdirSync(this.flags["data-dir"], { recursive: true });
@@ -235,10 +251,7 @@ export default class Create extends BaseCommand<typeof Create> {
235251
*/
236252
manageManifestToggeling() {
237253
// NOTE Use boilerplate manifest if exist
238-
const manifestPath = resolve(
239-
this.sharedConfig.folderPath || "",
240-
"manifest.json"
241-
);
254+
const manifestPath = resolve(this.sharedConfig.folderPath, "manifest.json");
242255

243256
if (existsSync(manifestPath)) {
244257
this.sharedConfig.manifestPath = manifestPath;
@@ -301,7 +314,10 @@ export default class Create extends BaseCommand<typeof Create> {
301314
this.appData = merge(this.appData, pick(response, validKeys));
302315
if (saveManifest) {
303316
writeFileSync(
304-
resolve(this.sharedConfig.folderPath, "manifest.json"),
317+
resolve(
318+
this.sharedConfig.folderPath,
319+
"manifest.json"
320+
),
305321
JSON.stringify(this.appData),
306322
{
307323
encoding: "utf8",

src/config/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const config = {
88
appBoilerplateGithubUrl:
99
"https://codeload.github.com/contentstack/marketplace-app-boilerplate/zip/refs/heads/main",
1010
defaultAppFileName: "manifest",
11+
boilerplatesUrl: 'https://marketplace-artifacts.contentstack.com/cli/starter-template.json'
1112
};
1213

1314
export default config;

src/types/app.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,12 @@ export interface LaunchProjectRes {
140140
environmentUid: any;
141141
developerHubAppUid: any;
142142
}
143+
144+
export interface BoilerplateAppType {
145+
name: string;
146+
description?: string;
147+
link: string;
148+
tags?: string[];
149+
created_at?: string;
150+
updated_at?: string;
151+
}

src/util/common-utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
cliux,
88
Stack,
99
FsUtility,
10+
HttpClient,
1011
} from "@contentstack/cli-utilities";
1112
import { projectsQuery } from "../graphql/queries";
1213
import { apiRequestHandler } from "./api-request-handler";
@@ -19,6 +20,7 @@ import {
1920
} from "../types";
2021
import { askProjectName } from "./inquirer";
2122
import { deployAppMsg } from "../messages";
23+
import config from "../config";
2224

2325
export type CommonOptions = {
2426
log: LogFn;
@@ -396,6 +398,14 @@ const handleProjectNameConflict = async (
396398
}
397399
return projectName;
398400
};
401+
async function fetchBoilerplateDetails(): Promise<Record<string, any>[]> {
402+
try {
403+
const content = await new HttpClient().get(config.boilerplatesUrl);
404+
return content?.data?.templates ?? [];
405+
} catch (error) {
406+
throw error;
407+
}
408+
}
399409

400410
export {
401411
getOrganizations,
@@ -418,4 +428,5 @@ export {
418428
disconnectApp,
419429
formatUrl,
420430
handleProjectNameConflict,
431+
fetchBoilerplateDetails
421432
};

src/util/inquirer.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
fetchApps,
2828
sanitizePath,
2929
MarketPlaceOptions,
30+
fetchBoilerplateDetails,
3031
} from "./common-utils";
3132
import { LaunchProjectRes } from "../types";
3233

@@ -385,6 +386,21 @@ function inquireRequireValidation(input: any): string | boolean {
385386
return true;
386387
}
387388

389+
const selectedBoilerplate = async (): Promise<any> => {
390+
const boilerplates = await fetchBoilerplateDetails();
391+
392+
return await cliux
393+
.inquire({
394+
type: "search-list",
395+
name: "App",
396+
choices: boilerplates.map((bp) => bp.name),
397+
message: "Select a boilerplate from search list",
398+
})
399+
.then((name) => {
400+
return find(boilerplates, (boilerplate) => boilerplate.name === name);
401+
});
402+
};
403+
388404
export {
389405
getOrg,
390406
getAppName,
@@ -399,5 +415,6 @@ export {
399415
askProjectType,
400416
askConfirmation,
401417
selectProject,
402-
askProjectName
418+
askProjectName,
419+
selectedBoilerplate,
403420
};

0 commit comments

Comments
 (0)