Skip to content

Commit 38e225f

Browse files
authored
fix: main editor undefined when creating vue project (#26)
* fix: vue project main editor undefined * chore: changeset * chore: recast print single quote * chore: deps version fix
1 parent 17c6456 commit 38e225f

File tree

11 files changed

+91
-32
lines changed

11 files changed

+91
-32
lines changed

.changeset/goofy-falcons-wave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-hana": patch
3+
---
4+
5+
Adjust the way Recast outputs the code; the default is to output single quotes.

.changeset/seven-masks-buy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"create-hana": patch
3+
---
4+
5+
Fixed the undefined issue caused by the main editor not being created when creating a Vue project.

src/__tests__/integration.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,44 @@ describe('project Generation Integration', () => {
175175
expect(packageJson.scripts).toHaveProperty('lint')
176176
expect(packageJson.scripts).toHaveProperty('format')
177177
}, 60000)
178+
179+
it('should generate vue + vite feature combinations that modify main entry', async () => {
180+
const config: Config = {
181+
targetDir: 'vue-vite-feature-combo',
182+
projectType: 'vue',
183+
language: 'typescript',
184+
pkgManager: 'pnpm',
185+
buildTool: 'vite',
186+
codeQualityTools: 'eslint-prettier',
187+
useRouter: true,
188+
usePinia: true,
189+
cssFramework: 'unocss',
190+
cssPreprocessor: 'none',
191+
httpLibrary: 'ky',
192+
modulePathAliasing: '@',
193+
git: false,
194+
}
195+
196+
await generateProject(config, TEST_DIR)
197+
198+
const projectDir = join(TEST_DIR, 'vue-vite-feature-combo')
199+
const packageJson = await readPackageJson(projectDir)
200+
const mainFile = await readFile(join(projectDir, 'src/main.ts'), 'utf8')
201+
const viteConfig = await readFile(join(projectDir, 'vite.config.ts'), 'utf8')
202+
203+
expect(mainFile).toContain(`import 'virtual:uno.css'`)
204+
expect(mainFile).toContain(`import router from './router'`)
205+
expect(mainFile).toContain(`import { createPinia } from 'pinia'`)
206+
expect(mainFile).toContain(`createApp(App).use(createPinia()).use(router).mount('#app')`)
207+
208+
expect(viteConfig).toContain(`import UnoCSS from 'unocss/vite'`)
209+
expect(viteConfig).toContain(`import path from 'node:path'`)
210+
expect(viteConfig).toContain(`vue()`)
211+
expect(viteConfig).toContain(`UnoCSS()`)
212+
expect(viteConfig).toContain(`'@': path.resolve(__dirname, "src")`)
213+
214+
expect(packageJson.dependencies).toHaveProperty('pinia')
215+
expect(packageJson.dependencies).toHaveProperty('vue-router')
216+
expect(packageJson.dependencies).toHaveProperty('ky')
217+
}, 60000)
178218
})

src/core/generator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Config, ProjectContext } from '@/types'
22
import { join } from 'node:path'
33
import { ErrorMessages } from '@/constants/errors'
4-
import { createReactMainEditor, createViteConfigEditor } from '@/editor'
4+
import { createReactMainEditor, createViteConfigEditor, createVueMainEditor } from '@/editor'
55
import { mainReactRouterProviderTemplate, mainReactTemplate, viteTemplate } from '@/editor/templates'
66
import { ErrorFactory } from '@/error/factory'
77
import { ErrorHandler } from '@/error/handler'
@@ -92,6 +92,9 @@ async function initializeProjectContext(config: Config, cwd: string) {
9292
context.mainEditor = createReactMainEditor(mainReactTemplate(context.fileExtension))
9393
}
9494
}
95+
if (config.projectType === 'vue') {
96+
context.mainEditor = createVueMainEditor()
97+
}
9598

9699
return context
97100
}
@@ -149,6 +152,9 @@ function saveEditors(context: ProjectContext) {
149152
if (context.config.projectType === 'react' && context.mainEditor) {
150153
context.files[`src/main${context.fileExtension}x`] = context.mainEditor.getContent('main')
151154
}
155+
else if (context.config.projectType === 'vue' && context.mainEditor) {
156+
context.files[`src/main${context.fileExtension}`] = context.mainEditor.getContent('main')
157+
}
152158
}
153159

154160
function shouldInstallDependencies(context: ProjectContext) {

src/dependencies/registry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const PATCH_AND_MINOR = {
2020
export const dependencyRegistry = validateDependencyRegistry({
2121
'typescript': createManagedDependency({
2222
packageName: 'typescript',
23-
version: '6.0.2',
23+
version: '5.9.3',
2424
prefix: '^',
2525
scope: 'devDependencies',
2626
risk: 'medium',
@@ -480,7 +480,7 @@ export const dependencyRegistry = validateDependencyRegistry({
480480
}),
481481
'oxlint-tsgolint': createManagedDependency({
482482
packageName: 'oxlint-tsgolint',
483-
version: '0.17.2',
483+
version: '0.19.0',
484484
prefix: '^',
485485
scope: 'devDependencies',
486486
risk: 'high',

src/editor/__tests__/vite.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export default defineConfig({})`,
169169
const generatedCode = editor.getContent('viteConfig')
170170
expect(generatedCode).toContain('resolve: {')
171171
expect(generatedCode).toContain('alias: {')
172-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
172+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
173173
})
174174

175175
it('应该在已有 resolve 配置中添加 alias', () => {
@@ -187,7 +187,7 @@ export default defineConfig({
187187
const generatedCode = editor.getContent('viteConfig')
188188
expect(generatedCode).toContain('extensions: [')
189189
expect(generatedCode).toContain('alias: {')
190-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
190+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
191191
})
192192

193193
it('应该在已有 alias 配置中添加新别名', () => {
@@ -207,7 +207,7 @@ export default defineConfig({
207207
const generatedCode = editor.getContent('viteConfig')
208208
expect(generatedCode).toContain('resolve: {')
209209
expect(generatedCode).toContain('alias: {')
210-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
210+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
211211
// 原有的 @utils 应该保持原来的字符串格式
212212
expect(generatedCode).toContain('\'@utils\': \'path.resolve(__dirname, "src/utils")\'')
213213
// 或者可以接受任何包含 @utils 和 src/utils 的格式
@@ -225,9 +225,9 @@ export default defineConfig({
225225
const generatedCode = editor.getContent('viteConfig')
226226
expect(generatedCode).toContain('resolve: {')
227227
expect(generatedCode).toContain('alias: {')
228-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
229-
expect(generatedCode).toContain('"@components": path.resolve(__dirname, "src/components")')
230-
expect(generatedCode).toContain('"@utils": path.resolve(__dirname, "src/utils")')
228+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
229+
expect(generatedCode).toContain('\'@components\': path.resolve(__dirname, "src/components")')
230+
expect(generatedCode).toContain('\'@utils\': path.resolve(__dirname, "src/utils")')
231231
})
232232

233233
it('应该跳过已存在的别名', () => {
@@ -259,8 +259,8 @@ export default defineConfig({
259259
const generatedCode = editor.getContent('viteConfig')
260260
expect(generatedCode).toContain('resolve: {')
261261
expect(generatedCode).toContain('alias: {')
262-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
263-
expect(generatedCode).toContain('"@components": path.resolve(__dirname, "src/components")')
262+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
263+
expect(generatedCode).toContain('\'@components\': path.resolve(__dirname, "src/components")')
264264
})
265265

266266
it('应该支持与 addVitePlugin 的链式调用', () => {
@@ -321,7 +321,7 @@ export default defineConfig({
321321
expect(generatedCode).toContain('build: {')
322322
expect(generatedCode).toContain('resolve: {')
323323
expect(generatedCode).toContain('alias: {')
324-
expect(generatedCode).toContain('"@": path.resolve(__dirname, "src")')
324+
expect(generatedCode).toContain('\'@\': path.resolve(__dirname, "src")')
325325
})
326326

327327
it('应该处理复杂的别名值表达式', () => {
@@ -344,7 +344,7 @@ export default defineConfig({
344344
const generatedCode = editor.getContent('viteConfig')
345345
expect(generatedCode).toContain('path.resolve(__dirname, "src")')
346346
expect(generatedCode).toContain('path.join(process.cwd(), "src", "api")')
347-
expect(generatedCode).toContain('"./src/assets"')
347+
expect(generatedCode).toContain('\'./src/assets\'')
348348
})
349349
})
350350
})

src/editor/core-editor.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as recast from 'recast'
22
import { parser } from '@/utils/parser'
3+
import { printAst } from '@/utils/recast'
34

45
interface CodeDetail {
56
source: string
@@ -14,7 +15,7 @@ export interface CoreEditorContents {
1415
export type EditableFiles = keyof CoreEditorContents
1516

1617
type CoreEditorOptions = {
17-
[K in EditableFiles]?: string
18+
[K in EditableFiles]?: string // source code for each editable file
1819
}
1920

2021
export class CoreEditor {
@@ -33,6 +34,6 @@ export class CoreEditor {
3334
public getContent(key: EditableFiles): string {
3435
if (!this.contents[key])
3536
return ''
36-
return recast.print(this.contents[key].ast).code
37+
return printAst(this.contents[key].ast)
3738
}
3839
}

src/editor/features/jsx-provider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { CoreEditor } from '../core-editor'
22
import * as recast from 'recast'
33
import { ErrorMessages } from '@/constants/errors'
44
import { ErrorFactory } from '@/error/factory'
5+
import { printAst } from '@/utils/recast'
56

67
export interface IJsxProviderFeature {
78
addJsxProvider: (componentName: string, props?: Record<string, string>) => this
@@ -65,7 +66,7 @@ export function withJsxProviderFeature<T extends new (...args: any[]) => CoreEdi
6566
})
6667

6768
if (this.contents.main) {
68-
this.contents.main.source = recast.print(this.contents.main.ast).code
69+
this.contents.main.source = printAst(this.contents.main.ast)
6970
}
7071

7172
return this

src/editor/features/vite.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as recast from 'recast'
33
import { ErrorMessages } from '@/constants/errors'
44
import { ErrorFactory } from '@/error/factory'
55
import { parser } from '@/utils/parser'
6+
import { printAst } from '@/utils/recast'
67

78
export interface IViteFeature {
89
addVitePlugin: (pluginCode: string) => this
@@ -67,7 +68,7 @@ export function withViteFeature<T extends new (...args: any[]) => CoreEditor>(Ba
6768
}
6869

6970
// 更新源代码和 AST
70-
this.contents.viteConfig.source = recast.print(ast).code
71+
this.contents.viteConfig.source = printAst(ast)
7172
this.contents.viteConfig.ast = recast.parse(this.contents.viteConfig.source, { parser })
7273

7374
return this
@@ -184,7 +185,7 @@ export function withViteFeature<T extends new (...args: any[]) => CoreEditor>(Ba
184185
}
185186

186187
// 更新源代码和 AST
187-
this.contents.viteConfig.source = recast.print(ast).code
188+
this.contents.viteConfig.source = printAst(ast)
188189
this.contents.viteConfig.ast = recast.parse(this.contents.viteConfig.source, { parser })
189190

190191
return this

src/generators/projects/vue/index.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { VueMainEditorType } from '@/editor'
12
import type { Generator } from '@/types'
23
import { ErrorMessages } from '@/constants/errors'
34
import { ErrorFactory } from '@/error/factory'
@@ -43,7 +44,10 @@ export const vueGenerator: Generator = {
4344

4445
context.files['src/App.vue'] = generateAppFile(lang, styleLang, !!config.useRouter)
4546
context.files['src/components/Counter.vue'] = generateCounterFile(lang, styleLang)
46-
context.files[`src/main${fileExtension}`] = generateMainFile(!!config.useRouter, !!config.usePinia)
47+
;(context.mainEditor as VueMainEditorType).configureVueApp({
48+
useRouter: config.useRouter,
49+
usePinia: config.usePinia,
50+
})
4751

4852
if (config.language === 'typescript') {
4953
context.files['src/vite-env.d.ts'] = generateViteEnvFile()
@@ -152,16 +156,3 @@ button:hover {
152156
</style>
153157
`
154158
}
155-
156-
function generateMainFile(useRouter: boolean, usePinia: boolean) {
157-
return `import { createApp } from 'vue'
158-
import App from './App.vue'
159-
${useRouter ? 'import router from \'./router\'' : ''}
160-
${usePinia ? 'import { createPinia } from \'pinia\'' : ''}
161-
162-
const app = createApp(App)
163-
${usePinia ? 'app.use(createPinia())' : ''}
164-
${useRouter ? 'app.use(router)' : ''}
165-
app.mount('#app')
166-
`
167-
}

0 commit comments

Comments
 (0)