Skip to content

Commit 7f3c451

Browse files
authored
feat(preview): auto format generated code in preview mode (#1794)
1 parent e80fb49 commit 7f3c451

3 files changed

Lines changed: 99 additions & 33 deletions

File tree

packages/common/composable/generateCode/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { generateApp, type IAppSchema } from '@opentiny/tiny-engine-dsl-vue'
22
import * as dslVue from '@opentiny/tiny-engine-dsl-vue'
33
import { getMergeMeta } from '@opentiny/tiny-engine-meta-register'
4+
import { formatString } from '../../js/ast'
45
import defaultPrettierConfig from '../../js/config-files/prettierrc'
56

67
// 应用出码默认配置
@@ -42,7 +43,7 @@ const generateAppCode = async (appSchema: IAppSchema, options = {}) => {
4243
const { parseRequiredBlocks, genSFCWithDefaultPlugin } = dslVue as any
4344

4445
const generatePageCode = (...args: any[]) => {
45-
return genSFCWithDefaultPlugin(...args)
46+
return formatString(genSFCWithDefaultPlugin(...args), 'vue')
4647
}
4748

4849
/**

packages/common/js/ast.ts

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,23 @@ export const string2Ast = (string = ''): BabelAst =>
3838
export const ast2String = (ast: GeneratorInput): string => generate(ast, { retainLines: true }).code // 将 AST 再转回字符串
3939

4040
type FormatterFn = (input: string) => string
41-
type SupportedLanguage = 'json' | 'typescript' | 'javascript' | 'html' | 'css'
41+
type SupportedLanguage = 'json' | 'typescript' | 'javascript' | 'html' | 'css' | 'vue' | 'less'
42+
type SupportedPrettierParser = 'json' | 'babel' | 'babel-ts' | 'html' | 'css' | 'less' | 'vue'
4243

43-
const formatScript: FormatterFn = (string) => {
44+
const PRETTIER_PLUGINS = [parserBabel, parseCss, parserHtml]
45+
46+
const formatWithParser = (string: string, parser: SupportedPrettierParser, options: PrettierOptions = {}): string =>
47+
prettier.format(string, {
48+
parser,
49+
plugins: PRETTIER_PLUGINS,
50+
...basePrettierConfig,
51+
...options
52+
})
53+
54+
const formatScript = (string: string, parser: 'babel' | 'babel-ts' = 'babel'): string => {
4455
let newStr = string
4556
const options: PrettierOptions = {
46-
parser: 'babel',
57+
parser,
4758
plugins: [parserBabel],
4859
...basePrettierConfig
4960
}
@@ -72,34 +83,49 @@ const formatScript: FormatterFn = (string) => {
7283
return newStr
7384
}
7485

75-
const formatJson: FormatterFn = (string) =>
76-
prettier.format(string, {
77-
parser: 'json',
78-
plugins: [parserBabel],
79-
trailingComma: 'es5',
80-
...basePrettierConfig
81-
})
86+
const formatJson: FormatterFn = (string) => formatWithParser(string, 'json')
8287

83-
const formatHtml: FormatterFn = (string) =>
84-
prettier.format(string, {
85-
parser: 'html',
86-
plugins: [parserBabel, parserHtml],
87-
...basePrettierConfig
88-
})
88+
const formatHtml: FormatterFn = (string) => formatWithParser(string, 'html')
8989

90-
const formatCss: FormatterFn = (string) =>
91-
prettier.format(string, {
92-
parser: 'css',
93-
plugins: [parseCss],
94-
...basePrettierConfig
95-
})
90+
const formatCss: FormatterFn = (string) => formatWithParser(string, 'css')
91+
92+
const formatVue: FormatterFn = (string) => formatWithParser(string, 'vue')
93+
94+
const formatLess: FormatterFn = (string) => formatWithParser(string, 'less')
9695

9796
const formatterMap: Record<SupportedLanguage, FormatterFn> = {
9897
json: formatJson,
99-
typescript: formatScript,
98+
typescript: (str) => formatScript(str, 'babel-ts'),
10099
javascript: formatScript,
101100
html: formatHtml,
102-
css: formatCss
101+
css: formatCss,
102+
vue: formatVue,
103+
less: formatLess
104+
}
105+
106+
const parserMap: Record<string, SupportedPrettierParser> = {
107+
json: 'json',
108+
js: 'babel',
109+
jsx: 'babel',
110+
mjs: 'babel',
111+
cjs: 'babel',
112+
ts: 'babel-ts',
113+
tsx: 'babel-ts',
114+
css: 'css',
115+
less: 'less',
116+
html: 'html',
117+
vue: 'vue'
118+
}
119+
120+
export const getPrettierParserByFileName = (fileName = ''): SupportedPrettierParser | undefined => {
121+
const pureFileName = fileName.split('?')[0].split('#')[0].toLowerCase()
122+
const extension = pureFileName.split('.').at(-1)
123+
124+
if (!extension || extension === pureFileName) {
125+
return undefined
126+
}
127+
128+
return parserMap[extension]
103129
}
104130

105131
export const formatString = (str: string, language: string): string => {
@@ -115,6 +141,27 @@ export const formatString = (str: string, language: string): string => {
115141
return result
116142
}
117143

144+
export const formatStringByFileName = (str: string, fileName: string): string => {
145+
const parser = getPrettierParserByFileName(fileName)
146+
147+
if (!parser) {
148+
return str
149+
}
150+
151+
try {
152+
if (parser === 'babel' || parser === 'babel-ts') {
153+
return formatScript(str, parser)
154+
}
155+
156+
return formatWithParser(str, parser)
157+
} catch (error) {
158+
const printer: Console = console
159+
printer.log(error)
160+
161+
return str
162+
}
163+
}
164+
118165
export { parse, parseExpression, traverse, generate }
119166

120167
export const includedExpression = (code: string, expression: string): boolean => {

packages/design-core/src/preview/src/preview/usePreviewData.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { reactive } from 'vue'
2+
import { formatStringByFileName } from '@opentiny/tiny-engine-common/js/ast'
23
import { constants } from '@opentiny/tiny-engine-utils'
34
import { getImportMap as getInitImportMap } from './importMap'
45
import { getMetaApi, getMergeMeta, META_SERVICE, useEnv } from '@opentiny/tiny-engine-meta-register'
@@ -342,6 +343,21 @@ interface IUsePreviewData {
342343
setImportMap: (importMap: Record<string, string>) => void
343344
}
344345

346+
const formatGeneratedFile = (fileContent: unknown, fileName: string): string => {
347+
if (typeof fileContent !== 'string') {
348+
return String(fileContent ?? '')
349+
}
350+
351+
return formatStringByFileName(fileContent, fileName)
352+
}
353+
354+
const formatGeneratedFiles = (files: Record<string, string>) =>
355+
Object.entries(files).reduce((acc, [fileName, fileContent]) => {
356+
acc[fileName] = formatGeneratedFile(fileContent, fileName)
357+
358+
return acc
359+
}, {} as Record<string, string>)
360+
345361
export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewData) => {
346362
const basicFiles = setFiles(srcFiles, 'src/Main.vue')
347363

@@ -407,10 +423,10 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat
407423
const enableTailwindCSS = getMergeMeta('engine.config')?.enableTailwindCSS
408424
const appJsCode = processAppJsCode(newFiles['app.js'] || '', params.styles, enableTailwindCSS)
409425

410-
newFiles['app.js'] = appJsCode
426+
newFiles['app.js'] = formatGeneratedFile(appJsCode, 'app.js')
411427
pageCode.forEach((item) => assignFiles(item, newFiles))
412428

413-
const metaFiles = generateMetaFiles(metaData)
429+
const metaFiles = formatGeneratedFiles(generateMetaFiles(metaData))
414430
Object.assign(newFiles, metaFiles)
415431
setFiles(newFiles, 'App.vue')
416432
} else if (previewType === 'app') {
@@ -581,7 +597,7 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat
581597
return `${importSnippet}\n ${routeSnippets} \n ${exportSnippet}`
582598
}
583599

584-
const formatCode = (fileContent, fileName) => {
600+
const formatPreviewFile = (fileContent, fileName) => {
585601
if (fileName === 'src/router/index.js') {
586602
fileContent = getRouterFile(appSchema)
587603
} else {
@@ -601,23 +617,25 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat
601617
`const route = useRoute()\nconst router = useRouter()\nconst currentRoute = ref()\n\nwatchEffect(() => {\n\tcurrentRoute.value = route.path\n})\n\nconst routeChange = () => {\n\trouter.push(currentRoute.value)\n}\nprovide(I18nInjectionKey, i18n)`
602618
)
603619
}
604-
return fileContent
620+
621+
return formatGeneratedFile(fileContent, fileName)
605622
}
606623

607624
const fileRes = await getPreGenerateInfo()
608625
const newFileRes = fileRes.filter((item) => item.filePath.includes('src/'))
609626
const srcFiles = newFileRes.reduce((prev, item) => {
610627
const fileName = item.filePath
611-
prev[fileName] = formatCode(item.fileContent, fileName)
628+
prev[fileName] = formatPreviewFile(item.fileContent, fileName)
612629
return prev
613-
}, {})
614-
srcFiles['import-map.json'] = JSON.stringify(importMapData)
630+
}, {} as Record<string, string>)
631+
srcFiles['import-map.json'] = formatGeneratedFile(JSON.stringify(importMapData), 'import-map.json')
615632
const newFiles = store.getFiles()
616633
const enableTailwindCSS = getMergeMeta('engine.config')?.enableTailwindCSS
617634
const appJsCode = processAppJsCode(newFiles['app.js'] || '', params.styles, enableTailwindCSS)
618-
srcFiles['app.js'] = appJsCode
635+
srcFiles['app.js'] = formatGeneratedFile(appJsCode, 'app.js')
619636
srcFiles['main.js'] = `import app from './app.js' \n ${srcFiles['src/main.js']}`
620637
srcFiles['main.js'] = srcFiles['main.js'].replace("import 'element-plus/dist/index.css'", '')
638+
srcFiles['main.js'] = formatGeneratedFile(srcFiles['main.js'], 'main.js')
621639
setFiles(srcFiles, 'src/main.js')
622640
}
623641
}

0 commit comments

Comments
 (0)