diff --git a/packages/common/composable/generateCode/index.ts b/packages/common/composable/generateCode/index.ts index 505a9e1acb..0b27a5daef 100644 --- a/packages/common/composable/generateCode/index.ts +++ b/packages/common/composable/generateCode/index.ts @@ -1,6 +1,7 @@ import { generateApp, type IAppSchema } from '@opentiny/tiny-engine-dsl-vue' import * as dslVue from '@opentiny/tiny-engine-dsl-vue' import { getMergeMeta } from '@opentiny/tiny-engine-meta-register' +import { formatString } from '../../js/ast' import defaultPrettierConfig from '../../js/config-files/prettierrc' // 应用出码默认配置 @@ -42,7 +43,7 @@ const generateAppCode = async (appSchema: IAppSchema, options = {}) => { const { parseRequiredBlocks, genSFCWithDefaultPlugin } = dslVue as any const generatePageCode = (...args: any[]) => { - return genSFCWithDefaultPlugin(...args) + return formatString(genSFCWithDefaultPlugin(...args), 'vue') } /** diff --git a/packages/common/js/ast.ts b/packages/common/js/ast.ts index 55ff615e7d..0c4cc40051 100644 --- a/packages/common/js/ast.ts +++ b/packages/common/js/ast.ts @@ -38,12 +38,23 @@ export const string2Ast = (string = ''): BabelAst => export const ast2String = (ast: GeneratorInput): string => generate(ast, { retainLines: true }).code // 将 AST 再转回字符串 type FormatterFn = (input: string) => string -type SupportedLanguage = 'json' | 'typescript' | 'javascript' | 'html' | 'css' +type SupportedLanguage = 'json' | 'typescript' | 'javascript' | 'html' | 'css' | 'vue' | 'less' +type SupportedPrettierParser = 'json' | 'babel' | 'babel-ts' | 'html' | 'css' | 'less' | 'vue' -const formatScript: FormatterFn = (string) => { +const PRETTIER_PLUGINS = [parserBabel, parseCss, parserHtml] + +const formatWithParser = (string: string, parser: SupportedPrettierParser, options: PrettierOptions = {}): string => + prettier.format(string, { + parser, + plugins: PRETTIER_PLUGINS, + ...basePrettierConfig, + ...options + }) + +const formatScript = (string: string, parser: 'babel' | 'babel-ts' = 'babel'): string => { let newStr = string const options: PrettierOptions = { - parser: 'babel', + parser, plugins: [parserBabel], ...basePrettierConfig } @@ -72,34 +83,49 @@ const formatScript: FormatterFn = (string) => { return newStr } -const formatJson: FormatterFn = (string) => - prettier.format(string, { - parser: 'json', - plugins: [parserBabel], - trailingComma: 'es5', - ...basePrettierConfig - }) +const formatJson: FormatterFn = (string) => formatWithParser(string, 'json') -const formatHtml: FormatterFn = (string) => - prettier.format(string, { - parser: 'html', - plugins: [parserBabel, parserHtml], - ...basePrettierConfig - }) +const formatHtml: FormatterFn = (string) => formatWithParser(string, 'html') -const formatCss: FormatterFn = (string) => - prettier.format(string, { - parser: 'css', - plugins: [parseCss], - ...basePrettierConfig - }) +const formatCss: FormatterFn = (string) => formatWithParser(string, 'css') + +const formatVue: FormatterFn = (string) => formatWithParser(string, 'vue') + +const formatLess: FormatterFn = (string) => formatWithParser(string, 'less') const formatterMap: Record = { json: formatJson, - typescript: formatScript, + typescript: (str) => formatScript(str, 'babel-ts'), javascript: formatScript, html: formatHtml, - css: formatCss + css: formatCss, + vue: formatVue, + less: formatLess +} + +const parserMap: Record = { + json: 'json', + js: 'babel', + jsx: 'babel', + mjs: 'babel', + cjs: 'babel', + ts: 'babel-ts', + tsx: 'babel-ts', + css: 'css', + less: 'less', + html: 'html', + vue: 'vue' +} + +export const getPrettierParserByFileName = (fileName = ''): SupportedPrettierParser | undefined => { + const pureFileName = fileName.split('?')[0].split('#')[0].toLowerCase() + const extension = pureFileName.split('.').at(-1) + + if (!extension || extension === pureFileName) { + return undefined + } + + return parserMap[extension] } export const formatString = (str: string, language: string): string => { @@ -115,6 +141,27 @@ export const formatString = (str: string, language: string): string => { return result } +export const formatStringByFileName = (str: string, fileName: string): string => { + const parser = getPrettierParserByFileName(fileName) + + if (!parser) { + return str + } + + try { + if (parser === 'babel' || parser === 'babel-ts') { + return formatScript(str, parser) + } + + return formatWithParser(str, parser) + } catch (error) { + const printer: Console = console + printer.log(error) + + return str + } +} + export { parse, parseExpression, traverse, generate } export const includedExpression = (code: string, expression: string): boolean => { diff --git a/packages/design-core/src/preview/src/preview/usePreviewData.ts b/packages/design-core/src/preview/src/preview/usePreviewData.ts index 798845c2b1..8dcaaf2384 100644 --- a/packages/design-core/src/preview/src/preview/usePreviewData.ts +++ b/packages/design-core/src/preview/src/preview/usePreviewData.ts @@ -1,4 +1,5 @@ import { reactive } from 'vue' +import { formatStringByFileName } from '@opentiny/tiny-engine-common/js/ast' import { constants } from '@opentiny/tiny-engine-utils' import { getImportMap as getInitImportMap } from './importMap' import { getMetaApi, getMergeMeta, META_SERVICE, useEnv } from '@opentiny/tiny-engine-meta-register' @@ -342,6 +343,21 @@ interface IUsePreviewData { setImportMap: (importMap: Record) => void } +const formatGeneratedFile = (fileContent: unknown, fileName: string): string => { + if (typeof fileContent !== 'string') { + return String(fileContent ?? '') + } + + return formatStringByFileName(fileContent, fileName) +} + +const formatGeneratedFiles = (files: Record) => + Object.entries(files).reduce((acc, [fileName, fileContent]) => { + acc[fileName] = formatGeneratedFile(fileContent, fileName) + + return acc + }, {} as Record) + export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewData) => { const basicFiles = setFiles(srcFiles, 'src/Main.vue') @@ -407,10 +423,10 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat const enableTailwindCSS = getMergeMeta('engine.config')?.enableTailwindCSS const appJsCode = processAppJsCode(newFiles['app.js'] || '', params.styles, enableTailwindCSS) - newFiles['app.js'] = appJsCode + newFiles['app.js'] = formatGeneratedFile(appJsCode, 'app.js') pageCode.forEach((item) => assignFiles(item, newFiles)) - const metaFiles = generateMetaFiles(metaData) + const metaFiles = formatGeneratedFiles(generateMetaFiles(metaData)) Object.assign(newFiles, metaFiles) setFiles(newFiles, 'App.vue') } else if (previewType === 'app') { @@ -581,7 +597,7 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat return `${importSnippet}\n ${routeSnippets} \n ${exportSnippet}` } - const formatCode = (fileContent, fileName) => { + const formatPreviewFile = (fileContent, fileName) => { if (fileName === 'src/router/index.js') { fileContent = getRouterFile(appSchema) } else { @@ -601,23 +617,25 @@ export const usePreviewData = ({ setFiles, store, setImportMap }: IUsePreviewDat `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)` ) } - return fileContent + + return formatGeneratedFile(fileContent, fileName) } const fileRes = await getPreGenerateInfo() const newFileRes = fileRes.filter((item) => item.filePath.includes('src/')) const srcFiles = newFileRes.reduce((prev, item) => { const fileName = item.filePath - prev[fileName] = formatCode(item.fileContent, fileName) + prev[fileName] = formatPreviewFile(item.fileContent, fileName) return prev - }, {}) - srcFiles['import-map.json'] = JSON.stringify(importMapData) + }, {} as Record) + srcFiles['import-map.json'] = formatGeneratedFile(JSON.stringify(importMapData), 'import-map.json') const newFiles = store.getFiles() const enableTailwindCSS = getMergeMeta('engine.config')?.enableTailwindCSS const appJsCode = processAppJsCode(newFiles['app.js'] || '', params.styles, enableTailwindCSS) - srcFiles['app.js'] = appJsCode + srcFiles['app.js'] = formatGeneratedFile(appJsCode, 'app.js') srcFiles['main.js'] = `import app from './app.js' \n ${srcFiles['src/main.js']}` srcFiles['main.js'] = srcFiles['main.js'].replace("import 'element-plus/dist/index.css'", '') + srcFiles['main.js'] = formatGeneratedFile(srcFiles['main.js'], 'main.js') setFiles(srcFiles, 'src/main.js') } }