feat(preview): auto format generated code in preview mode (#1794)
This commit is contained in:
parent
e80fb49346
commit
7f3c45168d
|
|
@ -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')
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<SupportedLanguage, FormatterFn> = {
|
||||
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<string, SupportedPrettierParser> = {
|
||||
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 => {
|
||||
|
|
|
|||
|
|
@ -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<string, string>) => void
|
||||
}
|
||||
|
||||
const formatGeneratedFile = (fileContent: unknown, fileName: string): string => {
|
||||
if (typeof fileContent !== 'string') {
|
||||
return String(fileContent ?? '')
|
||||
}
|
||||
|
||||
return formatStringByFileName(fileContent, fileName)
|
||||
}
|
||||
|
||||
const formatGeneratedFiles = (files: Record<string, string>) =>
|
||||
Object.entries(files).reduce((acc, [fileName, fileContent]) => {
|
||||
acc[fileName] = formatGeneratedFile(fileContent, fileName)
|
||||
|
||||
return acc
|
||||
}, {} as Record<string, string>)
|
||||
|
||||
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<string, string>)
|
||||
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')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue