From 930541e7265602e37d07deb2b7b576e619078a58 Mon Sep 17 00:00:00 2001 From: harrywan <445436867@qq.com> Date: Wed, 9 Mar 2022 10:27:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A1=B5=E9=9D=A2=E6=94=AF=E6=8C=81tsx?= =?UTF-8?q?=EF=BC=8C=E6=8F=90=E4=BE=9BdefineRoute=E9=85=8D=E7=BD=AEtsx?= =?UTF-8?q?=E5=92=8Cjsx=E7=9A=84route=20(#106)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 页面支持tsx,提供defineRoute配置tsx和jsx的route * .vue文件也支持defineRouteMeta * feat: 改回声明用法 --- .../src/plugins/misc/route/index.js | 44 ++++++++++++---- .../plugins/misc/route/template/routes.tpl | 4 ++ packages/fes-template-h5/src/pages/index.vue | 50 ++++++++++++------- .../fes-template-h5/src/pages/onepiece.vue | 2 +- packages/fes-template-h5/src/pages/test.jsx | 7 --- packages/fes-template-h5/src/pages/test.tsx | 15 ++++++ packages/fes-utils/package.json | 1 + packages/fes-utils/src/index.js | 4 +- packages/fes/types.d.ts | 7 ++- 9 files changed, 96 insertions(+), 38 deletions(-) delete mode 100644 packages/fes-template-h5/src/pages/test.jsx create mode 100644 packages/fes-template-h5/src/pages/test.tsx diff --git a/packages/fes-preset-built-in/src/plugins/misc/route/index.js b/packages/fes-preset-built-in/src/plugins/misc/route/index.js index 272d662e..f963cb00 100644 --- a/packages/fes-preset-built-in/src/plugins/misc/route/index.js +++ b/packages/fes-preset-built-in/src/plugins/misc/route/index.js @@ -2,7 +2,7 @@ import { readdirSync, statSync, readFileSync } from 'fs'; import { join, extname, posix, basename } from 'path'; -import { lodash } from '@fesjs/utils'; +import { lodash, parser, generator } from '@fesjs/utils'; import { parse } from '@vue/compiler-sfc'; import { Logger } from '@fesjs/compiler'; import { runtimePath } from '../../../utils/constants'; @@ -21,7 +21,7 @@ const logger = new Logger('fes:router'); const isProcessFile = function (path) { const ext = extname(path); - return statSync(path).isFile() && ['.vue', '.jsx'].includes(ext); + return statSync(path).isFile() && ['.vue', '.jsx', '.tsx'].includes(ext); }; const isProcessDirectory = function (path, item) { @@ -71,6 +71,19 @@ const getRoutePath = function (parentRoutePath, fileName) { return posix.join(parentRoutePath, fileName); }; +function getRouteMeta(content) { + const ast = parser.parse(content, { + sourceType: 'module', + plugins: ['jsx', 'typescript'] + }); + const defineRouteExpression = ast.program.body.filter(expression => expression.type === 'ExpressionStatement' && expression.expression.type === 'CallExpression' && expression.expression.callee.name === 'defineRouteMeta')[0]; + if (defineRouteExpression) { + const argument = generator(defineRouteExpression.expression.arguments[0]); + return JSON.parse(argument.code.replace(/'/g, '"').replace(/(\S+):/g, (global, m1) => `"${m1}":`)); + } + return null; +} + let cacheGenRoutes = {}; // TODO 约定 layout 目录作为布局文件夹, @@ -88,16 +101,12 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) { // 文件或者目录的绝对路径 const component = join(path, item); if (isProcessFile(component)) { - const { descriptor } = parse(readFileSync(component, 'utf-8')); - const routeMetaBlock = descriptor.customBlocks.find( - b => b.type === 'config' - ); const ext = extname(item); const fileName = basename(item, ext); // 路由的path const routePath = getRoutePath(parentRoutePath, fileName); if (cacheGenRoutes[routePath]) { - logger.warn(`[WARNING]: The file path: ${routePath}(.jsx/.vue) conflict in router,will only use ${routePath}.jsx,please remove one of.`); + logger.warn(`[WARNING]: The file path: ${routePath}(.jsx/.tsx/.vue) conflict in router,will only use ${routePath}.tsx or ${routePath}.jsx,please remove one of.`); return; } cacheGenRoutes[routePath] = true; @@ -105,7 +114,24 @@ const genRoutes = function (parentRoutes, path, parentRoutePath, config) { // 路由名称 const routeName = getRouteName(parentRoutePath, fileName); const componentPath = getComponentPath(parentRoutePath, fileName, config); - const routeMeta = routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {}; + + let content = readFileSync(component, 'utf-8'); + let routeMeta = {}; + if (ext === '.vue') { + const { descriptor } = parse(content); + const routeMetaBlock = descriptor.customBlocks.find( + b => b.type === 'config' + ); + routeMeta = routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {}; + if (descriptor.script) { + content = descriptor.script.content; + routeMeta = getRouteMeta(content) || routeMeta; + } + } + if (ext === '.jsx' || ext === '.tsx') { + routeMeta = getRouteMeta(content) || {}; + } + const routeConfig = { path: routePath, component: componentPath, @@ -302,7 +328,7 @@ export default function (api) { api.addCoreExports(() => [ { - specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter'], + specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter', 'defineRouteMeta'], source: absCoreFilePath } ]); diff --git a/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl b/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl index e6a6c6aa..6d2083d4 100644 --- a/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl +++ b/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl @@ -60,3 +60,7 @@ export const destroyRouter = ()=>{ router = null; history = null; } + +export const defineRouteMeta = (param)=>{ + return param +} diff --git a/packages/fes-template-h5/src/pages/index.vue b/packages/fes-template-h5/src/pages/index.vue index 8cf4ebc3..7d6479dc 100644 --- a/packages/fes-template-h5/src/pages/index.vue +++ b/packages/fes-template-h5/src/pages/index.vue @@ -1,26 +1,30 @@ <template> <div class="onepiece m-10px text-green"> fes h5 & 拉夫德鲁<br /> - <fes-icon :spin="true" class="one-icon" type="smile" @click="clickIcon" /> + <fes-icon + :spin="true" + class="one-icon" + type="smile" + @click="clickIcon" + /> <HelloWorld /> <HelloTSX /> <helloTS /> </div> </template> -<config> -{ - "title": "首页", - "name": "testIndex", - "layout": "false" -} -</config> <script> import { ref } from 'vue'; -import { request } from '@fesjs/fes'; +import { request, defineRouteMeta, useRoute } from '@fesjs/fes'; import HelloWorld from '@/components/helloWorld'; import HelloTSX from '@/components/helloTSX'; import helloTS from '@/components/helloTS'; +defineRouteMeta({ + title: '首页', + name: 'testIndex', + layout: false +}); + export default { components: { HelloWorld, @@ -33,6 +37,7 @@ export default { const clickIcon = () => { console.log('click Icon'); }; + console.log(useRoute()); // request('/api', null, { // }).then((res) => { // console.log(res); @@ -59,15 +64,23 @@ export default { // }); const get = (id) => { - request('/get/api', { id }, { - method: 'get' - }); + request( + '/get/api', + { id }, + { + method: 'get' + } + ); }; const post = (id) => { - request('/api', { id }, { - responseType: 'blob' - }).then((data) => { + request( + '/api', + { id }, + { + responseType: 'blob' + } + ).then((data) => { console.log(data); }); }; @@ -80,7 +93,6 @@ export default { // post(2); post(3); - // setTimeout(() => { // request('/api', null, { // throttle: 3000, @@ -138,8 +150,8 @@ export default { </script> <style lang="less" scoped> -@import "~@/styles/mixins/hairline"; -@import "~@/styles/mixins/hover"; +@import '~@/styles/mixins/hairline'; +@import '~@/styles/mixins/hover'; div { padding: 20px; @@ -154,6 +166,6 @@ div { } .onepiece { text-align: center; - .hairline("top"); + .hairline('top'); } </style> diff --git a/packages/fes-template-h5/src/pages/onepiece.vue b/packages/fes-template-h5/src/pages/onepiece.vue index bcf8dafd..e8c335c6 100644 --- a/packages/fes-template-h5/src/pages/onepiece.vue +++ b/packages/fes-template-h5/src/pages/onepiece.vue @@ -5,7 +5,7 @@ <config> { "title": "onepiece", - "layout": "true" + "layout": true } </config> <script> diff --git a/packages/fes-template-h5/src/pages/test.jsx b/packages/fes-template-h5/src/pages/test.jsx deleted file mode 100644 index 84472aed..00000000 --- a/packages/fes-template-h5/src/pages/test.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import { defineComponent } from 'vue'; - -export default defineComponent({ - setup() { - return () => <div>hello jsx</div>; - } -}); diff --git a/packages/fes-template-h5/src/pages/test.tsx b/packages/fes-template-h5/src/pages/test.tsx new file mode 100644 index 00000000..07efe642 --- /dev/null +++ b/packages/fes-template-h5/src/pages/test.tsx @@ -0,0 +1,15 @@ +import { defineRouteMeta, useRoute } from '@fesjs/fes'; +import { defineComponent } from 'vue'; + +defineRouteMeta({ + title: 'test', + name: 'test' +}) + +export default defineComponent({ + setup() { + const route = useRoute(); + console.log(route) + return () => <div>hello tsx</div>; + } +}); diff --git a/packages/fes-utils/package.json b/packages/fes-utils/package.json index e1020935..ac5bbe32 100644 --- a/packages/fes-utils/package.json +++ b/packages/fes-utils/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "@babel/parser": "^7.15.0", + "@babel/generator": "^7.15.0", "@babel/traverse": "^7.15.0", "chalk": "^4.1.2", "chokidar": "^3.5.2", diff --git a/packages/fes-utils/src/index.js b/packages/fes-utils/src/index.js index 816691c2..c7967646 100644 --- a/packages/fes-utils/src/index.js +++ b/packages/fes-utils/src/index.js @@ -8,6 +8,7 @@ import glob from 'glob'; import createDebug from 'debug'; import * as parser from '@babel/parser'; import traverse from '@babel/traverse'; +import generator from '@babel/generator'; import rimraf from 'rimraf'; import mkdirp from 'mkdirp'; import pkgUp from 'pkg-up'; @@ -40,7 +41,8 @@ export { traverse, pkgUp, portfinder, - resolve + resolve, + generator }; export { diff --git a/packages/fes/types.d.ts b/packages/fes/types.d.ts index 89ad8bf8..1a880b6b 100644 --- a/packages/fes/types.d.ts +++ b/packages/fes/types.d.ts @@ -1,5 +1,10 @@ - // @ts-ignore export * from '@@/core/coreExports'; // @ts-ignore export * from '@@/core/pluginExports'; + +export declare function defineRouteMeta(routeMeta: { + name?: string; + title?: string; + layout?: boolean | { sidebar?: boolean; header?: boolean; logo?: boolean }; +});