diff --git a/packages/chameleon-tool/configs/getCommonConfig.js b/packages/chameleon-tool/configs/getCommonConfig.js index e0ad61e2c88fc16385d079dea0844ff134b2c682..c7959597e5d0378eb0ddcfc54b8477b1ae266971 100644 --- a/packages/chameleon-tool/configs/getCommonConfig.js +++ b/packages/chameleon-tool/configs/getCommonConfig.js @@ -38,7 +38,6 @@ module.exports = function (options) { publicPath = `http://${config.ip}:${webServerPort}/${type}/` } - let commonConfig = { stats: cml.logLevel === 'debug' ? 'verbose' : 'none', output: { @@ -79,9 +78,7 @@ module.exports = function (options) { options: { 'filename': path.join(cml.root, 'chameleon.js') } - } - - ] + }] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, diff --git a/packages/chameleon-tool/configs/mvvm/getExtendConfig.js b/packages/chameleon-tool/configs/mvvm/getExtendConfig.js index e3bbe06802b7643937b9fec1f33bfe12ab38bc89..b6984c8a197932c9203bf2c0177f352fa16be8b0 100644 --- a/packages/chameleon-tool/configs/mvvm/getExtendConfig.js +++ b/packages/chameleon-tool/configs/mvvm/getExtendConfig.js @@ -4,6 +4,9 @@ const getCommonConfig = require('../getCommonConfig'); const utils = require('../utils.js'); const {MvvmGraphPlugin} = require('mvvm-pack'); const resolve = require('resolve'); +const originSourceLoader = { + loader: path.join(__dirname, './originSourceLoader.js') +}; module.exports = function(options) { let {type, media} = options; @@ -15,6 +18,15 @@ module.exports = function(options) { media }); cml.extPlatformPlugin[type] = platformPlugin; + + function getCmlLoaders() { + let loaders = utils.cssLoaders({type, media}); + loaders.js = [ + loaders.js, + originSourceLoader + ] + return loaders; + } let extendConfig = { entry: { app: path.join(cml.projectRoot, 'src/app/app.cml') @@ -30,7 +42,7 @@ module.exports = function(options) { use: [{ loader: 'mvvm-cml-loader', options: { - loaders: utils.cssLoaders({type, media}), + loaders: getCmlLoaders(), cmlType: type, media, check: cml.config.get().check @@ -46,13 +58,25 @@ module.exports = function(options) { }, platformPlugin) ] }; - // options.moduleIdType = 'hash'; let commonConfig = getCommonConfig(options); commonConfig.module.rules.forEach(item => { + // 静态资源的处理 if (~['chameleon-url-loader', 'file-loader'].indexOf(item.loader)) { item.loader = 'mvvm-file-loader'; item.options.publicPath = commonConfig.output.publicPath } + + if (item.test instanceof RegExp) { + // interface获取originSource + if (item.test.test('name.interface')) { + item.use.splice(1, 0, originSourceLoader) + } + + // js获取originSource + if (item.test.test('name.js')) { + item.use.push(originSourceLoader) + } + } }) // 用户可以扩展webpack的rules用于处理特有文件后缀 diff --git a/packages/chameleon-tool/configs/mvvm/originSourceLoader.js b/packages/chameleon-tool/configs/mvvm/originSourceLoader.js new file mode 100644 index 0000000000000000000000000000000000000000..d8de013b7ddf7b1ebd578d4628634b4b07e599a7 --- /dev/null +++ b/packages/chameleon-tool/configs/mvvm/originSourceLoader.js @@ -0,0 +1,7 @@ +/** + * js 模块获取节点源代码,在interface-loader处理后 babel-loader前 + */ +module.exports = function (output) { + this._module._cmlOriginSource = output; + return output; +} diff --git a/packages/chameleon-tool/configs/mvvm/styleWrapLoader.js b/packages/chameleon-tool/configs/mvvm/styleWrapLoader.js deleted file mode 100644 index 2f3554866cc0cdf83ad2afeb4400aac26800a9e9..0000000000000000000000000000000000000000 --- a/packages/chameleon-tool/configs/mvvm/styleWrapLoader.js +++ /dev/null @@ -1,33 +0,0 @@ - -/** 为style模块包装 防止webpack build moudle error */ -module.exports = function(content) { - this._module._nodeType = 'module'; - this._module._moduleType = 'style'; - this._module._cmlSource = content; - return `module.exports = ${JSON.stringify(content)}` -} -const postcss = require('postcss'); - -module.exports = function({source, filePath, compiler}) { - let deps = []; - const assetsPlugin = postcss.plugin('postcss-assets-plugin', function(options) { - return (root, result) => { - root.walkDecls((decl, i) => { - if (~decl.value.indexOf('url')) { - decl.value = decl.value.replace(/url\s*\(\s*[\'\"]?(.+?)[\'\"]?\s*\)/g, function(all, $1) { - let realDependPath = compiler.resolve(filePath, $1); - deps.push(realDependPath); - let publicPath = compiler.getPublicPath(realDependPath); - return publicPath; - }) - } - }) - } - }) - - return { - source: postcss([assetsPlugin]).process(source).css, - deps - } -} - diff --git a/packages/chameleon-tool/configs/mvvm/utils.js b/packages/chameleon-tool/configs/mvvm/utils.js deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/packages/chameleon-tool/configs/mvvm/utils.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/packages/interface-loader/src/index.js b/packages/interface-loader/src/index.js index f69acc9aea402f3a708c22d69db2ac78487e805c..c2906bc4ae184c6f7f0dc405ed2b7a1a26f17983 100644 --- a/packages/interface-loader/src/index.js +++ b/packages/interface-loader/src/index.js @@ -1,19 +1,12 @@ const loaderUtils = require('loader-utils') const mvvmInterfaceParser = require('mvvm-interface-parser'); -const path = require('path') module.exports = function (source) { const rawOptions = loaderUtils.getOptions(this); const options = rawOptions || {}; // loader的类型 wx web weex let {cmlType, media, check = {}} = options; const filePath = this.resourcePath; - let self = this; - const resolve = function(filePath, relativePath) { - - let context = path.dirname(filePath); - self.resolveSync(context, relativePath); - } // todo let {result, devDeps} = mvvmInterfaceParser({cmlType, media, source, filePath, check}); devDeps.forEach(item => { diff --git a/packages/mvvm-interface-parser/lib/check.js b/packages/mvvm-interface-parser/lib/check.js index 70bffe51e94b4f85fbcc50e8cc6caf43fadea973..2dfc3a61f658bf23ef09abde1ee9f6e9f5730d57 100644 --- a/packages/mvvm-interface-parser/lib/check.js +++ b/packages/mvvm-interface-parser/lib/check.js @@ -20,7 +20,7 @@ const handlExport = function (ast) { // 拿到 new Method(); 这一段代码 let declarationCode = generate["default"](path.node.declaration); // 得到 export default __OBJECT__WARPPER__(new Method()); - let codeSeg = exportCode.code.replace(declarationCode.code, '__OBJECT__WRAPPER__(' + declarationCode.code + ')'); + let codeSeg = exportCode.code.replace(declarationCode.code, '__OBJECT__WRAPPER__(' + declarationCode.code + ', __CML_ERROR__, __enableTypes__, __CHECK__DEFINES__ )'); // 转成ast let replacement = parser.parse(codeSeg, { plugins: parsePlugins, @@ -37,303 +37,6 @@ const handlExport = function (ast) { return ast; }; -/** - * 对象包裹器 - *运行时的错误信息,根据端传入不同的方法, - * @param {Object} obj 需要处理的对象 - * @return {Object} 对象 - */ -/* istanbul ignore next */ -const wrapper = function (obj) { - const className = obj.constructor.name; - /* eslint-disable no-undef */ - const defines = __CHECK__DEFINES__; - const enableTypes = __enableTypes__.split(',') || []; // ['Object','Array','Nullable'] - /* eslint-disable no-undef */ - const types = defines.types; - const interfaceNames = defines.classes[className]; - const methods = {}; - - interfaceNames && interfaceNames.forEach(interfaceName => { - const keys = Object.keys(defines.interfaces); - keys.forEach(key => { - Object.assign(methods, defines.interfaces[key]); - }); - }); - - /** - * 获取类型 - * - * @param {*} value 值 - * @return {string} 类型 - */ - const getType = function (value) { - if (value instanceof Promise) { - return "Promise"; - } - const type = Object.prototype.toString.call(value); - return type.replace(/\[object\s(.*)\]/g, '$1').replace(/( |^)[a-z]/g, (L) => L.toUpperCase()); - }; - - /** - * 校验类型 两个loader共用代码 - * - * @param {*} value 实际传入的值 - * @param {string} type 静态分析时候得到的值得类型 - * @param {array[string]} errList 校验错误信息 类型 - * @return {bool} 校验结果 - */ - - /* eslint complexity:[2,39] */ - const checkType = function(value, originType, errList = []) { - let isNullableReg = /_cml_nullable_lmc_/g; - let type = originType.replace('_cml_nullable_lmc_', ''); - (type === "Void") && (type = "Undefined") - let currentType = getType(value); - let canUseNullable = enableTypes.includes("Nullable"); - let canUseObject = enableTypes.includes("Object"); - if (currentType == 'Null') { - if (type == "Null") {// 如果定义的参数的值就是 Null,那么校验通过 - errList = []; - } else { - // 那么判断是否是可选参数的情况 - (canUseNullable && isNullableReg.test(originType)) ? errList = [] : errList.push(`定义了${type}类型的参数,传入的却是${currentType},请确认是否开启nullable配置`) - } - return errList; - - } - if (currentType == 'Undefined') { // 如果运行时传入的真实值是undefined,那么可能改值在接口处就是被定义为 Undefined类型或者是 ?string 这种可选参数 nullable的情况; - if (type == "Undefined") { - errList = []; - } else { - (canUseNullable && isNullableReg.test(originType)) ? errList = [] : errList.push(`定义了${type}类型的参数,传入的却是${currentType},请确认是否开启nullable配置或者检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'String') { - if (type == 'String') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'Boolean') { - if (type == 'Boolean') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'Number') { - if (type == 'Number') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'Object') { - if (type == 'Object') { - (!canUseObject) ? errList.push(`不能直接定义类型${type},需要使用符合类型定义,请确认是否开启了可以直接定义 Object 类型参数;`) : (errList = []); - } else if (type == 'CMLObject') { - errList = []; - } else { // 这种情况的对象就是自定义的对象; - if (types[type]) { - const keys = Object.keys(types[type]); - // todo 这里是同样的问题,可能多传递 - keys.forEach(key => { - let subError = checkType(value[key], types[type][key], []); - if (subError && subError.length) { - errList = errList.concat(subError) - } - }); - if (Object.keys(value).length > keys.length) { - errList.push(`type [${type}] 参数个数与定义不符`) - } - } else { - errList.push('找不到定义的type [' + type + ']!'); - } - } - return errList; - } - if (currentType == 'Array') { - if (type == 'Array') { - (!canUseObject) ? errList.push(`不能直接定义类型${type},需要使用符合类型定义,请确认是否开启了可以直接定义 Array 类型参数;`) : (errList = []); - } else { - if (types[type]) { - // 数组元素的类型 - let itemType = types[type][0]; - for (let i = 0; i < value.length; i++) { - let subError = checkType(value[i], itemType, []); - if (subError && subError.length) { - errList = errList.concat(subError) - } - } - } else { - errList.push('找不到定义的type [' + type + ']!'); - - } - } - - return errList; - } - if (currentType == 'Function') { - // if (type == 'Function') { - // errList = []; - // } else { - // errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - // } - if (types[type]) { - if (!types[type].input && !types[type].output) { - errList.push(`找不到${types[type]} 函数定义的输入输出`); - } - } else { - errList.push('找不到定义的type [' + type + ']!'); - } - return errList; - } - if (currentType == 'Promise') { - if (type == 'Promise') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'Date') { - if (type == 'Date') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - if (currentType == 'RegExp') { - if (type == 'RegExp') { - errList = []; - } else { - errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) - } - return errList; - } - - - return errList; - } - - /** - * 校验参数类型 - * - * @param {string} methodName 方法名称 - * @param {Array} argNames 参数名称列表 - * @param {Array} argValues 参数值列表 - * @return {bool} 校验结果 - */ - const checkArgsType = function (methodName, argValues) { - let argList; - - if (getType(methodName) == 'Array') { - // 回调函数的校验 methodName[0] 方法的名字 methodName[1]该回调函数在方法的参数索引 - argList = types[methods[methodName[0]].input[methodName[1]]].input; - // 拿到这个回调函数的参数定义 - } else { - argList = methods[methodName].input; - } - // todo 函数可能多传参数 - argList.forEach((argType, index) => { - let errList = checkType(argValues[index], argType, []); - if (errList && errList.length > 0) { - showErrorMessage(` - 校验位置: 方法${methodName}第${index + 1}个参数 - 错误信息: ${errList.join('\n')}`) - } - }); - if (argValues.length > argList.length) { - showErrorMessage(`[${methodName}]方法参数传递个数与定义不符`); - } - }; - - /** - * 校验返回值类型 - * - * @param {string} methodName 方法名称 - * @param {*} returnData 返回值 - * @return {bool} 校验结果 - */ - const checkReturnType = function (methodName, returnData) { - let output; - if (getType(methodName) == 'Array') { - output = types[methods[methodName[0]].input[methodName[1]]].output; - } else { - output = methods[methodName].output; - } - // todo output 为什么可以是数组 - // if (output instanceof Array) { - // output.forEach(type => { - - // //todo 而且是要有一个校验不符合就check失败? 应该是有一个校验通过就可以吧 - // checkType(returnData, type,[]) - // }); - // } - let errList = checkType(returnData, output, []); - if (errList && errList.length > 0) { - showErrorMessage(` - 校验位置: 方法${methodName}返回值 - 错误信息: ${errList.join('\n')}`) - } - }; - - /** - * 创建warpper - * - * @param {string} funcName 方法名称 - * @param {Function} originFunc 原有方法 - * @return {Function} 包裹后的方法 - */ - const createWarpper = function (funcName, originFunc) { - return function () { - const argValues = Array.prototype.slice.call(arguments) - .map(function (arg, index) { - // 对传入的方法要做特殊的处理,这个是传入的callback,对callback函数再做包装 - if (getType(arg) == 'Function') { - return createWarpper([funcName, index], arg); - } - return arg; - }); - - checkArgsType(funcName, argValues); - - - const result = originFunc.apply(this, argValues); - - checkReturnType(funcName, result) - return result; - } - }; - - // 获取所有方法 - const keys = Object.keys(methods); - - // 处理包装方法 - keys.forEach(key => { - const originFunc = obj[key]; - if (!originFunc) { - showErrorMessage('method [' + key + '] not found!'); - return; - } - - if (obj.hasOwnProperty(key)) { - obj[key] = createWarpper(key, originFunc); - } else { - Object.getPrototypeOf(obj)[key] = createWarpper(key, originFunc); - } - }); - - return obj; -}; - /** * 获取处理后的代码 * @@ -376,10 +79,8 @@ const getCode = function (code, options) { const __CML_ERROR__ = ${throwError.toString()} ` } - /* eslint-disable no-inner-declarations */ - wrapperCode = ` - ${wrapper.toString().replace(/showErrorMessage/g, '__CML_ERROR__')}` - + const wrapperPath = path.join(__dirname, '../runtime/checkWrapper.js'); + wrapperCode = `require('${cmlUtils.handleRelativePath(filePath, wrapperPath)}')` result += ` const __enableTypes__ = "${enableTypes}" diff --git a/packages/mvvm-interface-parser/runtime/checkWrapper.js b/packages/mvvm-interface-parser/runtime/checkWrapper.js new file mode 100644 index 0000000000000000000000000000000000000000..7f8dea505f6d8fc833e668ba533b1b43425c4cb6 --- /dev/null +++ b/packages/mvvm-interface-parser/runtime/checkWrapper.js @@ -0,0 +1,296 @@ +/** +* 对象包裹器 +*运行时的错误信息,根据端传入不同的方法, +* @param {Object} obj 需要处理的对象 +* @return {Object} 对象 +*/ +/* istanbul ignore next */ +module.exports = function (obj, __CML_ERROR__, __enableTypes__, __CHECK__DEFINES__) { + const className = obj.constructor.name; + /* eslint-disable no-undef */ + const defines = __CHECK__DEFINES__; + const enableTypes = __enableTypes__.split(',') || []; // ['Object','Array','Nullable'] + /* eslint-disable no-undef */ + const types = defines.types; + const interfaceNames = defines.classes[className]; + const methods = {}; + + interfaceNames && interfaceNames.forEach(interfaceName => { + const keys = Object.keys(defines.interfaces); + keys.forEach(key => { + Object.assign(methods, defines.interfaces[key]); + }); + }); + + /** + * 获取类型 + * + * @param {*} value 值 + * @return {string} 类型 + */ + const getType = function (value) { + if (value instanceof Promise) { + return "Promise"; + } + const type = Object.prototype.toString.call(value); + return type.replace(/\[object\s(.*)\]/g, '$1').replace(/( |^)[a-z]/g, (L) => L.toUpperCase()); + }; + + /** + * 校验类型 两个loader共用代码 + * + * @param {*} value 实际传入的值 + * @param {string} type 静态分析时候得到的值得类型 + * @param {array[string]} errList 校验错误信息 类型 + * @return {bool} 校验结果 + */ + + /* eslint complexity:[2,39] */ + const checkType = function(value, originType, errList = []) { + let isNullableReg = /_cml_nullable_lmc_/g; + let type = originType.replace('_cml_nullable_lmc_', ''); + (type === "Void") && (type = "Undefined") + let currentType = getType(value); + let canUseNullable = enableTypes.includes("Nullable"); + let canUseObject = enableTypes.includes("Object"); + if (currentType == 'Null') { + if (type == "Null") {// 如果定义的参数的值就是 Null,那么校验通过 + errList = []; + } else { + // 那么判断是否是可选参数的情况 + (canUseNullable && isNullableReg.test(originType)) ? errList = [] : errList.push(`定义了${type}类型的参数,传入的却是${currentType},请确认是否开启nullable配置`) + } + return errList; + + } + if (currentType == 'Undefined') { // 如果运行时传入的真实值是undefined,那么可能改值在接口处就是被定义为 Undefined类型或者是 ?string 这种可选参数 nullable的情况; + if (type == "Undefined") { + errList = []; + } else { + (canUseNullable && isNullableReg.test(originType)) ? errList = [] : errList.push(`定义了${type}类型的参数,传入的却是${currentType},请确认是否开启nullable配置或者检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'String') { + if (type == 'String') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'Boolean') { + if (type == 'Boolean') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'Number') { + if (type == 'Number') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'Object') { + if (type == 'Object') { + (!canUseObject) ? errList.push(`不能直接定义类型${type},需要使用符合类型定义,请确认是否开启了可以直接定义 Object 类型参数;`) : (errList = []); + } else if (type == 'CMLObject') { + errList = []; + } else { // 这种情况的对象就是自定义的对象; + if (types[type]) { + const keys = Object.keys(types[type]); + // todo 这里是同样的问题,可能多传递 + keys.forEach(key => { + let subError = checkType(value[key], types[type][key], []); + if (subError && subError.length) { + errList = errList.concat(subError) + } + }); + if (Object.keys(value).length > keys.length) { + errList.push(`type [${type}] 参数个数与定义不符`) + } + } else { + errList.push('找不到定义的type [' + type + ']!'); + } + } + return errList; + } + if (currentType == 'Array') { + if (type == 'Array') { + (!canUseObject) ? errList.push(`不能直接定义类型${type},需要使用符合类型定义,请确认是否开启了可以直接定义 Array 类型参数;`) : (errList = []); + } else { + if (types[type]) { + // 数组元素的类型 + let itemType = types[type][0]; + for (let i = 0; i < value.length; i++) { + let subError = checkType(value[i], itemType, []); + if (subError && subError.length) { + errList = errList.concat(subError) + } + } + } else { + errList.push('找不到定义的type [' + type + ']!'); + + } + } + + return errList; + } + if (currentType == 'Function') { + // if (type == 'Function') { + // errList = []; + // } else { + // errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + // } + if (types[type]) { + if (!types[type].input && !types[type].output) { + errList.push(`找不到${types[type]} 函数定义的输入输出`); + } + } else { + errList.push('找不到定义的type [' + type + ']!'); + } + return errList; + } + if (currentType == 'Promise') { + if (type == 'Promise') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'Date') { + if (type == 'Date') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + if (currentType == 'RegExp') { + if (type == 'RegExp') { + errList = []; + } else { + errList.push(`定义了${type}类型的参数,传入的却是${currentType},请检查所传参数是否和接口定义的一致`) + } + return errList; + } + + + return errList; + } + + /** + * 校验参数类型 + * + * @param {string} methodName 方法名称 + * @param {Array} argNames 参数名称列表 + * @param {Array} argValues 参数值列表 + * @return {bool} 校验结果 + */ + const checkArgsType = function (methodName, argValues) { + let argList; + + if (getType(methodName) == 'Array') { + // 回调函数的校验 methodName[0] 方法的名字 methodName[1]该回调函数在方法的参数索引 + argList = types[methods[methodName[0]].input[methodName[1]]].input; + // 拿到这个回调函数的参数定义 + } else { + argList = methods[methodName].input; + } + // todo 函数可能多传参数 + argList.forEach((argType, index) => { + let errList = checkType(argValues[index], argType, []); + if (errList && errList.length > 0) { + __CML_ERROR__(` + 校验位置: 方法${methodName}第${index + 1}个参数 + 错误信息: ${errList.join('\n')}`) + } + }); + if (argValues.length > argList.length) { + __CML_ERROR__(`[${methodName}]方法参数传递个数与定义不符`); + } + }; + + /** + * 校验返回值类型 + * + * @param {string} methodName 方法名称 + * @param {*} returnData 返回值 + * @return {bool} 校验结果 + */ + const checkReturnType = function (methodName, returnData) { + let output; + if (getType(methodName) == 'Array') { + output = types[methods[methodName[0]].input[methodName[1]]].output; + } else { + output = methods[methodName].output; + } + // todo output 为什么可以是数组 + // if (output instanceof Array) { + // output.forEach(type => { + + // //todo 而且是要有一个校验不符合就check失败? 应该是有一个校验通过就可以吧 + // checkType(returnData, type,[]) + // }); + // } + let errList = checkType(returnData, output, []); + if (errList && errList.length > 0) { + __CML_ERROR__(` + 校验位置: 方法${methodName}返回值 + 错误信息: ${errList.join('\n')}`) + } + }; + + /** + * 创建warpper + * + * @param {string} funcName 方法名称 + * @param {Function} originFunc 原有方法 + * @return {Function} 包裹后的方法 + */ + const createWarpper = function (funcName, originFunc) { + return function () { + const argValues = Array.prototype.slice.call(arguments) + .map(function (arg, index) { + // 对传入的方法要做特殊的处理,这个是传入的callback,对callback函数再做包装 + if (getType(arg) == 'Function') { + return createWarpper([funcName, index], arg); + } + return arg; + }); + + checkArgsType(funcName, argValues); + + + const result = originFunc.apply(this, argValues); + + checkReturnType(funcName, result) + return result; + } + }; + + // 获取所有方法 + const keys = Object.keys(methods); + + // 处理包装方法 + keys.forEach(key => { + const originFunc = obj[key]; + if (!originFunc) { + __CML_ERROR__('method [' + key + '] not found!'); + return; + } + + if (obj.hasOwnProperty(key)) { + obj[key] = createWarpper(key, originFunc); + } else { + Object.getPrototypeOf(obj)[key] = createWarpper(key, originFunc); + } + }); + + return obj; +}; diff --git a/packages/mvvm-interface-parser/test/checkWrapper.test.js b/packages/mvvm-interface-parser/test/checkWrapper.test.js new file mode 100644 index 0000000000000000000000000000000000000000..621945e3678eede0313f4c62247798366de45f6f --- /dev/null +++ b/packages/mvvm-interface-parser/test/checkWrapper.test.js @@ -0,0 +1,73 @@ + + +const _ = require('../runtime/checkWrapper.js'); +const expect = require('chai').expect; + + +var __INTERFACE__FILEPATH = "node_modules/chameleon-api/src/interfaces/showToast/index.interface"; +var __CML_ERROR__ = function throwError(content) { + throw new Error("\u6587\u4EF6\u4F4D\u7F6E: " + __INTERFACE__FILEPATH + "\n " + content); +}; + +var __enableTypes__ = ""; +var __CHECK__DEFINES__ = { + "types": { + "toastOpt": { + "message": "String", + "duration": "Number", + "date": "Null" + } + }, + "interfaces": { + "uiInterface": { + "showToast": { + "input": ["toastOpt"], + "output": "Undefined" + } + } + }, + "classes": { + "Method": ["uiInterface"] + } +}; + +function Method() { +} +Method.prototype.showToast = function({message, duration}) { +} + +var obj = _(new Method(), __CML_ERROR__, __enableTypes__, __CHECK__DEFINES__); + + +describe('mvvm-interface-parser/checkWrapper', function() { + it('定义了String类型的参数,传入的却是Number', function() { + obj.showToast({ + message: '22', + duration: 123, + "date": null + }) + try { + obj.showToast({ + message: 23, + duration: 123, + "date": null + }) + } catch (e) { + expect(!!~e.message.indexOf('错误信息: 定义了String类型的参数,传入的却是Number')).to.equal(true) + } + }) + + it('定义了String类型的参数,传入的却是Null', function() { + try { + obj.showToast({ + message: null, + duration: 123, + "date": null + }) + } catch (e) { + expect(!!~e.message.indexOf('定义了String类型的参数,传入的却是Null')).to.equal(true) + } + }) + +}) + diff --git a/packages/mvvm-pack/cmlNode.js b/packages/mvvm-pack/cmlNode.js index b635f77d7fec90c66f0d1ae95fb68a536c10c482..1a1edb0b2cec37dbc5c886185ae23dd93f48915f 100644 --- a/packages/mvvm-pack/cmlNode.js +++ b/packages/mvvm-pack/cmlNode.js @@ -7,7 +7,8 @@ class CMLNode { this.dependencies = []; // 该节点的直接依赖 app.cml依赖pages.cml pages.cml依赖components.cml js依赖js this.childrens = []; // 子模块 cml文件才有子模块 this.parent; // 父模块 cml文件中的子模块才有 - this.source; // 模块源代码 + this.originSource; // 模块源代码 + this.source; // 模块标准编译后代码 this.convert; // 源代码的格式化形式 this.output; // 模块输出 各种过程操作该字段 this.identifier; // 节点唯一标识 diff --git a/packages/mvvm-pack/compiler.js b/packages/mvvm-pack/compiler.js index f4118b2db834d79019ce62e570f402c6b892657c..8c952929c1d818557b8daf31311fe37c2d380d11 100644 --- a/packages/mvvm-pack/compiler.js +++ b/packages/mvvm-pack/compiler.js @@ -189,6 +189,10 @@ class Compiler { options.source = module._source && module._source._value; } + if (module._cmlOriginSource !== undefined) { + options.originSource = module._cmlOriginSource; + } + if (options.moduleType === 'template') { options.convert = cmlparse(options.source); options.extra = { diff --git a/packages/mvvm-pack/lib/amdbootstrap.module.js b/packages/mvvm-pack/lib/amdbootstrap.module.js index 2feab89d80dcdb057b9bfecc8f2a6f46aaedf821..77142122c2ccd9a1076222b4cb0d63b8686ad217 100644 --- a/packages/mvvm-pack/lib/amdbootstrap.module.js +++ b/packages/mvvm-pack/lib/amdbootstrap.module.js @@ -31,7 +31,7 @@ var ret = (typeof factory == 'function') ? factory.apply(mod, [cmlrequire, mod.exports, mod]) : factory; - + debugger if (ret) { mod.exports = ret; } diff --git a/packages/mvvm-pack/package.json b/packages/mvvm-pack/package.json index b3d86f9af89dfce2ceb5d1a34397b3cde276180e..c344d0842e08985d02408740db23086a06a8d0db 100644 --- a/packages/mvvm-pack/package.json +++ b/packages/mvvm-pack/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive", - "test": "mocha --recursive --reporter spec" + "test": "mocha --recursive --reporter spec", + "test-brk": "node --inspect-brk node_modules/mocha/bin/mocha --recursive --reporter spec" }, "author": "Chameleon-Team", "license": "Apache", diff --git a/packages/mvvm-pack/test/cmlNode.test.js b/packages/mvvm-pack/test/cmlNode.test.js new file mode 100644 index 0000000000000000000000000000000000000000..557bcc4e1d0214d5107f6920a2cf1d8efc03720a --- /dev/null +++ b/packages/mvvm-pack/test/cmlNode.test.js @@ -0,0 +1,27 @@ + +var CMLNode = require('../cmlNode.js') + +const expect = require('chai').expect; + +describe('cmlNode', function() { + it('constructor', function() { + var cmlNode = new CMLNode({ + ext: '.cml', + realPath : 'realPath', // 文件物理地址 + nodeType : 'nodeType', // app/page/component/module // 节点类型 app/page/component 其他的为module cml文件中的每一个部分也是一个Node节点 + moduleType: 'moduleType',// template/style/script/json/asset + dependencies: ['dependencies'], // 该节点的直接依赖 app.cml依赖pages.cml pages.cml依赖components.cml js依赖js + childrens : ['dependencies'], // 子模块 cml文件才有子模块 + parent: 'parent', // 父模块 cml文件中的子模块才有 + source: 'source', // 模块源代码 + convert: 'convert', // 源代码的格式化形式 + output: 'output', // 模块输出 各种过程操作该字段 + identifier: 'identifier', // 节点唯一标识 + modId: 'modId', // 模块化的id requirejs + extra: 'extra' // 节点的额外信息 + }) + expect(cmlNode.ext).to.be.equal('.cml') + expect(cmlNode.realPath).to.be.equal('realPath') + }) + +}) diff --git a/packages/mvvm-pack/test/lib/amd.test.js b/packages/mvvm-pack/test/lib/amd.test.js new file mode 100644 index 0000000000000000000000000000000000000000..1ccf459e0a4b5d0f74bc98e9fa919c1cb7f78089 --- /dev/null +++ b/packages/mvvm-pack/test/lib/amd.test.js @@ -0,0 +1,32 @@ + +let _ = require('../../lib/amd.js'); +const fs = require('fs'); +const path = require('path'); +const expect = require('chai').expect; + +describe('amd', function() { + it('amdWrapModule not has cmldefine', function() { + let code = 'sdfsdfsdfsdfd' + let result = _.amdWrapModule({content: code, modId: '123'}) + expect(result).to.be.equal('\ncmldefine(\'123\', function(require, exports, module) {\n sdfsdfsdfsdfd\n})') + }) + it('amdWrapModule has cmldefine', function() { + let code = '\ncmldefine(\'123\', function(require, exports, module) {\n sdfsdfsdfsdfd\n})'; + let result = _.amdWrapModule({content: code, modId: '123'}) + expect(result).to.be.equal(code) + }) + + + it('getModuleBootstrap', function() { + let amdCode = fs.readFileSync(path.join(__dirname, '../../lib/amdbootstrap.module.js'), {encoding: 'utf8'}) + + let result = _.getModuleBootstrap() + expect(result).to.be.equal(amdCode) + }) + + it('getGlobalBootstrap', function() { + let result = _.getGlobalBootstrap('global') + expect(!!~result.indexOf('(global)')).to.be.equal(true) + }) +}) + diff --git a/packages/mvvm-pack/test/lib/amdbootstrap.global.test.js b/packages/mvvm-pack/test/lib/amdbootstrap.global.test.js new file mode 100644 index 0000000000000000000000000000000000000000..921252aa9a6d9bbfdbb255cd5720f2a0e19f8a61 --- /dev/null +++ b/packages/mvvm-pack/test/lib/amdbootstrap.global.test.js @@ -0,0 +1,21 @@ +let fs = require('fs'); +let path = require('path'); +let code = fs.readFileSync(path.join(__dirname, '../../lib/amdbootstrap.global.js'), {encoding: 'utf8'}) +const expect = require('chai').expect; + +describe('amdbootstrap.global.js', function() { + it('global cmldefine', function() { + code = ` + ${code.replace('$GLOBAL', 'global')} + ` + eval(code) + global.cmldefine('name', function(require, exports, module) { + global.unittest = '123'; + }) + global.cmlrequire('name'); + + expect(global.unittest).to.be.equal('123'); + + }) +}) + diff --git a/packages/mvvm-pack/test/lib/amdbootstrap.module.test.js b/packages/mvvm-pack/test/lib/amdbootstrap.module.test.js new file mode 100644 index 0000000000000000000000000000000000000000..c6ea294293a556232ca40c3dd2b0d9df68aa43c9 --- /dev/null +++ b/packages/mvvm-pack/test/lib/amdbootstrap.module.test.js @@ -0,0 +1,39 @@ + +let amd = require('../../lib/amdbootstrap.module.js') +const expect = require('chai').expect; + +describe('amdbootstrap.moudle.js', function() { + it('module cmldefine', function() { + + amd.cmldefine('name', function(require, exports, module) { + global.unittest = '123'; + }) + amd.cmlrequire('name'); + amd.cmlrequire('name'); + + expect(global.unittest).to.be.equal('123'); + + }) + + it('module not find', function() { + + amd.cmldefine('name', function(require, exports, module) { + global.unittest = '123'; + }) + try { + amd.cmlrequire('name2'); + } catch (e) { + expect(!!~e.message.indexOf('[ModJS] Cannot find module')).to.be.equal(true); + } + }) + + it('module has return', function() { + + amd.cmldefine('name3', function(require, exports, module) { + return 'has return'; + }) + var value = amd.cmlrequire('name3'); + expect(!!~value.indexOf('has return')).to.be.equal(true); + }) +}) + diff --git a/packages/mvvm-pack/test/amdwrapper.test.js b/packages/mvvm-pack/test/lib/amdwrapper.test.js similarity index 92% rename from packages/mvvm-pack/test/amdwrapper.test.js rename to packages/mvvm-pack/test/lib/amdwrapper.test.js index 23c97740170cc22f3d835e993bc4e92b56a0b1c5..879d18d1e42d6595395977b7e48b8c343eb3a6a2 100644 --- a/packages/mvvm-pack/test/amdwrapper.test.js +++ b/packages/mvvm-pack/test/lib/amdwrapper.test.js @@ -1,4 +1,4 @@ -let wrapper = require('../lib/amdwrapper'); +let wrapper = require('../../lib/amdwrapper'); const expect = require('chai').expect; describe('amdwrapper', function() { diff --git a/packages/mvvm-pack/test/lib/replaceJsModId.test.js b/packages/mvvm-pack/test/lib/replaceJsModId.test.js new file mode 100644 index 0000000000000000000000000000000000000000..521ab34bf95b87d8264471b00193137c7afe0c4c --- /dev/null +++ b/packages/mvvm-pack/test/lib/replaceJsModId.test.js @@ -0,0 +1,75 @@ + +let _ = require('../../lib/replaceJsModId.js'); +const expect = require('chai').expect; + +describe('replaceJsModId.js', function() { + it('replaceJsModId', function() { + let code = ` + import a from '../a.js'; + var b = require('../b.js'); + require('../c.js'); + ` + var target = { + dependencies: [ + { + request: '../a.js', + module: { + request: '../a.js', + id: 'a' + } + }, + { + request: '../b.js', + module: { + request: '../b.js', + id: 'b' + } + }, + { + request: '../c.js', + module: { + request: '../c.js', + id: 'c' + } + } + ] + } + let result = _.replaceJsModId(code, target); + console.log(result) + expect(!!~result.indexOf('var b = require("b")')).to.be.equal(true); + expect(!!~result.indexOf('import a from "a";')).to.be.equal(true); + expect(!!~result.indexOf('require("c");')).to.be.equal(true); + }) + + it('no modId', function() { + let code = ` + import a from '../a.js'; + var b = require('../b.js'); + require('../c.js'); + ` + var target = { + dependencies: [ + { + request: '../a.js', + module: { + request: '../a.js', + id: 'a' + } + }, + { + request: '../b.js', + module: { + request: '../b.js', + id: 'b' + } + } + ] + } + try { + _.replaceJsModId(code, target); + } + catch (e) { + } + }) +}) + diff --git a/packages/mvvm-template-parser/test/index.test.js b/packages/mvvm-template-parser/test/index.test.js new file mode 100644 index 0000000000000000000000000000000000000000..9f5365fa63b2127c1d3d2995e378a72d8517a592 --- /dev/null +++ b/packages/mvvm-template-parser/test/index.test.js @@ -0,0 +1,37 @@ +var _ = require('../index.js'); +var expect = require('chai').expect; + +describe('mvvm-template-parser', function() { + it('cmlparse', function() { + let content = ` + + + + + + ` + let result = _.cmlparse(content); + expect(typeof result).to.be.equal('object') + }); + + it('postParseUnicode', function() { + let content = `\u4f60\u597d`; + let result = _.postParseUnicode(content); + expect(result).to.be.equal('你好') + }); + + it('generator', function() { + + let content = ` + + + + + + ` + let result = _.cmlparse(content); + let code = _.generator(result).code; + console.log(code) + expect(code).to.be.equal('\n \n \n \n ') + }); +}) \ No newline at end of file