提交 126916b3 编写于 作者: Y yylgit

component export

上级 37b1a2db
......@@ -1040,55 +1040,40 @@ function getPluginKey(type, key) {
// 获取export模式的入口cml文件
_.getExportEntry = function (cmlType, context, entry = []) {
let exportFiles = [];
function addExport(filePath) {
if (_.isFile(filePath) && !~exportFiles.indexOf(filePath)) {
exportFiles.push(filePath);
}
}
if (entry && entry.length > 0) {
entry.forEach(item => {
let filePath = path.join(context, item);
// cml文件插入
if (_.isFile(filePath)) {
exportFiles.push(filePath);
if (path.extname(filePath) === '.cml') {
exportFiles.push(filePath);
} else if (path.extname(filePath) === '.interface') {
let content = fs.readFileSync(filePath, {encoding: 'utf-8'});
let cmlFilePath = _.findPolymorphicComponent(filePath, content, cmlType);
addExport(cmlFilePath);
}
} else if (_.isDirectory(filePath)) {
let cmlFilePath = path.join(filePath, '**/*.cml');
let interfaceFilePath = path.join(filePath, '**/*.interface');
// 需要忽略掉的组件
// let ignoreComponents = ['web', 'weex', 'wx', 'alipay', 'baidu'];
// if (~ignoreComponents.indexOf(cmlType)) {
// ignoreComponents.splice(ignoreComponents.indexOf(cmlType), 1);
// }
// ignoreComponents = ignoreComponents.map(item => `\\.${item}\\.cml`);
// let ignoreReg = new RegExp(`(${ignoreComponents.join('|')})`);
// glob.sync(filePath).forEach(cmlPath => {
// // 其他端的多态cml组件排除在外
// if (!ignoreReg.test(cmlPath)) {
// exportFiles.push(cmlPath);
// }
// })
// 多态组件有了include语法之后不能采用上面的方法
// 1 先找interface指定的多态组件
// 2 再找cml文件 不能根据文件名称区分端,如果所有cml文件 也会把其他端多态组件引入,所以取只有一个逗号的cml文件
// todo
// 2 再找cml文件 不能根据文件名称区分端,如果所有cml文件 也会把其他端多态组件引入,所以取只有一个逗号的cml文件为非多态组件
// 获取重复添加入口时有校验
glob.sync(interfaceFilePath).forEach(interfacePath => {
// 其他端的多态cml组件排除在外
let content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
let cmlFilePath = _.findPolymorphicComponent(interfacePath, content, cmlType);
if (_.isFile(cmlFilePath)) {
// 组件的名称是interface文件的名称
if (!~exportFiles.indexOf(cmlFilePath)) {
exportFiles.push(cmlFilePath);
}
}
addExport(cmlFilePath);
})
glob.sync(cmlFilePath).forEach(item => {
if (_.isFile(item)) {
let basename = path.basename(item);
if (basename.split('.').length === 2) {
// 组件的名称是interface文件的名称
if (!~exportFiles.indexOf(cmlFilePath)) {
exportFiles.push(cmlFilePath);
}
}
let basename = path.basename(item);
if (basename.split('.').length === 2) {
addExport(item);
}
})
}
......
......@@ -378,7 +378,6 @@ describe('index.js', function () {
var cmlFilePath = path.join(__dirname, 'testlib/demo-project/src/pages/page1/page1.cml');
var comrefPath = 'vant-weapp/test'
debugger
let result = _.handleComponentUrl(cml.projectRoot, cmlFilePath, comrefPath, 'wx');
expect(result.refUrl).to.equal('./../../npm/vant-weapp/test');
})
......@@ -685,4 +684,20 @@ describe('index.js', function () {
expect(result3).to.be.equal('name');
})
it(`getExportEntry`, function () {
global.cml = {};
_.setCli(true);
global.cml.event = new EventEmitter();
global.cml.utils = _;
global.projectRoot = path.join(__dirname, 'testlib/demo-project');
let result = _.getExportEntry('web',global.projectRoot, [
'src/components',
'src/notfinr.cml',
'src/components/com2/com2.interface'
]);
console.log(result)
expect(result.length).to.be.equal(2);
})
})
<script cml-type="web" src="./com2.web.cml"></script>
<script cml-type="wx" src="./com2.wx.cml"></script>
\ No newline at end of file
......@@ -10,11 +10,12 @@
"dependencies": {
"babel-generator": "6.26.1",
"babel-traverse": "6.26.0",
"babel-types": "6.26.0"
"babel-types": "6.26.0",
"chai": "^4.2.0"
},
"scripts": {
"test": "mocha"
},
"author": "",
"license": "ISC"
}
\ No newline at end of file
}
此差异已折叠。
# Editor directories and files
.DS_Store
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
.vscode
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# ///////////////////////////
#### mvvm协议标准中处理cml文件script部分与js文件
\ No newline at end of file
/** 编译源码
* 分析依赖
* */
'use strict';
const {parsePlugins} = require('runtime-check');
const parser = require('@babel/parser');
const babel = require('@babel/core');
const traverse = require('@babel/traverse');
const t = require('@babel/types');
const cmlUtils = require('chameleon-tool-utils');
const {getCheckCode} = require('./lib/check');
const interfaceParser = require('mvvm-interface-parser');
const getInterfaceCode = require('mvvm-interface-parser/lib/getInterfaceCode.js');
const generator = require("@babel/generator");
const path = require('path');
const defaultResolve = function(filePath, relativePath) {
return path.resolve(path.dirname(filePath), relativePath)
}
// 标准的script部分处理
exports.standardParser = function({cmlType, media, source, filePath, check, resolve = defaultResolve }) {
let devDeps = [];
let reg = new RegExp(`\\.${cmlType}\\.cml$`);
// 多态组件 获取interface文件并拼接
if (reg.test(filePath)) {
let interfacePath = cmlUtils.RecordCml2Interface[filePath];
if (!interfacePath) {
throw new Error(`not find interface for ${filePath}`)
}
let {content: interfaceCode, devDeps: interfaceDevDeps, contentFilePath} = getInterfaceCode({interfacePath});
devDeps = interfaceDevDeps;
if (media === 'dev' && check.enable === true) {
try {
source = getCheckCode(interfaceCode, source, contentFilePath, filePath, cmlType, check.enableTypes);
} catch (e) {
// 当有语法错误 babel parse会报错,报错信息不友好
cmlUtils.log.error(`mvvm-interface-parser: ${filePath} or ${contentFilePath} syntax error!`)
}
}
}
// .interface文件
else if (/\.interface$/.test(filePath)) {
let interfaceResult = interfaceParser({cmlType, media, source, filePath, check, resolve });
source = interfaceResult.result;
devDeps = interfaceResult.devDeps;
}
return {source, devDeps};
}
// 源代码的ast
exports.JSCompile = function({source, filePath, compiler}) {
const ast = exports.getAST({source});
const dependencies = exports.getDependenciesAndReplace({ast, filePath, compiler});
const output = generator["default"](ast);
return {
ast,
dependencies,
output
};
}
// 获取ast
exports.getAST = function({source}) {
const ast = parser.parse(source, {
sourceType: 'module',
plugins: parsePlugins
});
return ast;
}
// 获取dependencies
exports.getDependenciesAndReplace = function({ast, filePath, compiler}) {
let dependencies = [];
traverse["default"](ast, {
enter: (path) => {
let node = path.node;
if (t.isImportDeclaration(node) && node.source.value) {
let {realPath, modId} = getJSModId(node.source.value);
node.source.value = modId;
node.source.raw = `'${modId}'`;
dependencies.push(realPath);
}
if (t.isVariableDeclaration(node)) {
node.declarations.forEach(item => {
if (item && item.init && item.init.callee && item.init.callee.name === 'require' && item.init.arguments && item.init.arguments[0] && item.init.arguments[0].value) {
let {realPath, modId} = getJSModId(item.init.arguments[0].value);
item.init.arguments[0].value = modId;
item.init.arguments[0].raw = `'${modId}'`;
dependencies.push(realPath);
}
})
}
if (t.isExpressionStatement(node) && node.expression && node.expression.callee && node.expression.callee.name === 'require' && node.expression.arguments && node.expression.arguments[0]) {
let {realPath, modId} = getJSModId(node.expression.arguments[0].value);
node.expression.arguments[0].value = modId;
node.expression.arguments[0].raw = `'${modId}'`;
dependencies.push(realPath);
}
}
})
function getJSModId(dependPath) {
if (compiler) {
let realDependPath = compiler.resolve(filePath, dependPath);
return {
realPath: realDependPath,
modId: compiler.createModId(realDependPath)
};
} else {
return {
realPath: dependPath,
modId: dependPath
}
}
}
return dependencies;
}
// 获取dependencies
exports.getDependencies = function({ast}) {
let dependencies = [];
traverse["default"](ast, {
enter: (path) => {
let node = path.node;
if (t.isImportDeclaration(node) && node.source.value) {
dependencies.push(node.source.value);
}
if (t.isVariableDeclaration(node)) {
node.declarations.forEach(item => {
if (item && item.init && item.init.callee && item.init.callee.name === 'require' && item.init.arguments && item.init.arguments[0] && item.init.arguments[0].value) {
dependencies.push(item.init.arguments[0].value);
}
})
}
if (t.isExpressionStatement(node) && node.expression && node.expression.callee && node.expression.callee.name === 'require' && node.expression.arguments && node.expression.arguments[0]) {
dependencies.push(node.expression.arguments[0].value);
}
}
})
return dependencies;
}
// 提供标准的jsbabel方法
exports.standardBabel = function({source, options}) {
// options 这里需要兼容
options = options || exports.standardBabelOptions;
let output = babel.transformSync(source, options); // => { code, map, ast }
return output;
}
exports.standardBabelOptions = {
"presets": [
"flow",
[
"env",
{
"targets": {
"browsers": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
}
],
"stage-0"
],
"plugins": [
"transform-remove-strict-mode",
["transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}],
["babel-plugin-chameleon-import", {
"libraryName": "chameleon-api",
"libraryDirectory": "src/interfaces",
"libraryFileName": "index.js",
"defaulLibraryDirectory": "",
"defaulLibraryFileName": "index.js"
}]
]
}
/* eslint-disable */
/**
* cml多态组件 interface校验
* 1 拿到interface的校验
* 2 写好处理default对象的方法
* 3 拼接代码
*/
const path = require('path');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require("@babel/generator");
const {getDefines, parsePlugins} = require('runtime-check');
const cmlUtils = require('chameleon-tool-utils');
/**
* 处理对象的函数
* @param {*} obj
*/
function wrapper(obj) {
const className = obj.constructor.name;
const interfaceDefines = __INTERFAE__DEFINES__;
const enableTypes = __enableTypes__; // ['Object','Array','Nullable']
const types = interfaceDefines.types;
const interfaceKey = Object.keys(interfaceDefines.interfaces)[0]; // interface Name
const interfaceObj = interfaceDefines.interfaces[interfaceKey];
const cmlDefines = __CML__DEFINES__;
let isImplementInterface = false;
// 找到class
if (cmlDefines.classes[className]) {
// class 的interface数组中有interface中的定义
if (~cmlDefines.classes[className].indexOf(interfaceKey)) {
isImplementInterface = true;
} else {
console.error(`class ${className} not implements interface ${interfaceKey}`);
}
}
let props = [];
let events = {};
Object.keys(interfaceObj).forEach(key => {
let item = interfaceObj[key];
if (is(item, 'Object')) {
// 是事件 有output和input
events[key] = item;
} else {
// 是属性
props.push({
key,
value: item
})
}
})
// created 时做props校验 同时建立watch属性检测props类型
// 包装this.$cmlEmit 校验自定义事件参数类型
function isFunc(target) {
return target && is(target, 'Function')
}
function is(source, type) {
return Object.prototype.toString.call(source) === '[object ' + type + ']';
}
const getType = function (value) {
const type = Object.prototype.toString.call(value);
return type.replace(/\[object\s(.*)\]/g, '$1').replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
};
// beforeCreate时 vue中还获取不到mixins的this.$cmlEmit方法
let oldCreated = obj.created || function() {};
obj.created = function(...args) {
checkProps.call(this);
oldCreated.call(this);
}
obj.methods = obj.methods || {};
obj.methods.$__checkCmlEmit__ = function(eventName, eventDetail) {
if (events[eventName]) {
let {input} = events[eventName];
let detailType = input[0];
let errList = checkType(eventDetail, detailType, []);
if (errList && errList.length) {
showErrorMessage(`errorinfo: event ${eventName} detail verification fails
${errList.join('\n')}
`)
}
} else {
showErrorMessage(`errorinfo: event ${eventName} is not defined in interface
${errList.join('\n')}
`)
}
}
function checkProps() {
props.forEach(item => {
let errList = checkType(this[item.key], item.value, []);
if (errList && errList.length) {
showErrorMessage(`error: prop [${item.key}] verification fails
${errList.join('\n')}
`)
}
})
}
obj.watch = obj.watch || {};
props.forEach(item => {
let oldWatch = obj.watch[item.key];
obj.watch[item.key] = function (newVal, oldVal) {
let errList = checkType(newVal, item.value, []);
if (errList && errList.length) {
showErrorMessage(`errorinfo: prop [${item.key}] verification fails
${errList.join('\n')}
`)
}
if (isFunc(oldWatch)) {
oldWatch.call(this, newVal, oldVal);
}
}
})
/**
* 校验类型 两个loader共用代码
*
* @param {*} value 实际传入的值
* @param {string} type 静态分析时候得到的值得类型
* @param {array[string]} errList 校验错误信息 类型
* @return {bool} 校验结果
*/
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);// Undefined Null Object Array Number String Function只可能是这几种类型;
// 但是对于type的值则可能是 Undefined Null Number String NullUndefinedStiring
// Object Array Function EventDetail(...这种自定义的复杂数据类型) 这几种;
// 判断nullable类型的参数
// 如果 currentType === type 那么就会直接返回 [];
let canUseNullable = enableTypes.includes("Nullable");
let canUseObject = enableTypes.includes("Object");
let canUseArray = enableTypes.includes("Array");
if (currentType == 'Null') { // 如果传入的值是 null类型,那么可能的情况是该值在接口处的被定义为Null或者是 ?string 这种可选参数的形式;
if (type == "Null") {// 如果定义的参数的值就是 Null,那么校验通过
errList = [];
} else { // 实际定义的参数的值不是 Null ?string这种形式的定义,type = new String('String') ?Callback type = new String('Callback')
// 那么判断是否是可选参数的情况
(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 (types[type]) {
if (!types[type].input && !types[type].output) {
errList.push(`找不到${types[type]} 函数定义的输入输出`);
}
} else {
errList.push('找不到定义的type [' + type + ']!');
}
}
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;
}
return obj;
}
/**
* 处理ast导出表达式
*
* @param {Object} ast ast
* @return {Object} ast
*/
const handlExport = function (ast) {
traverse["default"](ast, {
enter(path) {
if (path.node.type === 'ExportDefaultDeclaration') {
// 拿到export ddefault new Method(); 这一行代码
let exportCode = generate["default"](path.node);
// 拿到 new Method(); 这一段代码
let declarationCode = generate["default"](path.node.declaration);
// 得到 export default __OBJECT__WARPPER__(new Method());
let codeSeg = exportCode.code.replace(declarationCode.code, '__CML__WRAPPER__(' + declarationCode.code + ')');
// 转成ast
let replacement = parser.parse(codeSeg, {
plugins: parsePlugins,
sourceType: 'module'
});
traverse["default"].removeProperties(replacement);
// 替换
path.replaceWith(replacement.program.body[0]);
path.stop();
}
}
});
return ast;
};
/**
*
* @param {*} interfaceCode interface的代码
* @param {*} cmlCode cml的代码
* @param {*} interfacePath interface的文件路径
* @param {*} cmlPath cml的文件路径
* @param {*} cmlType web weex
* @param {*} enableTypes 启动的类型
*/
function getCheckCode(interfaceCode, cmlCode, interfacePath, cmlPath, cmlType, enableTypes) {
let interfaceDefines = getDefines(interfaceCode, interfacePath).defines;
let interfaceNumber = Object.keys(interfaceDefines.interfaces).length;
if (interfaceNumber === 0) {
throw new Error(`${interfacePath}中未定义interface`)
} else if (interfaceNumber > 1) {
throw new Error(`${interfacePath}中只能定义一个interface`)
}
// 为了拿到expot 对象的class implements 的interface
let cmlDefines = getDefines(cmlCode, cmlPath);
const newCode = generate["default"](handlExport(cmlDefines.ast)).code;
let result = '';
let wrapperCode = '';
if (interfacePath) {
interfacePath = path.resolve(interfacePath);
interfacePath = cmlUtils.handleWinPath(interfacePath);
result += `const __INTERFACE__FILEPATH="${interfacePath}"`;
}
if (cmlType === 'weex') {
function throwError(content) {
var modal = weex.requireModule('modal')
modal.alert({
message: `文件位置: ${__INTERFACE__FILEPATH}
${content}`
})
}
result += `
const __CML_ERROR__ = ${throwError.toString()}
`
} else {
function throwError(content) {
throw new Error(`文件位置: ${__INTERFACE__FILEPATH}
${content}`)
}
result += `
const __CML_ERROR__ = ${throwError.toString()}
`
}
wrapperCode = `
${wrapper.toString().replace(/showErrorMessage/g, '__CML_ERROR__')}`
result += `
const __enableTypes__ = ${JSON.stringify(enableTypes)}
const __INTERFAE__DEFINES__ = ${JSON.stringify(interfaceDefines, null, 2)};
const __CML__DEFINES__ = ${JSON.stringify(cmlDefines.defines, null, 2)};
const __CML__WRAPPER__ = ${wrapperCode};
${newCode}
`
return result;
}
module.exports = {
getCheckCode
}
{
"name": "mvvm-script-parser",
"version": "0.4.0-mvvm.2",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.3.4",
"@babel/generator": "^7.4.0",
"@babel/parser": "^7.3.4",
"@babel/traverse": "^7.3.4",
"@babel/types": "^7.3.4",
"chameleon-tool-utils": "0.4.0-mvvm.2",
"mvvm-interface-parser": "0.4.0-mvvm.2",
"runtime-check": "0.4.0-mvvm.2"
}
}
\ No newline at end of file
let source = `import a from './a.js'
import {b,c} from './b.js'
import * as d from './c.js'
const e = require('./d.js');
require('./e.js')`
const mvvmpack = require('../../mvvm-pack/index.js');
const path = require('path');
let compiler = new mvvmpack({
config: {
check: {
enable: true, // 是否开启接口校验
enableTypes: [] // 接口校验支持的类型 可以开启["Object","Array","Nullable"]
}
},
logLevel: 3,
cmlType: 'web',
media: 'dev',
cmlRoot: path.resolve('./'),
projectRoot: path.resolve(__dirname, '../')
})
let parser = require('../index.js');
let result = parser.JSCompile({source, compiler, realPath: path.join(__dirname, './dependencies.js')})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册