+ [jsfm] add weex variable api in runtime
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/b7383a41 Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/b7383a41 Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/b7383a41 Branch: refs/heads/release Commit: b7383a41670a231bb24893beb0f5bed848f5be47 Parents: f5fbd4e Author: Hanks <zhanghan...@gmail.com> Authored: Wed Oct 11 17:19:48 2017 +0800 Committer: gurisxie <279483...@qq.com> Committed: Tue Oct 17 17:27:39 2017 +0800 ---------------------------------------------------------------------- html5/runtime/api/WeexInstance.js | 126 +++++++++++++++++++++++++++++++++ html5/runtime/api/component.js | 51 +++++++++++++ html5/runtime/api/init.js | 107 +++++++++++++++++----------- html5/runtime/api/module.js | 56 +++++++++++++++ 4 files changed, 300 insertions(+), 40 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b7383a41/html5/runtime/api/WeexInstance.js ---------------------------------------------------------------------- diff --git a/html5/runtime/api/WeexInstance.js b/html5/runtime/api/WeexInstance.js new file mode 100644 index 0000000..2093f17 --- /dev/null +++ b/html5/runtime/api/WeexInstance.js @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Document from '../vdom/Document' +import { isRegisteredModule, getModuleDescription } from './module' +import { isRegisteredComponent } from './component' + +const moduleProxies = {} + +function setId (weex, id) { + Object.defineProperty(weex, '[[CurrentInstanceId]]', { value: id }) +} + +function getId (weex) { + return weex['[[CurrentInstanceId]]'] +} + +function moduleGetter (module, method, taskCenter) { + return (...args) => taskCenter.send('module', { module, method }, args) +} + +export default class WeexInstance { + constructor (id, config) { + setId(this, id) + this.config = config || {} + this.document = new Document(id, this.config.bundleUrl) + this.requireModule = this.requireModule.bind(this) + this.isRegisteredModule = isRegisteredModule + this.isRegisteredComponent = isRegisteredComponent + } + + requireModule (moduleName) { + const id = getId(this) + if (!(id && this.document && this.document.taskCenter)) { + console.error(`[JS Framework] invalid instance id "${id}"`) + return + } + + // warn for unknown module + if (!isRegisteredModule(moduleName)) { + console.warn(`[JS Framework] using unregistered weex module "${moduleName}"`) + return + } + + // create new module proxy + if (!moduleProxies[moduleName]) { + const moduleDefine = getModuleDescription(moduleName) + const taskCenter = this.document.taskCenter + + // create registered module apis + const moduleApis = {} + for (const methodName in moduleDefine) { + Object.defineProperty(moduleApis, methodName, { + enumerable: true, + configurable: true, + get: () => moduleGetter(moduleName, methodName, taskCenter), + set (fn) { + if (typeof fn === 'function') { + return taskCenter.send('module', { + module: moduleName, + method: methodName + }, [fn]) + } + } + }) + } + + // create module Proxy + if (typeof Proxy === 'function') { + moduleProxies[moduleName] = new Proxy(moduleApis, { + get (target, methodName) { + if (methodName in target) { + return target[methodName] + } + console.warn(`[JS Framework] using unregistered method "${moduleName}.${methodName}"`) + return moduleGetter(moduleName, methodName, taskCenter) + } + }) + } + else { + moduleProxies[moduleName] = moduleApis + } + } + + return moduleProxies[moduleName] + } + + supports (condition) { + if (typeof condition !== 'string') return null + + const res = condition.match(/^@(\w+)\/(\w+)(\.(\w+))?$/i) + if (res) { + const type = res[1] + const name = res[2] + const method = res[4] + switch (type) { + case 'module': return isRegisteredModule(name, method) + case 'component': return isRegisteredComponent(name) + } + } + + return null + } + + // registerStyleSheet (styles) { + // if (this.document) { + // this.document.registerStyleSheet(styles) + // } + // } +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b7383a41/html5/runtime/api/component.js ---------------------------------------------------------------------- diff --git a/html5/runtime/api/component.js b/html5/runtime/api/component.js new file mode 100644 index 0000000..8a56961 --- /dev/null +++ b/html5/runtime/api/component.js @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { registerElement } from '../vdom/WeexElement' + +const weexComponents = {} + +/** + * Register native components information. + * @param {array} newComponents + */ +export function registerComponents (newComponents) { + if (Array.isArray(newComponents)) { + newComponents.forEach(component => { + if (!component) { + return + } + if (typeof component === 'string') { + weexComponents[component] = true + } + else if (typeof component === 'object' && typeof component.type === 'string') { + weexComponents[component.type] = component + registerElement(component.type, component.methods) + } + }) + } +} + +/** + * Check whether the component has been registered. + * @param {String} component name + */ +export function isRegisteredComponent (name) { + return !!weexComponents[name] +} http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b7383a41/html5/runtime/api/init.js ---------------------------------------------------------------------- diff --git a/html5/runtime/api/init.js b/html5/runtime/api/init.js index 024afa7..cad0179 100644 --- a/html5/runtime/api/init.js +++ b/html5/runtime/api/init.js @@ -18,8 +18,10 @@ */ import { init as initTaskHandler } from '../bridge/TaskCenter' -import { registerElement } from '../vdom/WeexElement' +import { registerModules } from './module' +import { registerComponents } from './component' import { services, register, unregister } from './service' +import WeexInstance from './WeexInstance' let frameworks let runtimeConfig @@ -95,58 +97,61 @@ function createInstance (id, code, config, data) { config = JSON.parse(JSON.stringify(config || {})) config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {})) - const context = { - config, + const weex = new WeexInstance(id, config) + Object.freeze(weex) + + const runtimeEnv = { + weex, + config, // TODO: deprecated created: Date.now(), framework: bundleType } - context.services = createServices(id, context, runtimeConfig) - instanceMap[id] = context + runtimeEnv.services = createServices(id, runtimeEnv, runtimeConfig) + instanceMap[id] = runtimeEnv - if (process.env.NODE_ENV === 'development') { - console.debug(`[JS Framework] create an ${bundleType} instance`) - } + const runtimeContext = Object.create(null) + Object.assign(runtimeContext, runtimeEnv.services, { weex }) - const fm = frameworks[bundleType] - if (!fm) { + const framework = runtimeConfig.frameworks[bundleType] + if (!framework) { return new Error(`invalid bundle type "${bundleType}".`) } - return fm.createInstance(id, code, config, data, context) -} - -const methods = { - createInstance, - registerService: register, - unregisterService: unregister + // run create instance + if (typeof framework.prepareInstanceContext === 'function') { + const instanceContext = framework.prepareInstanceContext(runtimeContext) + return runInContext(code, instanceContext) + } + return framework.createInstance(id, code, config, data, runtimeEnv) } /** - * Register methods which init each frameworks. - * @param {string} methodName + * Run js code in a specific context. + * @param {string} code + * @param {object} context */ -function genInit (methodName) { - methods[methodName] = function (...args) { - if (methodName === 'registerComponents') { - checkComponentMethods(args[0]) - } - for (const name in frameworks) { - const framework = frameworks[name] - if (framework && framework[methodName]) { - framework[methodName](...args) - } - } +function runInContext (code, context) { + const keys = [] + const args = [] + for (const key in context) { + keys.push(key) + args.push(context[key]) } + + const bundle = ` + (function (global) { + "use strict"; + ${code} + })(Object.create(this)) + ` + + return (new Function(...keys, bundle))(...args) } -function checkComponentMethods (components) { - if (Array.isArray(components)) { - components.forEach((name) => { - if (name && name.type && name.methods) { - registerElement(name.type, name.methods) - } - }) - } +const methods = { + createInstance, + registerService: register, + unregisterService: unregister } /** @@ -203,6 +208,27 @@ function adaptInstance (methodName, nativeMethodName) { } } +/** + * Register methods which init each frameworks. + * @param {string} methodName + * @param {function} sharedMethod + */ +function adaptMethod (methodName, sharedMethod) { + methods[methodName] = function (...args) { + if (typeof sharedMethod === 'function') { + sharedMethod(...args) + } + + // TODO: deprecated + for (const name in runtimeConfig.frameworks) { + const framework = runtimeConfig.frameworks[name] + if (framework && framework[methodName]) { + framework[methodName](...args) + } + } + } +} + export default function init (config) { runtimeConfig = config || {} frameworks = runtimeConfig.frameworks || {} @@ -216,8 +242,9 @@ export default function init (config) { framework.init(config) } - // @todo: The method `registerMethods` will be re-designed or removed later. - ; ['registerComponents', 'registerModules', 'registerMethods'].forEach(genInit) + adaptMethod('registerComponents', registerComponents) + adaptMethod('registerModules', registerModules) + adaptMethod('registerMethods') ; ['destroyInstance', 'refreshInstance', 'receiveTasks', 'getRoot'].forEach(genInstance) http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/b7383a41/html5/runtime/api/module.js ---------------------------------------------------------------------- diff --git a/html5/runtime/api/module.js b/html5/runtime/api/module.js new file mode 100644 index 0000000..df26b92 --- /dev/null +++ b/html5/runtime/api/module.js @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const weexModules = {} + +/** + * Register native modules information. + * @param {object} newModules + */ +export function registerModules (newModules) { + for (const name in newModules) { + if (!weexModules[name]) { + weexModules[name] = {} + } + newModules[name].forEach(method => { + if (typeof method === 'string') { + weexModules[name][method] = true + } + else { + weexModules[name][method.name] = method.args + } + }) + } +} + +/** + * Check whether the module or the method has been registered. + * @param {String} module name + * @param {String} method name (optional) + */ +export function isRegisteredModule (name, method) { + if (typeof method === 'string') { + return !!(weexModules[name] && weexModules[name][method]) + } + return !!weexModules[name] +} + +export function getModuleDescription (name) { + return weexModules[name] +}