diff --git a/packages/driver-harmony/CHANGELOG.md b/packages/driver-harmony/CHANGELOG.md
new file mode 100644
index 000000000..6d81f6f12
--- /dev/null
+++ b/packages/driver-harmony/CHANGELOG.md
@@ -0,0 +1,3 @@
+## Changelog
+
+### v1.0.0
diff --git a/packages/driver-harmony/README.md b/packages/driver-harmony/README.md
new file mode 100644
index 000000000..27faa2519
--- /dev/null
+++ b/packages/driver-harmony/README.md
@@ -0,0 +1,28 @@
+# driver-harmony
+
+> Harmony OS driver for Rax.
+
+## Install
+
+```bash
+$ npm install --save driver-harmony
+```
+
+## Use
+
+```jsx
+import {createElement, render} from 'rax';
+import DriverHarmony from 'driver-harmony';
+
+function Example() {
+ return (
+
+
+
+ );
+}
+
+render(, null, {
+ driver: DriverHarmony
+});
+```
diff --git a/packages/driver-harmony/package.json b/packages/driver-harmony/package.json
new file mode 100644
index 000000000..b47fdeb7e
--- /dev/null
+++ b/packages/driver-harmony/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "driver-harmony",
+ "version": "1.0.0-beta.0",
+ "description": "Harmony driver for Rax",
+ "license": "BSD-3-Clause",
+ "main": "lib/index.js",
+ "module": "es/index.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/alibaba/rax.git"
+ },
+ "bugs": {
+ "url": "https://github.com/alibaba/rax/issues"
+ },
+ "homepage": "https://github.com/alibaba/rax#readme",
+ "dependencies": {
+ "style-unit": "^3.0.0"
+ },
+ "devDependencies": {
+ "rax": "^1.0.0"
+ }
+}
diff --git a/packages/driver-harmony/src/index.d.ts b/packages/driver-harmony/src/index.d.ts
new file mode 100644
index 000000000..0658336d4
--- /dev/null
+++ b/packages/driver-harmony/src/index.d.ts
@@ -0,0 +1,20 @@
+// Driver spec: https://github.com/alibaba/rax/blob/master/docs/en-US/driver-spec.md
+declare namespace Driver {
+ function createBody(): void;
+ function createEmpty(component: any): void;
+ function createText(): void;
+ function updateText(node: any, text: any)
+ function createElement(type: any, props: any, component: any)
+ function appendChild(node: any, parent: any)
+ function removeChild(node: any, parent: any)
+ function replaceChild(newChild: any, oldChild: any, parent: any)
+ function insertAfter(node: any, after: any, parent: any)
+ function insertBefore(node: any, before: any, parent: any)
+ function addEventListener(node: any, eventName: any, eventHandler: any)
+ function removeEventListener(node: any, eventName: any, eventHandler: any)
+ function setAttribute(node: any, propKey: any, propValue: any)
+ function removeAttribute(node: any, propKey: any)
+ function setStyle(node: any, styleObject: any)
+}
+
+export default Driver;
diff --git a/packages/driver-harmony/src/index.js b/packages/driver-harmony/src/index.js
new file mode 100644
index 000000000..315c632f3
--- /dev/null
+++ b/packages/driver-harmony/src/index.js
@@ -0,0 +1,200 @@
+import { convertUnit, setTargetPlatform } from 'style-unit';
+
+/**
+ * Server driver
+ **/
+const ID = 'id';
+const STYLE = 'style';
+const CHILDREN = 'children';
+const EVENT_PREFIX_REGEXP = /^on[A-Z]/;
+const REF = 'ref';
+
+const TEXT = 'text';
+const EMPTY = '';
+
+setTargetPlatform('harmony');
+
+const Driver = {
+ // Internal state
+ nodeMaps: {},
+
+ getElementById(id) {
+ return this.nodeMaps[id];
+ },
+
+ createBody() {
+ return document.body;
+ },
+
+ createComment() {
+ // Use block element as comment node, because harmony not support append comment node
+ const node = this.createElement('block');
+ node.__type = 'comment';
+ return node;
+ },
+
+ createEmpty() {
+ return this.createComment(EMPTY);
+ },
+
+ createText(text) {
+ // Use comment node type mock text node
+ const node = this.createComment(EMPTY);
+ node.value = text;
+ return node;
+ },
+
+ updateText(node, text) {
+ node.value = text;
+ this.updateTextValue(node.parentNode);
+ },
+
+ updateTextValue(node) {
+ const value = node.children.map(function(child) {
+ // Comment node type
+ return child.__type === 'comment' ? child.value : EMPTY;
+ }).join(EMPTY);
+
+ node.setAttr('value', value);
+ },
+
+ createElement(type, props) {
+ props = props || {};
+ const style = {};
+ const originStyle = props.style;
+ if (originStyle) {
+ for (let prop in originStyle) {
+ style[prop] = convertUnit(originStyle[prop], prop);
+ }
+ }
+
+ const node = document.createElement(type, {
+ style,
+ });
+
+ this.setNativeProps(node, props, true);
+
+ return node;
+ },
+
+ appendChild(node, parent) {
+ parent.appendChild(node);
+
+ if (parent.type === TEXT) {
+ this.updateTextValue(parent);
+ }
+ },
+
+ aceAppendChildren(node, parent) {
+ this.aceAppendChild(node, parent);
+ },
+
+ removeChild(node, parent) {
+ parent = parent || node.parentNode;
+ let id = node.attr && node.attr[ID];
+ if (id != null) {
+ this.nodeMaps[id] = null;
+ }
+
+ parent.removeChild(node);
+
+ if (parent.type === TEXT) {
+ this.updateTextValue(parent);
+ }
+ },
+
+ replaceChild(newChild, oldChild, parent) {
+ parent = parent || oldChild.parentNode;
+
+ let previousSibling;
+ let nextSibling;
+
+ previousSibling = oldChild.previousSibling;
+ nextSibling = oldChild.nextSibling;
+ this.removeChild(oldChild, parent);
+
+ if (previousSibling) {
+ this.insertAfter(newChild, previousSibling, parent);
+ } else if (nextSibling) {
+ this.insertBefore(newChild, nextSibling, parent);
+ } else {
+ this.appendChild(newChild, parent);
+ }
+ },
+
+ insertAfter(node, after, parent) {
+ parent = parent || after.parentNode;
+ parent.insertAfter(node, after);
+
+
+ if (parent.type === TEXT) {
+ this.updateTextValue(parent);
+ }
+ },
+
+ insertBefore(node, before, parent) {
+ parent = parent || before.parentNode;
+ parent.insertBefore(node, before);
+
+ if (parent.type === TEXT) {
+ this.updateTextValue(parent);
+ }
+ },
+
+ addEventListener(node, eventName, eventHandler) {
+ node.addEvent(eventName, eventHandler);
+ },
+
+ removeEventListener(node, eventName, eventHandler) {
+ node.removeEvent(eventName, eventHandler);
+ },
+
+ removeAttribute(node, propKey, propValue) {
+ if (propKey === ID) {
+ this.nodeMaps[propValue] = null;
+ }
+ return node.setAttr(propKey, undefined, false);
+ },
+
+ setAttribute(node, propKey, propValue) {
+ if (propKey === ID) {
+ this.nodeMaps[propValue] = node;
+ }
+ // ref is harmony node internal key
+ if (propKey === REF) return;
+ return node.setAttr(propKey, propValue);
+ },
+
+ setStyle(node, style) {
+ for (let prop in style) {
+ // Translate `rpx` to weex `px`
+ style[prop] = convertUnit(style[prop], prop);
+ }
+ node.setStyles(style);
+ },
+
+ setNativeProps(node, props, shouldIgnoreStyleProp) {
+ for (let prop in props) {
+ let value = props[prop];
+ if (prop === CHILDREN) {
+ continue;
+ }
+
+ if (value != null) {
+ if (prop === STYLE) {
+ if (shouldIgnoreStyleProp) {
+ continue;
+ }
+ this.setStyle(node, value);
+ } else if (EVENT_PREFIX_REGEXP.test(prop)) {
+ let eventName = prop.slice(2).toLowerCase();
+ this.addEventListener(node, eventName, value);
+ } else {
+ this.setAttribute(node, prop, value);
+ }
+ }
+ }
+ }
+};
+
+export default Driver;
diff --git a/packages/driver-universal/package.json b/packages/driver-universal/package.json
index 8e4da57af..b2486db35 100644
--- a/packages/driver-universal/package.json
+++ b/packages/driver-universal/package.json
@@ -1,6 +1,6 @@
{
"name": "driver-universal",
- "version": "3.3.1",
+ "version": "3.4.0-beta.1",
"description": "Driver for Universal App.",
"license": "BSD-3-Clause",
"main": "lib/index.js",
@@ -15,6 +15,7 @@
"bytedance-microapp": "./es/miniapp.js",
"baidu-smartprogram": "./es/miniapp.js",
"kuaishou-miniprogram": "./es/miniapp.js",
+ "harmony": "./es/harmony.js",
"default": "./es/index.js"
},
"./*": "./*"
@@ -36,6 +37,7 @@
"driver-kraken": "^0.2.0",
"driver-miniapp": "^0.1.0",
"driver-weex": "^2.0.0",
+ "driver-harmony": "^1.0.0-beta.0",
"universal-env": "^3.0.0"
}
}
diff --git a/packages/driver-universal/src/harmony.js b/packages/driver-universal/src/harmony.js
new file mode 100644
index 000000000..5e69cdf13
--- /dev/null
+++ b/packages/driver-universal/src/harmony.js
@@ -0,0 +1,3 @@
+import HarmonyDriver from 'driver-harmony';
+
+export default HarmonyDriver;
diff --git a/packages/driver-universal/src/index.js b/packages/driver-universal/src/index.js
index f3198ba44..1e948ff9d 100644
--- a/packages/driver-universal/src/index.js
+++ b/packages/driver-universal/src/index.js
@@ -10,7 +10,9 @@ import {
} from 'universal-env';
let currentDriver;
-if (isWeex) {
+if (typeof ace !== 'undefined') {
+ currentDriver = require('./harmony').default;
+} else if (isWeex) {
currentDriver = require('./weex').default;
} else if (isWeb) {
currentDriver = require('./web').default;
diff --git a/packages/style-unit/src/index.js b/packages/style-unit/src/index.js
index 112c180d4..08ff64743 100644
--- a/packages/style-unit/src/index.js
+++ b/packages/style-unit/src/index.js
@@ -65,7 +65,7 @@ export function calcRpx(str) {
// In Web convert rpx to 'vw', same as driver-dom and driver-universal
// '375rpx' => '50vw'
return str.replace(RPX_REG, decimalVWTransformer);
- } else if (targetPlatform === 'weex') {
+ } else if (targetPlatform === 'weex' || targetPlatform === 'harmony') {
// In Weex convert rpx to 'px'
// '375rpx' => 375 * px
return str.replace(RPX_REG, decimalPixelTransformer);
diff --git a/scripts/dist-core.js b/scripts/dist-core.js
index 56f510d57..7db471c3b 100755
--- a/scripts/dist-core.js
+++ b/scripts/dist-core.js
@@ -174,6 +174,10 @@ buildCorePackages({
packageName: 'driver-weex',
name: 'DriverKraken'
});
+buildCorePackages({
+ packageName: 'driver-harmony',
+ name: 'DriverHarmony'
+});
// Build rax compat react version to rax/lib/compat/index.js
// It needs external ../../index, which won't bundle rax into lib/compat/index.js