Skip to content

Commit 0e898e0

Browse files
committed
feat:project creation command
1 parent 19bbc65 commit 0e898e0

File tree

10 files changed

+176
-18
lines changed

10 files changed

+176
-18
lines changed

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,11 @@
965965
"title": "Global State",
966966
"category": "Nuxtr",
967967
"when": "nuxtr.isNuxtProject"
968+
},
969+
{
970+
"command": "nuxtr.createProject",
971+
"title": "Create new Nuxt Project",
972+
"category": "Nuxtr"
968973
}
969974
],
970975
"keybindings": [],
@@ -1076,6 +1081,7 @@
10761081
"@nuxt/schema": "^3.8.2",
10771082
"destr": "^2.0.2",
10781083
"fs-extra": "^11.2.0",
1084+
"giget": "^1.1.3",
10791085
"jiti": "^1.21.0",
10801086
"magicast": "^0.3.2",
10811087
"ofetch": "^1.3.3",
@@ -1093,4 +1099,4 @@
10931099
"uglify-js@<2.4.24": ">=2.4.24"
10941100
}
10951101
}
1096-
}
1102+
}

pnpm-lock.yaml

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

src/commands/Project.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { window, QuickPick, QuickPickItem, QuickInputButton, ThemeIcon, OpenDialogOptions, Uri, commands } from 'vscode';
2+
import { downloadTemplate } from 'giget';
3+
import { ofetch } from 'ofetch'
4+
import { ProjectTemplate } from '../types'
5+
import { projectRootDirectory, openExternalLink, openFolder } from '../utils';
6+
7+
const github: QuickInputButton = {
8+
iconPath: new ThemeIcon("github"),
9+
tooltip: "Template Github Repo",
10+
}
11+
12+
const docs: QuickInputButton = {
13+
iconPath: new ThemeIcon("book"),
14+
tooltip: "Template Docs/Refrence",
15+
}
16+
17+
18+
const fetchTemplates = async () => {
19+
try {
20+
let res = await ofetch('https://nuxt.new/data/starters.json');
21+
return res as ProjectTemplate[]
22+
23+
} catch (error) {
24+
window.showErrorMessage(`Failed to fetch Nuxt templates`)
25+
}
26+
}
27+
28+
const fetchTemplate = async (repo: ProjectTemplate, path: string, projectName: string) => {
29+
try {
30+
await downloadTemplate(`${repo.branch}`, {
31+
cwd: path,
32+
registry: "https://raw.githubusercontent.com/nuxt/starter/templates/templates",
33+
dir: projectName,
34+
})
35+
} catch (error) {
36+
console.log('error', error);
37+
window.showErrorMessage(`Failed to fetch Nuxt ${repo.name} template`)
38+
}
39+
}
40+
41+
export async function createProject() {
42+
try {
43+
const templates = await fetchTemplates() as ProjectTemplate[];
44+
45+
if (!templates) {
46+
return;
47+
} else {
48+
49+
50+
const picker: QuickPick<QuickPickItem> = window.createQuickPick()
51+
52+
picker.canSelectMany = false
53+
picker.ignoreFocusOut = true
54+
picker.matchOnDescription = true
55+
picker.placeholder = "Select a Nuxt template"
56+
57+
const items: QuickPickItem[] = templates.map((item) => {
58+
return {
59+
label: item.name,
60+
description: item.description,
61+
buttons: [github, docs],
62+
package: item,
63+
}
64+
})
65+
66+
picker.items = items
67+
68+
69+
picker.onDidChangeSelection(async (selection) => {
70+
if (selection[0]) {
71+
const repo = templates.find((item) => item.name === selection[0].label)
72+
if (repo) {
73+
try {
74+
75+
const proName = await window.showInputBox({
76+
placeHolder: 'Project Name',
77+
prompt: 'Enter a project name',
78+
})
79+
80+
if (proName) {
81+
82+
const options: OpenDialogOptions = {
83+
canSelectMany: false,
84+
openLabel: 'Select',
85+
canSelectFiles: false,
86+
canSelectFolders: true
87+
};
88+
89+
window.showOpenDialog(options).then(async fileUri => {
90+
if (fileUri && fileUri[0]) {
91+
try {
92+
await fetchTemplate(repo, fileUri[0].fsPath, proName)
93+
const result = await window.showInformationMessage(`Projec created`, 'Open in current window', 'Open in new window')
94+
if (result === 'Open in current window') {
95+
let uri = Uri.file(`${fileUri[0].fsPath}/${proName}`);
96+
await openFolder(uri, proName, false)
97+
}
98+
99+
if (result === 'Open in new window') {
100+
let uri = Uri.file(`${fileUri[0].fsPath}/${proName}`);
101+
await openFolder(uri, proName, true)
102+
}
103+
} catch (error) {
104+
window.showInformationMessage(`Failed to create Nuxt ${proName} template`)
105+
}
106+
}
107+
});
108+
109+
}
110+
111+
112+
} catch (error) {
113+
window.showErrorMessage(`Failed to fetch Nuxt ${repo.name} template`)
114+
}
115+
}
116+
}
117+
})
118+
119+
picker.onDidTriggerItemButton(async (e) => {
120+
const selectedItem = e.item as QuickPickItem & { package: ProjectTemplate }
121+
if (e.button === github) { openExternalLink(`https://github.com/${selectedItem.package.repo}`) }
122+
123+
if (e.button === docs) { openExternalLink(selectedItem.package.docs) }
124+
})
125+
126+
picker.onDidChangeSelection(async (item: any) => { picker.dispose() })
127+
128+
picker.show()
129+
}
130+
131+
} catch (error) {
132+
window.showErrorMessage(`Failed to fetch Nuxt templates`)
133+
}
134+
}

src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { configurePug } from './Templates'
1818
import { createPageTemplate, createLayoutTemplate, createFileFromTemplate, createEmptyFileTemplate } from './FileTemplates'
1919
import { nuxtConfigWatcher, directToggleDevTools } from './Devtools'
2020
import { createUtil, directCreateUtil } from './Util'
21+
import { createProject } from './Project'
2122

2223

2324
const commands = {
@@ -42,6 +43,7 @@ const commands = {
4243
createUtil,
4344
createNitroUtil,
4445
directCreateUtil,
46+
createProject,
4547
projectStructure,
4648
openDocumentation,
4749
openModules,

src/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const commandList = [
2121
{ command: 'nuxtr.createNitroRoute', function: nuxtrCommands.createNitroRoute },
2222
{ command: 'nuxtr.createStore', function: nuxtrCommands.createStore },
2323
{ command: 'nuxtr.createUtil', function: nuxtrCommands.createUtil },
24+
{ command: 'nuxtr.createProject', function: async () => await nuxtrCommands.createProject() },
2425
{ command: 'nuxtr.projectStructure', function: nuxtrCommands.projectStructure },
2526
{ command: 'nuxtr.openDocumentation', function: nuxtrCommands.openDocumentation },
2627
{ command: 'nuxtr.openModules', function: nuxtrCommands.openModules },

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { ExtensionContext, commands } from 'vscode';
2+
import nuxtrCommands from './commands'
23
import { activateExtension } from './extension'
34

5+
const creationCommand = { command: 'nuxtr.createProject', function: async () => await nuxtrCommands.createProject() }
6+
47
export async function activate(context: ExtensionContext) {
58
const nuxtProject = true
69

710
commands.executeCommand('setContext', 'nuxtr.isNuxtProject', nuxtProject)
811
activateExtension(context)
9-
}
12+
}

src/types.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,12 @@ export type nuxtModule = {
7979
requires: Record<string, any>;
8080
};
8181
tags: string[];
82-
};
82+
};
83+
84+
export type ProjectTemplate = {
85+
name: string;
86+
repo: string;
87+
branch: string;
88+
docs: string;
89+
description: string;
90+
};

src/utils/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { getCommandType } from './commands'
4343

4444
import { logger } from './outputChannel'
4545

46-
import { languageSelector, patternSelector, pathExists, isDirectory, readDirectory } from './vscode';
46+
import { languageSelector, patternSelector, pathExists, isDirectory, readDirectory, openFolder } from './vscode';
4747

4848
import { generateVueFileBasicTemplate, generateVueFileTemplate, generatePiniaTemplates, normalizeLFToCRLF } from './files'
4949

@@ -103,5 +103,6 @@ export {
103103
createConfigWatcher,
104104
isNuxiInstalled,
105105
normalizeName,
106-
normalizeFileExtension
106+
normalizeFileExtension,
107+
openFolder
107108
}

src/utils/vscode.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DocumentSelector, workspace, Uri, FileType } from 'vscode';
1+
import { DocumentSelector, workspace, Uri, FileType, commands, window } from 'vscode';
22

33
export const languageSelector = (language: string): DocumentSelector => ({ scheme: 'file', language, } as const);
44

@@ -30,3 +30,14 @@ export async function readDirectory(filePath: string) {
3030
return [];
3131
}
3232
}
33+
34+
35+
export async function openFolder(path: Uri, folderName: string, newWindow: boolean) {
36+
try {
37+
await commands.executeCommand('vscode.openFolder', path, {
38+
forceNewWindow: newWindow,
39+
});
40+
} catch (error) {
41+
window.showErrorMessage(`Failed to open Nuxt ${folderName} template`)
42+
}
43+
}

tsup.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default defineConfig({
2727
"pkg-types",
2828
"string-ts",
2929
"util",
30+
"giget"
3031
]
3132

3233
})

0 commit comments

Comments
 (0)