diff --git a/packages/miniapp-builder-shared/CHANGELOG.md b/packages/miniapp-builder-shared/CHANGELOG.md index 789a78aa..0d87ecd8 100644 --- a/packages/miniapp-builder-shared/CHANGELOG.md +++ b/packages/miniapp-builder-shared/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.2.11] + +### Added + +- Add ComponentWrapper + ## [0.2.10] - 2021-08-05 ### Changed diff --git a/packages/miniapp-builder-shared/package.json b/packages/miniapp-builder-shared/package.json index 54f6c4a7..fb5a941b 100644 --- a/packages/miniapp-builder-shared/package.json +++ b/packages/miniapp-builder-shared/package.json @@ -1,6 +1,6 @@ { "name": "miniapp-builder-shared", - "version": "0.2.10", + "version": "0.2.11", "description": "miniapp project builder shared lib", "author": "Rax Team", "homepage": "https://github.com/raxjs/miniapp#readme", diff --git a/packages/miniapp-builder-shared/src/componentWrapper.js b/packages/miniapp-builder-shared/src/componentWrapper.js new file mode 100644 index 00000000..65d2b03d --- /dev/null +++ b/packages/miniapp-builder-shared/src/componentWrapper.js @@ -0,0 +1,4 @@ +module.exports = { + WrapperPackage: 'rax-componentwrapper', + WrapperElement: 'component-wrapper' +}; diff --git a/packages/miniapp-builder-shared/src/index.js b/packages/miniapp-builder-shared/src/index.js index 4c9485f0..73396f98 100644 --- a/packages/miniapp-builder-shared/src/index.js +++ b/packages/miniapp-builder-shared/src/index.js @@ -5,6 +5,7 @@ const pathHelper = require('./pathHelper'); const platformMap = require('./platformMap'); const constants = require('./constants'); const autoInstallNpm = require('./autoInstallNpm'); +const componentWrapper = require('./componentWrapper'); module.exports = { filterNativePages, @@ -13,5 +14,6 @@ module.exports = { pathHelper, platformMap, constants, - autoInstallNpm + autoInstallNpm, + componentWrapper }; diff --git a/packages/miniapp-render/CHANGELOG.md b/packages/miniapp-render/CHANGELOG.md index 0905bdc7..657492ea 100644 --- a/packages/miniapp-render/CHANGELOG.md +++ b/packages/miniapp-render/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [2.9.0] - 2021-09-23 + +### Added + +- Use Component.setData in `rax-componentwrapper` + ## [2.8.3] - 2021-09-09 ### Changed diff --git a/packages/miniapp-render/package.json b/packages/miniapp-render/package.json index 1f06de35..f48f1362 100644 --- a/packages/miniapp-render/package.json +++ b/packages/miniapp-render/package.json @@ -1,6 +1,6 @@ { "name": "miniapp-render", - "version": "2.8.3", + "version": "2.9.0", "description": "DOM simulator for MiniApp", "files": [ "dist" diff --git a/packages/miniapp-render/src/bridge/lifeCycleAdapter.js b/packages/miniapp-render/src/bridge/lifeCycleAdapter.js index caf98cdf..dd0b78a8 100644 --- a/packages/miniapp-render/src/bridge/lifeCycleAdapter.js +++ b/packages/miniapp-render/src/bridge/lifeCycleAdapter.js @@ -1,7 +1,7 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { isMiniApp } from 'universal-env'; -export function getComponentLifeCycle({ mount, unmount, update }) { +export function getComponentLifeCycle({ mount, unmount, update, onInit }) { if (isMiniApp) { return { didMount(...args) { @@ -12,6 +12,9 @@ export function getComponentLifeCycle({ mount, unmount, update }) { }, didUnmount(...args) { unmount && unmount.apply(this, args); + }, + onInit(...args) { + onInit && onInit.apply(this, args); } }; } else { diff --git a/packages/miniapp-render/src/constants.js b/packages/miniapp-render/src/constants.js index c2857c52..392ca49d 100644 --- a/packages/miniapp-render/src/constants.js +++ b/packages/miniapp-render/src/constants.js @@ -44,3 +44,11 @@ export const CATCH_COMPONENTS = new Set(isBaiduSmartProgram || isKuaiShouMiniPro export const APPEAR_COMPONENT = 'view'; // Without appear event components export const ANCHOR_COMPONENT = 'scroll-view'; // Components which only use scrollIntoView to scroll + +export const COMPONENT_WRAPPER = 'component-wrapper'; // rax-componentwrapper tag + +export const NATIVE_TYPES = { + customComponent: 'customComponent', + miniappPlugin: 'miniappPlugin', + componentWrapper: 'componentWrapper' +}; diff --git a/packages/miniapp-render/src/createConfig/element.js b/packages/miniapp-render/src/createConfig/element.js index 8a8493aa..9da1837f 100644 --- a/packages/miniapp-render/src/createConfig/element.js +++ b/packages/miniapp-render/src/createConfig/element.js @@ -3,6 +3,7 @@ import { isMiniApp } from 'universal-env'; import createEventProxy from '../bridge/createEventProxy'; import cache from '../utils/cache'; import { getComponentLifeCycle } from '../bridge/lifeCycleAdapter'; +import { COMPONENT_WRAPPER } from '../constants'; export default function() { if (isMiniApp) { @@ -14,6 +15,16 @@ export default function() { ...getComponentLifeCycle({ mount() { cache.setElementInstance(this); + const node = cache.getNode(this.props.r.nodeId); + if (node) { + node._internal = this; + node.__isCustomComponentRoot = true; // add __isCustomComponentRoot tag to mark the custom component when getting native component instance + } + }, + onInit() { + if (this.props.__tag === COMPONENT_WRAPPER) { + this.data = this.props; // init set data + } } }) }; diff --git a/packages/miniapp-render/src/document.js b/packages/miniapp-render/src/document.js index 6252f22b..f3ff6b32 100755 --- a/packages/miniapp-render/src/document.js +++ b/packages/miniapp-render/src/document.js @@ -10,7 +10,8 @@ import Textarea from './node/element/textarea'; import Video from './node/element/video'; import CustomComponent from './node/element/custom-component'; import RootElement from './node/root'; -import { BODY_NODE_ID } from './constants'; +import { BODY_NODE_ID, COMPONENT_WRAPPER, NATIVE_TYPES } from './constants'; +import ComponentWrapper from './node/element/component-wrapper'; const CONSTRUCTOR_MAP = new Map([['img', Image], ['input', Input], ['textarea', Textarea], ['video', Video]]); @@ -61,11 +62,15 @@ class Document extends EventTarget { if (options.attrs.__native) { if (this.usingComponents[options.tagName]) { + if (options.tagName === COMPONENT_WRAPPER) { + options.nativeType = NATIVE_TYPES.componentWrapper; + return new ComponentWrapper(options); + } // Transform to custom-component - options.nativeType = 'customComponent'; + options.nativeType = NATIVE_TYPES.customComponent; return new CustomComponent(options); } else if (this.usingPlugins[options.tagName]) { - options.nativeType = 'miniappPlugin'; + options.nativeType = NATIVE_TYPES.miniappPlugin; return new CustomComponent(options); } } else { diff --git a/packages/miniapp-render/src/node/element.js b/packages/miniapp-render/src/node/element.js index 3fa441d0..50c0c18f 100755 --- a/packages/miniapp-render/src/node/element.js +++ b/packages/miniapp-render/src/node/element.js @@ -53,6 +53,9 @@ class Element extends Node { } _triggerUpdate(payload, immediate = true) { + payload.nodeId = this.__nodeId; + payload.componentWrapperId = this.componentWrapperId; + if (immediate) { this._enqueueRender(payload); } else { @@ -421,7 +424,6 @@ class Element extends Node { replaceChild(node, old) { if (!(node instanceof Node) || !(old instanceof Node)) return; - const replaceIndex = this.childNodes.indexOf(old); if (replaceIndex !== -1) this.childNodes.splice(replaceIndex, 1); diff --git a/packages/miniapp-render/src/node/element/component-wrapper.js b/packages/miniapp-render/src/node/element/component-wrapper.js new file mode 100644 index 00000000..55d00efc --- /dev/null +++ b/packages/miniapp-render/src/node/element/component-wrapper.js @@ -0,0 +1,29 @@ +import { omitFalsyFields } from '../../utils/tool'; +import Element from '../element'; + +class ComponentWrapper extends Element { + constructor(options) { + super(options); + this.__nativeType = options.nativeType; + this.__componentWrapperId = this.__nodeId; + } + + _destroy() { + super._destroy(); + this.__nativeType = null; + this.__componentWrapperId = null; + } + + get _renderInfo() { + const renderInfo = omitFalsyFields({ + nodeId: this.__nodeId, + nodeType: this.__tagName, + style: this.style.cssText, + class: this.className, + ...this.__attrs.__value + }, ['class', 'style']); + return renderInfo; + } +} + +export default ComponentWrapper; diff --git a/packages/miniapp-render/src/node/element/custom-component.js b/packages/miniapp-render/src/node/element/custom-component.js index 351817df..b7c17db8 100755 --- a/packages/miniapp-render/src/node/element/custom-component.js +++ b/packages/miniapp-render/src/node/element/custom-component.js @@ -1,6 +1,7 @@ import Element from '../element'; import cache from '../../utils/cache'; import { getId, omitFalsyFields } from '../../utils/tool'; +import { NATIVE_TYPES } from '../../constants'; class CustomComponent extends Element { constructor(options) { @@ -25,9 +26,9 @@ class CustomComponent extends Element { const config = cache.getConfig(); let nativeInfo = null; - if (this.__nativeType === 'customComponent') { + if (this.__nativeType === NATIVE_TYPES.customComponent) { nativeInfo = config.usingComponents[this.__tagName]; - } else if (this.__nativeType === 'miniappPlugin') { + } else if (this.__nativeType === NATIVE_TYPES.miniappPlugin) { nativeInfo = config.usingPlugins[this.__tagName]; } if (nativeInfo) { diff --git a/packages/miniapp-render/src/node/node.js b/packages/miniapp-render/src/node/node.js index 67596f93..80012464 100755 --- a/packages/miniapp-render/src/node/node.js +++ b/packages/miniapp-render/src/node/node.js @@ -15,6 +15,7 @@ class Node extends EventTarget { this.parentNode = null; this.__rendered = false; this.__ownerDocument = options.document; + this.__componentWrapperId = null; // ComponentWrapper Id which this belongs to } get __pageId() { @@ -60,6 +61,16 @@ class Node extends EventTarget { return this.__rendered; } + get componentWrapperId() { + if (this.__componentWrapperId) { + return this.__componentWrapperId; + } + if (this.parentNode) { + this.__componentWrapperId = this.parentNode.componentWrapperId; + } + return this.__componentWrapperId; + } + get nodeValue() { return null; } diff --git a/packages/miniapp-render/src/node/root.js b/packages/miniapp-render/src/node/root.js index 048db5a4..66d008c9 100644 --- a/packages/miniapp-render/src/node/root.js +++ b/packages/miniapp-render/src/node/root.js @@ -1,8 +1,110 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { isMiniApp } from 'universal-env'; + import Element from './element'; import cache from '../utils/cache'; import perf from '../utils/perf'; import { isFunction } from '../utils/tool'; +/** + * Return a function that filter render stacks by whether task occurs in Component-Wrapper + */ +function getFilterRenderStacks() { + if (isMiniApp) { + // In Miniapp, if sdk version != 2.x and can't use component 2, all stacks shall be passed by Page.setData although in Component-Wrapper + if (my.SDKVersion.startsWith('2') || my.canIUse('component2')) { + return (renderStacks) => { + const rootStacks = []; + const componentWrapperObject = Object.create(null); + + renderStacks.forEach(task => { + const path = task.path; + let componentWrapperNode = null; + if (task.componentWrapperId) { + componentWrapperNode = cache.getNode(task.componentWrapperId); + } + if (componentWrapperNode) { + if (!componentWrapperObject[task.componentWrapperId]) { + componentWrapperObject[task.componentWrapperId] = { + node: componentWrapperNode, + data: [] + }; + } + componentWrapperObject[task.componentWrapperId].data.push({ + ...task, + path: 'r' + path.replace(componentWrapperNode._path, '') + }); + } else { + rootStacks.push(task); + } + }); + return { + rootStacks, + componentWrapperObject + }; + }; + } + + return (renderStacks) => { + return { + rootStacks: renderStacks, + componentWrapperObject: Object.create(null) + }; + }; + } + + return (renderStacks) => { + const rootStacks = []; + const componentWrapperObject = Object.create(null); + const renderObject = Object.create(null); + const childrenValuePaths = []; + + renderStacks.forEach(task => { + const path = task.path; + if (task.type === 'children') { + childrenValuePaths.push(path); + } + renderObject[path] = task; + }); + + for (const path in renderObject) { + // If the whole father children path is set, then its children path can be deleted + childrenValuePaths.forEach(cp => { + if (path.includes(cp) && cp !== path) { + delete renderObject[path]; + } + }); + + const task = renderObject[path]; + if (task) { + let componentWrapperNode = null; + if (task.componentWrapperId) { + componentWrapperNode = cache.getNode(task.componentWrapperId); + } + if (componentWrapperNode) { + if (!componentWrapperObject[task.componentWrapperId]) { + componentWrapperObject[task.componentWrapperId] = { + node: componentWrapperNode, + data: [] + }; + } + componentWrapperObject[task.componentWrapperId].data.push({ + ...task, + path: 'r' + path.replace(componentWrapperNode._path, '') + }); + } else { + rootStacks.push(task); + } + } + } + + return { + rootStacks, + componentWrapperObject + }; + }; +} + class RootElement extends Element { constructor(options) { super(options); @@ -10,6 +112,7 @@ class RootElement extends Element { this.__allowRender = true; this.__renderStacks = []; this.__renderCallbacks = []; + this._filterRenderStacks = getFilterRenderStacks(); } _destroy() { @@ -47,60 +150,13 @@ class RootElement extends Element { const { mainPackageName } = cache.getConfig(); const window = cache.getWindow(mainPackageName); - if (internal.$batchedUpdates) { - let callback; - // there is no need to aggregate arrays if $batchedUpdate and $spliceData exist - internal.$batchedUpdates(() => { - this.__renderStacks.forEach((task, index) => { - if (index === this.__renderStacks.length - 1) { - callback = () => { - window._trigger('setDataFinished'); - if (process.env.NODE_ENV === 'development') { - perf.stop('setData'); - } - let fn; - while (fn = this.__renderCallbacks.pop()) { - fn(); - } - }; - internal.firstRenderCallback(); - } - if (task.type === 'children') { - const spliceArgs = [task.start, task.deleteCount]; - internal.$spliceData({ - [task.path]: task.item ? spliceArgs.concat(task.item) : spliceArgs - }, callback); - } else { - internal.setData({ - [task.path]: task.value - }, callback); - } - }); - }); - } else { - const renderObject = Object.create(null); - const childrenValuePaths = []; - this.__renderStacks.forEach(task => { - const path = task.path; - if (task.type === 'children') { - childrenValuePaths.push(path); - } - renderObject[path] = task.value; - }); - for (const path in renderObject) { - // If the whole father children path is set, then its children path can be deleted - childrenValuePaths.forEach(cp => { - if (path.includes(cp) && cp !== path) { - delete renderObject[path]; - } - }); - const value = renderObject[path]; - if (isFunction(value)) { - renderObject[path] = value(); - } - } - internal.firstRenderCallback(renderObject); - internal.setData(renderObject, () => { + const { rootStacks, componentWrapperObject } = this._filterRenderStacks(this.__renderStacks); + const componentWrapperCount = Object.keys(componentWrapperObject).length; + let count = rootStacks.length + componentWrapperCount; + + const callback = () => { + count--; + if (!count) { window._trigger('setDataFinished'); let fn; while (fn = this.__renderCallbacks.pop()) { @@ -109,11 +165,58 @@ class RootElement extends Element { if (process.env.NODE_ENV === 'development') { perf.stop('setData'); } + } + }; + + if (rootStacks.length > 0) { + if (internal.$batchedUpdates) { + internal.firstRenderCallback(); + this._batchedUpdate(internal, internal, rootStacks, callback); + } else { + const renderObject = Object.create(null); + rootStacks.forEach((task) => { + renderObject[task.path] = isFunction(task.value) ? task.value() : task.value; + }); + internal.firstRenderCallback(renderObject); + internal.setData(renderObject, callback); + } + } + + if (componentWrapperCount > 0) { + Object.values(componentWrapperObject).forEach(({ node, data }) => { + if (internal.$batchedUpdates) { + internal.firstRenderCallback(); + this._batchedUpdate(internal, node._internal, data, callback); + } else { + const renderObject = Object.create(null); + data.forEach((task) => { + renderObject[task.path] = isFunction(task.value) ? task.value() : task.value; + }); + internal.firstRenderCallback(renderObject); + node._internal.setData(renderObject, callback); + } }); } this.__renderStacks = []; } + + _batchedUpdate(pageInternal, internal, stacks, callback) { + pageInternal.$batchedUpdates(() => { + stacks.forEach(task => { + if (task.type === 'children') { + const spliceArgs = [task.start, task.deleteCount]; + internal.$spliceData({ + [task.path]: task.item ? spliceArgs.concat(task.item) : spliceArgs + }, callback); + } else { + internal.setData({ + [task.path]: task.value + }, callback); + } + }); + }); + } } export default RootElement; diff --git a/packages/miniapp-render/src/node/text-node.js b/packages/miniapp-render/src/node/text-node.js index 8b030981..039b510e 100755 --- a/packages/miniapp-render/src/node/text-node.js +++ b/packages/miniapp-render/src/node/text-node.js @@ -16,6 +16,8 @@ class TextNode extends Node { } _triggerUpdate(payload) { + payload.nodeId = this.__nodeId; + payload.componentWrapperId = this.componentWrapperId; this._root._enqueueRender(payload); } diff --git a/packages/miniapp-render/src/utils/cache.js b/packages/miniapp-render/src/utils/cache.js index 56300945..80b96f9a 100755 --- a/packages/miniapp-render/src/utils/cache.js +++ b/packages/miniapp-render/src/utils/cache.js @@ -10,6 +10,7 @@ const windowMap = new Map(); const elementsCache = []; const pagesCache = []; const elementMethodsCache = new Map(); +const componentWrapperCache = new Map(); // Init function init(pageId, document) { @@ -56,7 +57,6 @@ function getNode(nodeId) { return nodeIdMap.get(nodeId); } - /** * Get all nodes */ diff --git a/packages/miniapp-runtime-config/CHANGELOG.md b/packages/miniapp-runtime-config/CHANGELOG.md index 587f6c38..716b3b2d 100644 --- a/packages/miniapp-runtime-config/CHANGELOG.md +++ b/packages/miniapp-runtime-config/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.3.10] + +### Added + +- Support componentWrapper + ## [0.3.9] - 2021-07-21 ### Fixed diff --git a/packages/miniapp-runtime-config/package.json b/packages/miniapp-runtime-config/package.json index 9714da3a..3e857de1 100644 --- a/packages/miniapp-runtime-config/package.json +++ b/packages/miniapp-runtime-config/package.json @@ -1,6 +1,6 @@ { "name": "miniapp-runtime-config", - "version": "0.3.9", + "version": "0.3.10-2", "description": "miniapp runtime project config", "author": "Rax Team", "homepage": "https://github.com/raxjs/miniapp#readme", @@ -24,11 +24,11 @@ "webpack": "^4.0.0" }, "dependencies": { - "miniapp-builder-shared": "^0.2.0", - "rax-miniapp-babel-plugins": "^0.1.3", + "miniapp-builder-shared": "beta", + "rax-miniapp-babel-plugins": "beta", "rax-miniapp-config-webpack-plugin": "^2.0.0", - "rax-miniapp-runtime-webpack-plugin": "^4.0.0", - "miniapp-render": "^2.0.0" + "rax-miniapp-runtime-webpack-plugin": "beta", + "miniapp-render": "beta" }, "devDependencies": { "webpack": "^4.44.2" diff --git a/packages/miniapp-runtime-config/src/setBaseConfig.js b/packages/miniapp-runtime-config/src/setBaseConfig.js index f33a706e..d00a0b2f 100644 --- a/packages/miniapp-runtime-config/src/setBaseConfig.js +++ b/packages/miniapp-runtime-config/src/setBaseConfig.js @@ -33,6 +33,12 @@ module.exports = ( // Using plugins const usingPlugins = {}; + // hasing rax-componentwrapper + // pass variable by reference + const usingComponentWrapper = { + using: false + }; + config.output.filename('[name].js'); // publicPath should not work in miniapp, just keep default value config.output.publicPath('/'); @@ -60,6 +66,7 @@ module.exports = ( rootDir, usingPlugins, runtimeDependencies: userConfig.runtimeDependencies, + usingComponentWrapper }), }, ]; @@ -79,7 +86,8 @@ module.exports = ( nativeLifeCycleMap, usingPlugins, needCopyList, - isPluginProject + isPluginProject, + usingComponentWrapper }, ]); diff --git a/packages/rax-miniapp-babel-plugins/CHANGELOG.md b/packages/rax-miniapp-babel-plugins/CHANGELOG.md index 3fe5d50f..3087bded 100644 --- a/packages/rax-miniapp-babel-plugins/CHANGELOG.md +++ b/packages/rax-miniapp-babel-plugins/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.1.16] + +### Added + +- Support ComponentWrapper + ## [0.1.15] - 2021-05-11 ### Changed diff --git a/packages/rax-miniapp-babel-plugins/package.json b/packages/rax-miniapp-babel-plugins/package.json index 0bb45253..a1e151af 100644 --- a/packages/rax-miniapp-babel-plugins/package.json +++ b/packages/rax-miniapp-babel-plugins/package.json @@ -1,6 +1,6 @@ { "name": "rax-miniapp-babel-plugins", - "version": "0.1.15", + "version": "0.1.16-3", "description": "rax miniapp babel plugins", "main": "src/index.js", "repository": { @@ -22,6 +22,6 @@ "@babel/code-frame": "^7.8.3", "fs-extra": "^9.0.1", "md5": "^2.2.1", - "miniapp-builder-shared": "^0.2.0" + "miniapp-builder-shared": "0.2.11-0" } } diff --git a/packages/rax-miniapp-babel-plugins/src/index.js b/packages/rax-miniapp-babel-plugins/src/index.js index 0964646f..243be2cf 100644 --- a/packages/rax-miniapp-babel-plugins/src/index.js +++ b/packages/rax-miniapp-babel-plugins/src/index.js @@ -1,4 +1,4 @@ -module.exports = function({ usingComponents, nativeLifeCycleMap, target, rootDir, usingPlugins, runtimeDependencies }) { +module.exports = function({ usingComponents, nativeLifeCycleMap, target, rootDir, usingPlugins, runtimeDependencies, usingComponentWrapper }) { return [ require.resolve('./plugins/babel-plugin-remove-Function'), require.resolve('./plugins/babel-plugin-external-module'), @@ -14,7 +14,8 @@ module.exports = function({ usingComponents, nativeLifeCycleMap, target, rootDir usingComponents, target, rootDir, - runtimeDependencies + runtimeDependencies, + usingComponentWrapper } ], [ diff --git a/packages/rax-miniapp-babel-plugins/src/plugins/babel-plugin-handle-native-component.js b/packages/rax-miniapp-babel-plugins/src/plugins/babel-plugin-handle-native-component.js index 1a258e29..e0fe82e9 100644 --- a/packages/rax-miniapp-babel-plugins/src/plugins/babel-plugin-handle-native-component.js +++ b/packages/rax-miniapp-babel-plugins/src/plugins/babel-plugin-handle-native-component.js @@ -2,7 +2,10 @@ const { resolve, dirname, join } = require('path'); const { existsSync, readJSONSync } = require('fs-extra'); const { pathHelper: { absoluteModuleResolve, removeExt }, - platformMap + platformMap, + componentWrapper: { + WrapperPackage + } } = require('miniapp-builder-shared'); const extMap = require('../utils/extMap'); const { collectComponentAttr, collectUsings } = require('../utils/handleComponentAST'); @@ -121,7 +124,7 @@ function hasDefaultSpecifier(specifiers, t) { module.exports = function visitor( { types: t }, - { usingComponents, target, rootDir, runtimeDependencies } + { usingComponents, target, rootDir, runtimeDependencies, usingComponentWrapper } ) { // Collect imported dependencies let nativeComponents = {}; @@ -151,6 +154,15 @@ module.exports = function visitor( }); } collectUsings(path, nativeComponents, usingComponents, filePath, t); + } else if (source.value === WrapperPackage) { + usingComponentWrapper.using = true; + if (!scanedPageMap[filename]) { + scanedPageMap[filename] = true; + path.parentPath.traverse({ + JSXOpeningElement: collectComponentAttr(nativeComponents, t) + }); + } + collectUsings(path, nativeComponents, usingComponents, filePath, t); } } }, diff --git a/packages/rax-miniapp-babel-plugins/src/utils/handleComponentAST.js b/packages/rax-miniapp-babel-plugins/src/utils/handleComponentAST.js index f6d66fe9..eec4736e 100644 --- a/packages/rax-miniapp-babel-plugins/src/utils/handleComponentAST.js +++ b/packages/rax-miniapp-babel-plugins/src/utils/handleComponentAST.js @@ -1,4 +1,5 @@ const getTagName = require('./getTagName'); +const {componentWrapper: { WrapperElement, WrapperPackage }} = require('miniapp-builder-shared'); function collectComponentAttr(components, t) { return (innerPath) => { @@ -39,7 +40,7 @@ function collectUsings( filePath, t ) { - const { specifiers } = path.node; + const { specifiers, source } = path.node; specifiers.some((specifier, index) => { if (!t.isImportDefaultSpecifier(specifier)) return; @@ -57,12 +58,13 @@ function collectUsings( }); // Generate a random tag name - const replacedTagName = getTagName(tagName); + const isComponentWrapper = source.type === 'StringLiteral' && source.value === WrapperPackage; + const replacedTagName = isComponentWrapper ? WrapperElement : getTagName(tagName); if (!usings[replacedTagName]) { usings[replacedTagName] = { props: [], events: [] }; } usings[replacedTagName] = { - path: filePath, + path: isComponentWrapper ? './wrapper' : filePath, props: [ ...new Set(componentInfo.props.concat(usings[replacedTagName].props)), ], diff --git a/packages/rax-miniapp-runtime-webpack-plugin/package.json b/packages/rax-miniapp-runtime-webpack-plugin/package.json index 3ee4b7e3..c9473b66 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/package.json +++ b/packages/rax-miniapp-runtime-webpack-plugin/package.json @@ -1,6 +1,6 @@ { "name": "rax-miniapp-runtime-webpack-plugin", - "version": "4.11.0", + "version": "4.12.0", "description": "A webpack plugin for miniapp runtime build", "main": "src/index.js", "files": [ @@ -21,12 +21,15 @@ "csso": "^4.0.3", "fs-extra": "^8.1.0", "lodash.isequal": "^4.5.0", - "miniapp-builder-shared": "^0.2.0", + "miniapp-builder-shared": "0.2.11-0", "path-to-regexp": "^3.0.0", "postcss": "^7.0.17", "pretty-data": "^0.40.0", "terser": "^4.6.10", "webpack": "^4.35.3", "webpack-sources": "^1.3.0" + }, + "resolutions": { + "miniapp-builder-shared": "0.2.11-0" } } diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/componentWrapper.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/componentWrapper.js new file mode 100644 index 00000000..a3adcbff --- /dev/null +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/componentWrapper.js @@ -0,0 +1,92 @@ +/** + * generate ComponentWrapper +*/ +const { join } = require('path'); +const { platformMap, componentWrapper: { WrapperElement } } = require('miniapp-builder-shared'); + +const platformConfig = require('../platforms'); +const { RECURSIVE_TEMPLATE_TYPE, UNRECURSIVE_TEMPLATE_TYPE } = require('../constants'); + +const addFileToCompilation = require('../utils/addFileToCompilation'); +const getAssetPath = require('../utils/getAssetPath'); +const isNpmModule = require('../utils/isNpmModule'); +const rmCurDirPathSymbol = require('../utils/rmCurDirPathSymbol'); +const { generateRootTmpl } = require('./root'); +const { buildTemplate, buildNativeComponentTemplate, buildSjs } = require('./templates'); + +function ensureWrapperFolder() { + +} + +function generateWrapperJS(compilation, { target, command, subAppRoot = '' }) { + const filename = join(subAppRoot, 'wrapper.js'); + addFileToCompilation(compilation, { + filename, + content: +`const render = require('${getAssetPath('render.js', filename)}'); + +Component(render.createElementConfig());`, + target, + command, + }); +} + +function generateWrapperTemplate(compilation, + { target, command, subAppRoot = '' }) { + const { adapter: { formatBindedData, for: targetFor, key } } = platformConfig[target]; + + let content = ` + + +`; + + const isRecursiveTemplate = RECURSIVE_TEMPLATE_TYPE.has(target); + if (!isRecursiveTemplate) { + content = `` + content; + } else { + content = `` + content; + } + + addFileToCompilation(compilation, { + filename: join(subAppRoot, `wrapper${platformMap[target].extension.xml}`), + content, + target, + command + }); +} + +function generateWrapperJSON(compilation, { usingComponents, usingPlugins, target, command, subAppRoot = '' }) { + const content = { + component: true, + usingComponents: { + [WrapperElement]: './wrapper' + } + }; + + if (UNRECURSIVE_TEMPLATE_TYPE.has(target)) { + content.usingComponents.element = './comp'; + } + + Object.keys(usingComponents).forEach(component => { + const componentPath = usingComponents[component].path; + content.usingComponents[component] = isNpmModule(componentPath) ? componentPath : getAssetPath(rmCurDirPathSymbol(componentPath), join(subAppRoot, 'comp')); + }); + Object.keys(usingPlugins).forEach(plugin => { + content.usingComponents[plugin] = usingPlugins[plugin].path; + }); + + addFileToCompilation(compilation, { + filename: join(subAppRoot, 'wrapper.json'), + content: JSON.stringify(content, null, 2), + target, + command, + }); +} + + +module.exports = { + ensureWrapperFolder, + generateWrapperJS, + generateWrapperTemplate, + generateWrapperJSON, +}; diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/element.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/element.js index d12e9cee..b5626c01 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/element.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/element.js @@ -10,6 +10,7 @@ const isNpmModule = require('../utils/isNpmModule'); const rmCurDirPathSymbol = require('../utils/rmCurDirPathSymbol'); const { generateRootTmpl } = require('./root'); const { buildTemplate, buildNativeComponentTemplate, buildSjs } = require('./templates'); +const { buildComponentWrapperTemplate } = require('./componentWrapper'); function generateElementJS(compilation, { target, command, subAppRoot = '' }) { @@ -45,7 +46,9 @@ function generateElementTemplate(compilation, }); const template = buildTemplate(target, modifyTemplate, { isRecursiveTemplate }); - const nativeComponentTemplate = buildNativeComponentTemplate(usingPlugins, target, isRecursiveTemplate) + buildNativeComponentTemplate(usingComponents, target, isRecursiveTemplate); + const nativeComponentTemplate = + buildNativeComponentTemplate(usingPlugins, target, isRecursiveTemplate) + + buildNativeComponentTemplate(usingComponents, target, isRecursiveTemplate); // In recursiveTemplate, root.axml need be written into comp.axml content = template + nativeComponentTemplate + content; diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/index.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/index.js index 0f94f82b..2663cece 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/index.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/index.js @@ -14,6 +14,13 @@ const { } = require('./element'); const generateRender = require('./render'); const generatePkg = require('./pkg'); +const { + ensureWrapperFolder, + generateWrapperJS, + generateWrapperTemplate, + generateWrapperJSON, + buildComponentWrapperTemplate +} = require('./componentWrapper'); module.exports = { generateAppCSS, @@ -28,5 +35,10 @@ module.exports = { generateElementJSON, generateElementTemplate, generateRender, - generatePkg + generatePkg, + ensureWrapperFolder, + generateWrapperJS, + generateWrapperTemplate, + generateWrapperJSON, + buildComponentWrapperTemplate }; diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/page.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/page.js index a03f3136..bb01f0e4 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/page.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/page.js @@ -1,5 +1,5 @@ const { join } = require('path'); -const { platformMap, pathHelper: { getBundlePath }} = require('miniapp-builder-shared'); +const { platformMap, pathHelper: { getBundlePath }, componentWrapper: { WrapperElement }} = require('miniapp-builder-shared'); const platformConfig = require('../platforms'); const getAssetPath = require('../utils/getAssetPath'); @@ -96,6 +96,7 @@ function generatePageJSON( useComponent, usingComponents, usingPlugins, pageRoute, + usingComponentWrapper, { target, command, subAppRoot = '' } ) { if (!pageConfig.usingComponents) { @@ -110,6 +111,10 @@ function generatePageJSON( const componentPath = usingComponents[component].path; pageConfig.usingComponents[component] = isNpmModule(componentPath) ? componentPath : getAssetPath(rmCurDirPathSymbol(componentPath), pageRoute); }); + + if (usingComponentWrapper) { + pageConfig.usingComponents[WrapperElement] = getAssetPath(join(subAppRoot, 'wrapper'), pageRoute); + } Object.keys(usingPlugins).forEach(plugin => { pageConfig.usingComponents[plugin] = usingPlugins[plugin].path; }); diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/root.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/root.js index 550a102b..82c96ef3 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/root.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/root.js @@ -1,6 +1,7 @@ const { platformMap } = require('miniapp-builder-shared'); const addFileToCompilation = require('../utils/addFileToCompilation'); +const { buildComponentWrapperTemplate } = require('./componentWrapper'); const { buildTemplate, buildSjs, buildNativeComponentTemplate } = require('./templates'); function generateRootTmpl( @@ -9,7 +10,9 @@ function generateRootTmpl( ) { const template = buildTemplate(target, modifyTemplate); const sjs = buildSjs(target); - const nativeComponentTemplate = buildNativeComponentTemplate(usingPlugins, target) + buildNativeComponentTemplate(usingComponents, target); + const nativeComponentTemplate = + buildNativeComponentTemplate(usingPlugins, target) + + buildNativeComponentTemplate(usingComponents, target); addFileToCompilation(compilation, { filename: `${subAppRoot}/root${platformMap[target].extension.xml}`, diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/templates/index.js b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/templates/index.js index 37bdd531..c3bbcb08 100644 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/generators/templates/index.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/generators/templates/index.js @@ -1,3 +1,5 @@ +const { componentWrapper: {WrapperElement} } = require('miniapp-builder-shared'); + const platformConfig = require('../../platforms'); const { buildRecursiveTemplate, buildRecursiveTemplateSjs } = require('./recursiveTemplate'); const { buildUnrecursiveTemplate, buildUnrecursiveTemplateSjs } = require('./unrecursiveTemplate'); @@ -31,6 +33,14 @@ function buildNativeComponentTemplate(usings, target) { const { formatBindedData, supportSjs } = adapter; return Object.keys(usings).reduce((current, componentTag) => { + if (componentTag === WrapperElement) { + return ` +`; + } const props = usings[componentTag].props.reduce((cur, prop) => { const tmpl = ` ${prop}="{{r['${prop}']}}"`; return cur + tmpl; diff --git a/packages/rax-miniapp-runtime-webpack-plugin/src/index.js b/packages/rax-miniapp-runtime-webpack-plugin/src/index.js index 7abddcc3..e3a05fd4 100755 --- a/packages/rax-miniapp-runtime-webpack-plugin/src/index.js +++ b/packages/rax-miniapp-runtime-webpack-plugin/src/index.js @@ -22,7 +22,10 @@ const { generateElementJSON, generateElementTemplate, generateRender, - generatePkg + generatePkg, + generateWrapperJS, + generateWrapperTemplate, + generateWrapperJSON } = require('./generators'); const getFinalRouteMap = require('./utils/getFinalRouteMap'); @@ -47,7 +50,8 @@ class MiniAppRuntimePlugin { mainPackageRoot, appConfig, subAppConfigList = [], - isPluginProject = false + isPluginProject = false, + usingComponentWrapper: _usingComponentWrapper } = options; const { context: { command, userConfig: rootUserConfig, rootDir }, @@ -58,6 +62,7 @@ class MiniAppRuntimePlugin { let isFirstRender = true; let lastUsingComponents = {}; let lastUsingPlugins = {}; + let lastImportComponentWrapper = false; let needAutoInstallDependency = false; const packageJsonFilePath = []; @@ -74,6 +79,7 @@ class MiniAppRuntimePlugin { }); compiler.hooks.emit.tapAsync(PluginName, (compilation, callback) => { + const usingComponentWrapper = _usingComponentWrapper.using; const outputPath = compilation.outputOptions.path; const sourcePath = join(rootDir, 'src'); const pages = []; @@ -93,14 +99,17 @@ class MiniAppRuntimePlugin { if (!isFirstRender) { useComponentChanged = !isEqual(usingComponents, lastUsingComponents) || - !isEqual(usingPlugins, lastUsingPlugins); + !isEqual(usingPlugins, lastUsingPlugins) || + lastImportComponentWrapper !== usingComponentWrapper; } lastUsingComponents = Object.assign({}, usingComponents); lastUsingPlugins = Object.assign({}, usingPlugins); + lastImportComponentWrapper = usingComponentWrapper; + const useComponent = Object.keys(lastUsingPlugins).length + Object.keys(lastUsingComponents).length > - 0; + 0 || usingComponentWrapper; // These files need be written in first render if (isFirstRender) { // render.js @@ -144,7 +153,8 @@ class MiniAppRuntimePlugin { pages, target, command, - subPackages + subPackages, + usingComponentWrapper }); // Only when developer may use native component, it will generate package.json in output @@ -188,7 +198,8 @@ class MiniAppRuntimePlugin { usingComponents: mainPackageUsingComponents, usingPlugins: mainPackageUsingPlugins, target, - command + command, + usingComponentWrapper }); generateElementTemplate(compilation, { usingComponents: mainPackageUsingComponents, @@ -197,6 +208,25 @@ class MiniAppRuntimePlugin { command, modifyTemplate }); + if (usingComponentWrapper) { + generateWrapperJS(compilation, { + target, + command + }); + generateWrapperJSON(compilation, { + usingComponents: mainPackageUsingComponents, + usingPlugins: mainPackageUsingPlugins, + target, + command + }); + generateWrapperTemplate(compilation, { + usingComponents: mainPackageUsingComponents, + usingPlugins: mainPackageUsingPlugins, + target, + command, + modifyTemplate + }); + } } else { // Only when there isn't native component, it need generate root template file // Generate root template xml @@ -267,7 +297,7 @@ class MiniAppRuntimePlugin { usingPlugins: subPackageUsingPlugins, target, command, - subAppRoot + subAppRoot, }); generateElementTemplate(compilation, { usingComponents: subPackageUsingComponents, @@ -304,6 +334,7 @@ class MiniAppRuntimePlugin { isSubPackagePage ? subPackageUsingComponents : mainPackageUsingComponents, isSubPackagePage ? subPackageUsingPlugins : mainPackageUsingPlugins, entryName, + usingComponentWrapper, { target, command,