From 784f5433d64452bcc5cd7baf75e0ff95ef7a51f5 Mon Sep 17 00:00:00 2001 From: pqcqaq <905739777@qq.com> Date: Sat, 16 Nov 2024 09:53:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=A4=8D=E7=94=A8OakComponen?= =?UTF-8?q?t=E7=9A=84=E6=8F=92=E4=BB=B6=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/babel-plugin/reuse-oak-component.js | 63 ++++++ config/utils/injectGetRender.js | 242 +++++++++++---------- config/web/webpack.config.js | 2 + 3 files changed, 189 insertions(+), 118 deletions(-) create mode 100644 config/babel-plugin/reuse-oak-component.js diff --git a/config/babel-plugin/reuse-oak-component.js b/config/babel-plugin/reuse-oak-component.js new file mode 100644 index 0000000..4f4be7c --- /dev/null +++ b/config/babel-plugin/reuse-oak-component.js @@ -0,0 +1,63 @@ +const t = require('@babel/types'); +const generate = require('@babel/generator').default; +const { relative, dirname, parse, join } = require('path'); +const fs = require('fs'); +const { getStatements } = require('../utils/injectGetRender'); + +module.exports = (babel) => { + return { + visitor: { + ExportDefaultDeclaration(path, state) { + + const { declaration } = path.node; + const { cwd, filename } = state; + const { base } = parse(filename); + if (['index.ts', 'index.js'].includes(base) && t.isIdentifier(declaration) && declaration.name === 'OakComponent') { + const dir = dirname(filename); + const statements = getStatements(dir); + + const resetRenderFunction = t.arrowFunctionExpression( + [], + t.blockStatement([ + ...statements + ]) + ); + + const newComponent = t.arrowFunctionExpression( + [t.identifier('props')], + t.blockStatement([ + t.returnStatement( + t.callExpression( + t.memberExpression(t.identifier('React'), t.identifier('createElement')), + [ + t.identifier('OakComponent'), + t.objectExpression([ + t.spreadElement(t.identifier('props')), + // #开头的不是identifier,而是StringLiteral + t.objectProperty(t.stringLiteral('#resetRender'), resetRenderFunction) + ]) + ] + ) + ) + ]) + ); + + // 输出一下生成的内容: + // const { code } = generate(newComponent); + // console.log(code); + + path.replaceWith( + t.exportDefaultDeclaration(newComponent) + ); + + // 先判断有没有import React,没有的话,插入 + if (!path.findParent(t.File).node.body.some(node => t.isImportDeclaration(node) && node.source.value === 'react')) { + path.insertBefore(t.importDeclaration([t.importDefaultSpecifier(t.identifier('React'))], t.stringLiteral('react'))); + } + + } + } + } + }; +}; + diff --git a/config/utils/injectGetRender.js b/config/utils/injectGetRender.js index feb7a71..88242d2 100644 --- a/config/utils/injectGetRender.js +++ b/config/utils/injectGetRender.js @@ -4,6 +4,127 @@ const t = require('@babel/types'); const traverse = require('@babel/traverse').default; const assert = require('assert'); +function getStatements(dir) { + // web要根据this.props.width的宽度决定注入web.tsx还是web.pc.tsx + const tsxFile = join(dir, 'web.tsx'); + const jsFile = join(dir, 'web.js'); + const jsxFile = join(dir, 'web.jsx'); + + let webDestFile; + if (fs.existsSync(tsxFile)) { + webDestFile = './web.tsx'; + } + else if (fs.existsSync(jsFile)) { + webDestFile = './web.js'; + } + else if (fs.existsSync(jsxFile)) { + webDestFile = './web.jsx'; + } + const acquireWebFileStmt = webDestFile && t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier('oakRenderFn'), + t.memberExpression( + t.callExpression(t.identifier('require'), [ + t.stringLiteral(webDestFile), + ]), + t.identifier('default') + ) + ), + ]); + + let pcDestFile; + const pcTsxFile = join(dir, 'web.pc.tsx'); + const pcJsFile = join(dir, 'web.pc.js'); + const pcJsxFile = join(dir, 'web.pc.jsx'); + if (fs.existsSync(pcTsxFile)) { + pcDestFile = './web.pc.tsx'; + } + else if (fs.existsSync(pcJsFile)) { + pcDestFile = './web.pc.js'; + } + else if (fs.existsSync(pcJsxFile)) { + pcDestFile = './web.pc.jsx'; + } + const acquirePcFileStmt = pcDestFile && t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier('oakRenderFn'), + t.memberExpression( + t.callExpression(t.identifier('require'), [ + t.stringLiteral(pcDestFile), + ]), + t.identifier('default') + ) + ), + ]); + + const getStatementsInner = () => { + /** 根据tsx文件存在的情况,注入如下的getRender函数 + * if (this.props.width === 'xs') { + const oakRenderFn = require('./web.tsx').default; + return oakRenderFn; + } + else { + const oakRenderFn = require('./web.pc.tsx').default; + return oakRenderFn; + } + */ + const statements = []; + if (acquirePcFileStmt && acquireWebFileStmt) { + statements.push( + t.ifStatement( + t.binaryExpression( + '===', + t.memberExpression( + t.memberExpression( + t.thisExpression(), + t.identifier('props') + ), + t.identifier('width') + ), + t.stringLiteral('xs') + ), + t.blockStatement([ + acquireWebFileStmt, + t.returnStatement( + t.identifier('oakRenderFn') + ) + ]), + t.blockStatement([ + acquirePcFileStmt, + t.returnStatement( + t.identifier('oakRenderFn') + ) + ]) + ) + ); + } + else if (acquirePcFileStmt) { + statements.push( + acquirePcFileStmt, + t.returnStatement( + t.identifier('oakRenderFn') + ) + ); + } + else if (acquireWebFileStmt) { + statements.push( + acquireWebFileStmt, + t.returnStatement( + t.identifier('oakRenderFn') + ) + ); + } else { + assert( + false, + `${dir}文件夹中不存在web.tsx或者web.pc.tsx,无法渲染` + ); + } + return statements; + }; + + return getStatementsInner(); +} + /** * * @param {*} node @@ -15,123 +136,7 @@ function injectGetRender(node, projectRoot, filename, env) { assert(t.isCallExpression(node) && t.isIdentifier(node.callee) && node.callee.name === 'OakComponent'); const dir = dirname(filename); if (env === 'web') { - // web要根据this.props.width的宽度决定注入web.tsx还是web.pc.tsx - const tsxFile = join(dir, 'web.tsx'); - const jsFile = join(dir, 'web.js'); - const jsxFile = join(dir, 'web.jsx'); - - let webDestFile; - if (fs.existsSync(tsxFile)) { - webDestFile = './web.tsx'; - } - else if (fs.existsSync(jsFile)) { - webDestFile = './web.js'; - } - else if (fs.existsSync(jsxFile)) { - webDestFile = './web.jsx'; - } - const acquireWebFileStmt = webDestFile && t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('oakRenderFn'), - t.memberExpression( - t.callExpression(t.identifier('require'), [ - t.stringLiteral(webDestFile), - ]), - t.identifier('default') - ) - ), - ]); - - let pcDestFile; - const pcTsxFile = join(dir, 'web.pc.tsx'); - const pcJsFile = join(dir, 'web.pc.js'); - const pcJsxFile = join(dir, 'web.pc.jsx'); - if (fs.existsSync(pcTsxFile)) { - pcDestFile = './web.pc.tsx'; - } - else if (fs.existsSync(pcJsFile)) { - pcDestFile = './web.pc.js'; - } - else if (fs.existsSync(pcJsxFile)) { - pcDestFile = './web.pc.jsx'; - } - const acquirePcFileStmt = pcDestFile && t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('oakRenderFn'), - t.memberExpression( - t.callExpression(t.identifier('require'), [ - t.stringLiteral(pcDestFile), - ]), - t.identifier('default') - ) - ), - ]); - - const getStatements = () => { - /** 根据tsx文件存在的情况,注入如下的getRender函数 - * if (this.props.width === 'xs') { - const oakRenderFn = require('./web.tsx').default; - return oakRenderFn; - } - else { - const oakRenderFn = require('./web.pc.tsx').default; - return oakRenderFn; - } - */ - const statements = []; - if (acquirePcFileStmt && acquireWebFileStmt) { - statements.push( - t.ifStatement( - t.binaryExpression( - '===', - t.memberExpression( - t.memberExpression( - t.thisExpression(), - t.identifier('props') - ), - t.identifier('width') - ), - t.stringLiteral('xs') - ), - t.blockStatement([ - acquireWebFileStmt, - t.returnStatement( - t.identifier('oakRenderFn') - ) - ]), - t.blockStatement([ - acquirePcFileStmt, - t.returnStatement( - t.identifier('oakRenderFn') - ) - ]) - ) - ); - } - else if (acquirePcFileStmt) { - statements.push( - acquirePcFileStmt, - t.returnStatement( - t.identifier('oakRenderFn') - ) - ); - } - else if (acquireWebFileStmt) { - statements.push( - acquireWebFileStmt, - t.returnStatement( - t.identifier('oakRenderFn') - ) - ); - } else { - assert( - false, - `${dir}文件夹中不存在web.tsx或者web.pc.tsx,无法渲染` - ); - } - return statements; - }; - const statements = getStatements(); + const statements = getStatements(dir); node.arguments.forEach((node4) => { if (t.isObjectExpression(node4)) { const propertyRender = t.objectProperty( @@ -195,5 +200,6 @@ function injectGetRender(node, projectRoot, filename, env) { } module.exports = { - injectGetRender + injectGetRender, + getStatements }; \ No newline at end of file diff --git a/config/web/webpack.config.js b/config/web/webpack.config.js index 089446b..65fd35d 100644 --- a/config/web/webpack.config.js +++ b/config/web/webpack.config.js @@ -34,6 +34,7 @@ const oakRenderTsxPlugin = require('../babel-plugin/oakRender'); const oakI18nPlugin = require('../babel-plugin/oakI18n'); const oakStylePlugin = require('../babel-plugin/oakStyle'); const oakRpxToPxPlugin = require('../postcss-plugin/oakRpxToPx'); +const reuseOakComponentPlugin = require('../babel-plugin/reuse-oak-component'); // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; @@ -582,6 +583,7 @@ module.exports = function (webpackEnv) { oakRenderTsxPlugin, // oakRouterPlugin, oakI18nPlugin, + reuseOakComponentPlugin, // [ // 'import', // {