Skip to content

Commit ef882cd

Browse files
feat: review structure and questions for template
1 parent 5179731 commit ef882cd

File tree

6 files changed

+72
-85
lines changed

6 files changed

+72
-85
lines changed

src/index.ts

+4-16
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@ import {grey, red} from 'kleur';
22
import {version} from '../package.json';
33
import {installCliIfNecessary} from './services/cli.services';
44
import {generate} from './services/generate.services';
5-
import {
6-
promptDestination,
7-
promptKind,
8-
promptStarter,
9-
promptTemplate
10-
} from './services/prompt.services';
5+
import {promptDestination, promptProjectKind, promptTemplate} from './services/prompt.services';
116
import {checkNodeVersion} from './utils/env.utils';
127

138
const JUNO_LOGO = ` __ __ __ __ _ ____
@@ -30,20 +25,13 @@ export const run = async () => {
3025

3126
const {destination} = await promptDestination();
3227

33-
const {kind} = await promptKind();
28+
const projectKind = await promptProjectKind();
3429

35-
if (kind === 'website') {
36-
console.warn('🚧 This feature is not yet implemented. Please try again later.');
37-
return;
38-
}
39-
const template = await promptTemplate(kind);
40-
const starter = kind === 'app' ? await promptStarter() : null;
30+
const template = await promptTemplate(projectKind);
4131

4232
await generate({
43-
kind,
4433
destination,
45-
template,
46-
starter
34+
template
4735
});
4836

4937
await installCliIfNecessary();

src/services/generate.services.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import {downloadFromURL} from '../utils/download.utils';
1010
import {
1111
createParentFolders,
1212
getLocalTemplatePath,
13-
getRelativeTemplatePath,
14-
getTemplateName
13+
getRelativeTemplatePath
1514
} from '../utils/fs.utils';
1615
import {createDirectory, getLocalFiles, type LocalFileDescriptor} from '../utils/populate.utils';
1716

@@ -43,8 +42,8 @@ export const populate = async (input: PopulateInput) => {
4342
}
4443
};
4544

46-
const populateFromCDN = async ({where, ...rest}: PopulateInput) => {
47-
const templatePath = getRelativeTemplatePath(rest);
45+
const populateFromCDN = async ({where, template}: PopulateInput) => {
46+
const templatePath = getRelativeTemplatePath(template);
4847

4948
const {hostname} = new URL(JUNO_CDN_URL);
5049

@@ -62,7 +61,7 @@ const populateFromCDN = async ({where, ...rest}: PopulateInput) => {
6261

6362
await createDirectory(where);
6463

65-
const templateName = getTemplateName(rest);
64+
const {key: templateName} = template;
6665

6766
const createFile = async ({name, content}: UntarOutputFile) => {
6867
const target = join(process.cwd(), where ?? '', name.replace(templateName, ''));
@@ -75,8 +74,8 @@ const populateFromCDN = async ({where, ...rest}: PopulateInput) => {
7574
await Promise.all(files.filter(({content}) => content.length !== 0).map(createFile));
7675
};
7776

78-
const populateFromLocal = async ({where, ...rest}: PopulateInput) => {
79-
const templatePath = getLocalTemplatePath(rest);
77+
const populateFromLocal = async ({where, template}: PopulateInput) => {
78+
const templatePath = getLocalTemplatePath(template);
8079

8180
const files = await getLocalFiles(templatePath);
8281

src/services/prompt.services.ts

+50-34
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,72 @@
11
import {isNullish} from '@junobuild/utils';
22
import {red} from 'kleur';
33
import prompts from 'prompts';
4-
import type {GeneratorInput} from '../types/generator';
4+
import {GeneratorInput, ProjectKind} from '../types/generator';
55
import type {Template} from '../types/template';
66
import {assertAnswerCtrlC} from '../utils/prompts.utils';
77

8-
const WEBSITE_TEMPLATES: Template[] = [];
8+
const WEBSITE_TEMPLATES: Template[] = [
9+
{
10+
framework: `Astro`,
11+
key: `astro-starter`,
12+
type: 'Starter',
13+
description: 'Opt for a barebones scaffolding to kickstart your website'
14+
}
15+
];
16+
17+
const APP_TEMPLATES: Template[] = [
18+
{
19+
framework: `Next.js`,
20+
key: `nextjs-starter`,
21+
type: 'Starter',
22+
description: 'Opt for a barebones scaffolding to kickstart your app'
23+
}
24+
];
925

10-
const APP_TEMPLATES: Template[] = [{title: `Next.js`, key: `nextjs`}];
26+
export const promptTemplate = async (kind: ProjectKind): Promise<Template> => {
27+
const allTemplates = Object.groupBy(
28+
kind === 'app' ? APP_TEMPLATES : WEBSITE_TEMPLATES,
29+
({framework}) => framework
30+
);
1131

12-
export const promptTemplate = async (type: 'app' | 'website'): Promise<Template> => {
13-
const collection = type === 'app' ? APP_TEMPLATES : WEBSITE_TEMPLATES;
32+
const frameworks = Object.keys(allTemplates);
1433

15-
const {template}: {template: string} = await prompts({
34+
const {framework}: Pick<Template, 'framework'> = await prompts({
1635
type: 'select',
17-
name: 'template',
18-
message: 'Which template do you want to use?',
19-
choices: collection.map(({title, key}) => ({title, value: key}))
36+
name: 'framework',
37+
message: 'Which framework do you want to use?',
38+
choices: frameworks.map((framework) => ({title: framework, value: framework}))
2039
});
2140

22-
assertAnswerCtrlC(template);
41+
assertAnswerCtrlC(framework);
2342

24-
const item = collection.find(({key}) => key === template);
43+
const templates = allTemplates[framework];
2544

26-
if (isNullish(item)) {
27-
console.log(red(`Invalid ${type} template: ${template}`));
45+
if (isNullish(templates) || templates.length === 0) {
46+
console.log(`No template(s) found for ${red(framework)}. This is unexpected.`);
2847
process.exit(1);
2948
}
3049

31-
return item;
32-
};
50+
if (templates.length === 1) {
51+
return templates[0];
52+
}
3353

34-
export const promptStarter = async () => {
35-
const {starter}: {starter: 'blank' | 'tutorial'} = await prompts({
54+
const {template}: {template: Template | undefined} = await prompts({
3655
type: 'select',
37-
name: 'starter',
38-
message: 'Which starter template would you like to use?',
39-
choices: [
40-
{
41-
title: 'Blank (A blank starter with "just" a customized index page)',
42-
value: 'blank'
43-
},
44-
{
45-
title: 'Tutorial (the "diary" example app)',
46-
value: 'tutorial'
47-
}
48-
]
56+
name: 'template',
57+
message: 'Choose your starting point?',
58+
choices: templates.map((value) => ({
59+
title: value.type,
60+
description: value.description,
61+
value
62+
}))
4963
});
5064

51-
assertAnswerCtrlC(starter);
65+
if (isNullish(template)) {
66+
process.exit(1);
67+
}
5268

53-
return starter;
69+
return template;
5470
};
5571

5672
export const promptDestination = async (): Promise<Pick<GeneratorInput, 'destination'>> => {
@@ -68,8 +84,8 @@ export const promptDestination = async (): Promise<Pick<GeneratorInput, 'destina
6884
return {destination};
6985
};
7086

71-
export const promptKind = async (): Promise<Pick<GeneratorInput, 'kind'>> => {
72-
const {kind}: Pick<GeneratorInput, 'kind'> = await prompts({
87+
export const promptProjectKind = async (): Promise<ProjectKind> => {
88+
const {kind}: {kind: ProjectKind | undefined} = await prompts({
7389
type: 'select',
7490
name: 'kind',
7591
message: 'What kind of project are you starting?',
@@ -81,5 +97,5 @@ export const promptKind = async (): Promise<Pick<GeneratorInput, 'kind'>> => {
8197

8298
assertAnswerCtrlC(kind);
8399

84-
return {kind};
100+
return kind;
85101
};

src/types/generator.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type {Template, TemplateStarter} from './template';
1+
import {Template} from './template';
22

33
export interface GeneratorInput {
4-
kind: 'website' | 'app';
54
destination: string | '';
65
template: Template;
7-
starter: TemplateStarter | null;
86
}
7+
8+
export type ProjectKind = 'website' | 'app';

src/types/template.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export interface Template {
22
key: string;
3-
title: string;
3+
framework: TemplateFramework;
4+
type: TemplateType;
5+
description: string;
46
}
57

6-
export type TemplateStarter = 'blank' | 'tutorial';
8+
export type TemplateFramework = "Next.js" | "Astro"
9+
10+
export type TemplateType = 'Starter' | 'Demo';

src/utils/fs.utils.ts

+3-23
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,16 @@
1-
import {nonNullish} from '@junobuild/utils';
21
import {existsSync, lstatSync, mkdirSync, readdirSync} from 'node:fs';
32
import {dirname, join} from 'node:path';
43
import {fileURLToPath} from 'node:url';
5-
import type {GeneratorInput} from '../types/generator';
6-
import type {Template, TemplateStarter} from '../types/template';
4+
import type {Template} from '../types/template';
75

86
const __filename = fileURLToPath(import.meta.url);
97
const __dirname = dirname(__filename);
108

119
const TEMPLATE_PATH = 'templates';
1210

13-
export const getTemplateName = ({
14-
template: {key},
15-
starter
16-
}: {
17-
template: Template;
18-
starter: TemplateStarter | null;
19-
}): string => `${key}${nonNullish(starter) ? `-${starter}` : ''}`;
11+
export const getRelativeTemplatePath = ({key}: Pick<Template, 'key'>) => join(TEMPLATE_PATH, key);
2012

21-
export const getRelativeTemplatePath = (params: {
22-
template: Template;
23-
starter: TemplateStarter | null;
24-
}) => join(TEMPLATE_PATH, getTemplateName(params));
25-
26-
export const getLocalTemplatePath = ({
27-
kind,
28-
...rest
29-
}: {
30-
template: Template;
31-
starter: TemplateStarter | null;
32-
} & Pick<GeneratorInput, 'kind'>) =>
33-
join(__dirname, '..', TEMPLATE_PATH, kind, getTemplateName(rest));
13+
export const getLocalTemplatePath = ({key}: Pick<Template, 'key'>) => join(__dirname, '..', TEMPLATE_PATH, key);
3414

3515
export const createParentFolders = (target: string) => {
3616
const folder = dirname(target);

0 commit comments

Comments
 (0)