Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DiDi
Chameleon
提交
e55aab4c
C
Chameleon
项目概览
DiDi
/
Chameleon
通知
13
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
Chameleon
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
e55aab4c
编写于
5月 15, 2019
作者:
K
kevinluohuan
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
interface check support include
上级
69ae5e1f
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
204 addition
and
124 deletion
+204
-124
packages/chameleon-linter/bin/cli.js
packages/chameleon-linter/bin/cli.js
+1
-0
packages/chameleon-linter/checkers/script.js
packages/chameleon-linter/checkers/script.js
+112
-100
packages/chameleon-linter/checkers/template/lib/template-ast-parser/classes/customized-node.js
...mplate/lib/template-ast-parser/classes/customized-node.js
+1
-1
packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/mustache-node.js
...e/lib/template-ast-parser/rules/template/mustache-node.js
+1
-1
packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/parse-single-expression.js
.../lib/template-ast-parser/tools/parse-single-expression.js
+8
-0
packages/chameleon-linter/classes/message.js
packages/chameleon-linter/classes/message.js
+13
-0
packages/chameleon-linter/file-spec.js
packages/chameleon-linter/file-spec.js
+1
-6
packages/chameleon-linter/index.js
packages/chameleon-linter/index.js
+0
-1
packages/chameleon-linter/utils.js
packages/chameleon-linter/utils.js
+67
-15
未找到文件。
packages/chameleon-linter/bin/cli.js
浏览文件 @
e55aab4c
#!/usr/bin/env node
// --inspect-brk
const
program
=
require
(
'
commander
'
);
const
packageJson
=
require
(
'
../package.json
'
);
...
...
packages/chameleon-linter/checkers/script.js
浏览文件 @
e55aab4c
const
traverse
=
require
(
'
@babel/traverse
'
)[
'
default
'
];
const
deepTraverse
=
require
(
'
traverse
'
);
const
uniq
=
require
(
'
lodash.uniq
'
);
const
config
=
require
(
'
../config
'
);
const
DEFAULT_TOKENS_MAP
=
{
WEEX
:
[
'
weex
'
,
'
global
'
],
...
...
@@ -62,6 +61,7 @@ const DEFAULT_TOKENS_MAP = {
]
};
// 这个path是否有需要校验的token
const
needCheck
=
function
(
path
,
tokenList
)
{
let
flag
=
false
;
...
...
@@ -103,37 +103,20 @@ function checkToken(path, token) {
}
const
checkGlobal
=
function
(
ast
,
type
=
'
ALL
'
)
{
let
tokensMap
=
DEFAULT_TOKENS_MAP
;
const
TOKENS_MAP
=
tokensMap
;
type
=
type
.
toUpperCase
();
const
TOKENS_MAP
=
DEFAULT_TOKENS_MAP
;
let
tokenList
=
[];
let
messages
=
[];
type
=
type
.
toUpperCase
();
if
(
type
===
'
ALL
'
)
{
// 都要校验
tokenList
=
TOKENS_MAP
[
type
];
}
else
{
Object
.
keys
(
TOKENS_MAP
).
forEach
(
key
=>
{
// 把自身的和All的去掉,其他端的token放进去
if
(
key
!==
type
&&
key
!==
'
ALL
'
)
{
tokenList
=
tokenList
.
concat
(
TOKENS_MAP
[
key
]);
}
})
// 然后要把自身的全局变量去掉
for
(
let
i
=
0
;
i
<
tokenList
.
length
;)
{
if
(
~
TOKENS_MAP
[
type
].
indexOf
(
tokenList
[
i
]))
{
tokenList
.
splice
(
i
,
1
);
}
else
{
i
++
;
}
Object
.
keys
(
TOKENS_MAP
).
forEach
(
key
=>
{
if
(
type
===
'
ALL
'
||
key
!==
type
)
{
tokenList
=
tokenList
.
concat
(
TOKENS_MAP
[
key
]);
}
}
});
tokenList
=
uniq
(
tokenList
);
const
messages
=
[];
traverse
(
ast
,
{
t
okenList
.
length
&&
t
raverse
(
ast
,
{
enter
:
(
path
)
=>
{
// path是一个上下文
// 需要校验的变量值
...
...
@@ -154,8 +137,6 @@ const checkGlobal = function (ast, type = 'ALL') {
}
next
=
next
.
parent
;
}
if
(
globalVariable
&&
path
.
parent
.
type
!=
'
ObjectMethod
'
&&
path
.
parent
.
type
!=
'
ClassMethod
'
)
{
messages
.
push
({
line
:
path
.
node
.
loc
.
start
.
line
,
...
...
@@ -197,11 +178,15 @@ const getInterfaces = (ast) => {
/**
* 获取类定义
*
* @param
{Object} ast ast
* @return {Object} 类定义
*
@param {Object} ast ast
* @param
{Object} isComp a flag indentify whether the ast is component or an interface portion
* @return
{Object} 类定义
*/
const
getClass
=
(
ast
)
=>
{
const
getClass
=
(
ast
,
isComp
)
=>
{
return
isComp
?
getCompClassDef
(
ast
)
:
getInterfacePortionClassDef
(
ast
);
};
function
getCompClassDef
(
ast
)
{
let
classes
=
[];
traverse
(
ast
,
{
...
...
@@ -221,15 +206,18 @@ const getClass = (ast) => {
});
}
// 参数
path
.
node
.
body
.
body
.
forEach
(
define
=>
{
if
(
define
.
key
.
name
==
'
props
'
)
{
define
.
value
.
properties
.
forEach
(
property
=>
{
clazz
.
properties
.
push
(
property
.
key
.
name
);
});
}
else
if
(
define
.
type
==
'
ClassMethod
'
)
{
clazz
.
methods
.
push
(
define
.
key
.
name
);
else
if
(
define
.
key
.
name
==
'
methods
'
)
{
define
.
value
.
properties
.
filter
(
property
=>
{
return
property
.
type
===
'
ObjectMethod
'
;
}).
forEach
(
property
=>
{
clazz
.
methods
.
push
(
property
.
key
.
name
);
});
}
else
{
deepTraverse
(
define
)
...
...
@@ -268,7 +256,45 @@ const getClass = (ast) => {
});
return
classes
;
};
}
function
getInterfacePortionClassDef
(
ast
)
{
let
classes
=
[];
traverse
(
ast
,
{
enter
(
path
)
{
if
(
path
.
node
.
type
==
'
ClassDeclaration
'
)
{
let
clazz
=
{
interfaces
:
[],
properties
:
[],
events
:
[],
methods
:
[]
};
// 接口
if
(
path
.
node
[
'
implements
'
])
{
path
.
node
[
'
implements
'
].
forEach
(
implament
=>
{
clazz
.
interfaces
.
push
(
implament
.
id
.
name
);
});
}
// 参数
path
.
node
.
body
.
body
.
forEach
(
define
=>
{
if
(
define
.
type
==
'
ClassProperty
'
)
{
clazz
.
properties
.
push
(
define
.
key
.
name
);
}
else
if
(
define
.
type
==
'
ClassMethod
'
)
{
clazz
.
methods
.
push
(
define
.
key
.
name
);
}
});
classes
.
push
(
clazz
);
}
}
});
return
classes
;
}
/**
* 校验接口与脚本
...
...
@@ -277,76 +303,62 @@ const getClass = (ast) => {
* @return {Array} 数组
*/
const
checkScript
=
async
(
result
)
=>
{
let
script
;
let
platforms
=
config
.
getPlatforms
();
[
'
script
'
].
concat
(
platforms
).
forEach
(
item
=>
{
if
(
result
[
item
]
&&
result
[
item
].
ast
)
{
script
=
result
[
item
];
}
});
if
(
!
result
[
'
interface
'
]
&&
script
)
{
let
interfaceFile
=
script
.
file
.
replace
(
new
RegExp
(
'
\\
.(
'
+
platforms
.
join
(
'
|
'
)
+
'
)
\\
.cml$
'
,
'
ig
'
),
'
.interface
'
);
if
(
/
\.
interface$/
.
test
(
interfaceFile
))
{
result
[
'
interface
'
]
=
{
messages
:
[{
msg
:
'
file: [
'
+
interfaceFile
+
'
] was not found!
'
}],
file
:
interfaceFile
};
let
validPlatforms
=
Object
.
keys
(
result
)
.
filter
(
platform
=>
{
return
platform
&&
(
platform
!=
'
interface
'
);
});
// add a script type for multi-file components.
result
[
'
interface
'
]
&&
validPlatforms
.
concat
(
'
script
'
).
forEach
(
platform
=>
{
let
script
;
if
(
result
[
platform
]
&&
result
[
platform
].
ast
)
{
script
=
result
[
platform
];
}
}
if
(
result
[
'
interface
'
]
&&
result
[
'
interface
'
].
ast
&&
script
&&
script
.
ast
)
{
const
interfaceDefine
=
getInterfaces
(
result
[
'
interface
'
].
ast
);
const
classDefines
=
getClass
(
script
.
ast
);
classDefines
.
forEach
(
clazz
=>
{
clazz
.
interfaces
.
forEach
(
interfaceName
=>
{
let
define
=
interfaceDefine
[
interfaceName
];
for
(
let
key
of
Object
.
keys
(
define
))
{
if
((
define
[
key
]
&&
define
[
key
].
type
==
'
Generic
'
)
&&
clazz
.
properties
.
indexOf
(
key
)
==
-
1
)
{
result
[
'
interface
'
].
messages
.
push
({
line
:
define
[
key
].
line
,
column
:
define
[
key
].
column
,
token
:
key
,
msg
:
'
property [
'
+
key
+
'
] is not found in file [
'
+
script
.
file
+
'
]
'
});
}
else
if
((
define
[
key
]
&&
define
[
key
].
type
==
'
Function
'
)
&&
clazz
.
methods
.
indexOf
(
key
)
==
-
1
)
{
platforms
.
forEach
(
platform
=>
{
if
(
result
[
platform
])
{
result
[
'
interface
'
].
messages
.
push
({
line
:
define
[
key
].
line
,
column
:
define
[
key
].
column
,
token
:
key
,
msg
:
'
method [
'
+
key
+
'
] is not found in file [
'
+
script
.
file
+
'
]
'
});
}
});
if
(
result
[
'
interface
'
]
&&
result
[
'
interface
'
].
ast
&&
script
&&
script
.
ast
)
{
const
interfaceDefine
=
getInterfaces
(
result
[
'
interface
'
].
ast
);
const
classDefines
=
getClass
(
script
.
ast
,
platform
===
'
script
'
);
classDefines
.
forEach
(
clazz
=>
{
clazz
.
interfaces
.
forEach
(
interfaceName
=>
{
let
define
=
interfaceDefine
[
interfaceName
];
for
(
let
key
of
Object
.
keys
(
define
))
{
if
((
define
[
key
]
&&
define
[
key
].
type
==
'
Generic
'
)
&&
clazz
.
properties
.
indexOf
(
key
)
==
-
1
)
{
result
[
'
interface
'
].
messages
.
push
({
line
:
define
[
key
].
line
,
column
:
define
[
key
].
column
,
token
:
key
,
msg
:
`interface property [
${
key
}
] is not defined for platform "
${
platform
}
" in file [
${
script
.
file
}
]`
});
}
else
if
((
define
[
key
]
&&
define
[
key
].
type
==
'
Function
'
)
&&
clazz
.
methods
.
indexOf
(
key
)
==
-
1
)
{
result
[
'
interface
'
].
messages
.
push
({
line
:
define
[
key
].
line
,
column
:
define
[
key
].
column
,
token
:
key
,
msg
:
`interface method [
${
key
}
] is not defined for platform "
${
platform
}
" in file [
${
script
.
file
}
]`
});
}
}
}
clazz
.
events
.
forEach
(
event
=>
{
if
(
!
define
[
event
.
event
]
||
(
define
[
event
.
event
]
&&
(
define
[
event
.
event
].
type
!=
'
Function
'
)))
{
script
.
messages
.
push
({
line
:
event
.
line
,
column
:
event
.
column
,
token
:
event
.
event
,
msg
:
'
event [
'
+
event
.
event
+
'
] is not defined in interface file [
'
+
result
[
'
interface
'
].
file
+
'
]
'
});
}
clazz
.
events
.
forEach
(
event
=>
{
if
(
!
define
[
event
.
event
]
||
(
define
[
event
.
event
]
&&
(
define
[
event
.
event
].
type
!=
'
Function
'
)))
{
script
.
messages
.
push
({
line
:
event
.
line
,
column
:
event
.
column
,
token
:
event
.
event
,
msg
:
'
event [
'
+
event
.
event
+
'
] is not defined in interface file [
'
+
result
[
'
interface
'
].
file
+
'
]
'
});
}
});
});
});
});
if
(
script
.
platform
)
{
let
messages
=
checkGlobal
(
script
.
ast
,
script
.
platform
);
script
.
messages
=
script
.
messages
.
concat
(
messages
);
if
(
script
.
platform
)
{
let
messages
=
checkGlobal
(
script
.
ast
,
script
.
platform
);
script
.
messages
=
script
.
messages
.
concat
(
messages
);
}
}
}
}
);
};
...
...
packages/chameleon-linter/checkers/template/lib/template-ast-parser/classes/customized-node.js
浏览文件 @
e55aab4c
/**
* A class reprents a customized component.
* A class repre
se
nts a customized component.
*/
class
CustomizedNode
{
constructor
(
tag
,
lang
=
'
cml
'
)
{
...
...
packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/mustache-node.js
浏览文件 @
e55aab4c
...
...
@@ -3,7 +3,7 @@ const Tools = require('../../tools');
const
mustacheRegex
=
/{{
(
.*
?)
}}/g
;
module
.
exports
=
{
name
:
'
m
ethod
-node
'
,
name
:
'
m
ustache
-node
'
,
on
:
[
'
cml
'
,
'
vue
'
],
filter
:
{
key
:
'
rawValue
'
,
...
...
packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/parse-single-expression.js
浏览文件 @
e55aab4c
...
...
@@ -67,6 +67,14 @@ module.exports.parseSingleExpression = function(expressinoStr = '') {
nodes
.
push
(
getVarFromIdentifier
(
path
.
node
.
property
,
isFakeBlock
));
}
},
LogicalExpression
(
path
)
{
if
(
path
.
get
(
'
left
'
).
isIdentifier
())
{
nodes
.
push
(
getVarFromIdentifier
(
path
.
node
.
left
,
isFakeBlock
));
}
if
(
path
.
get
(
'
right
'
).
isIdentifier
())
{
nodes
.
push
(
getVarFromIdentifier
(
path
.
node
.
right
,
isFakeBlock
));
}
},
BinaryExpression
(
path
)
{
if
(
path
.
get
(
'
left
'
).
isIdentifier
())
{
nodes
.
push
(
getVarFromIdentifier
(
path
.
node
.
left
,
isFakeBlock
));
...
...
packages/chameleon-linter/classes/message.js
0 → 100644
浏览文件 @
e55aab4c
/**
* A class represents an error message.
*/
class
Message
{
constructor
({
line
=
undefined
,
column
=
undefined
,
token
=
''
,
msg
=
''
})
{
this
.
line
=
line
;
this
.
column
=
column
;
this
.
token
=
token
;
this
.
msg
=
msg
||
'
An unknown error occurred
'
}
}
module
.
exports
=
Message
;
packages/chameleon-linter/file-spec.js
浏览文件 @
e55aab4c
...
...
@@ -76,7 +76,6 @@ const lintCmlFile = async (parts) => {
* @return {Promise} promise
*/
const
checkFile
=
async
(
lintedResult
,
filepath
)
=>
{
// 校验style
checkers
.
style
(
lintedResult
);
...
...
@@ -108,7 +107,6 @@ const checkFile = async (lintedResult, filepath) => {
*/
const
checkFileContent
=
async
(
filepath
)
=>
{
let
parts
=
utils
.
getCmlParts
(
filepath
);
let
result
=
await
lintCmlFile
(
parts
);
result
=
await
checkFile
(
result
,
filepath
);
...
...
@@ -128,7 +126,6 @@ const checkCMLFileSpecification = async (filepath) => {
messages
:
[]
}
};
if
(
new
RegExp
(
'
([^/]*?)
\
.(
'
+
platforms
.
join
(
'
|
'
)
+
'
)
\
.cml$
'
,
'
g
'
).
test
(
filepath
))
{
let
interfaceFile
=
filepath
.
replace
(
new
RegExp
(
'
\
.(
'
+
platforms
.
join
(
'
|
'
)
+
'
)
\
.cml$
'
,
'
g
'
),
'
.interface
'
);
if
(
!
fs
.
existsSync
(
interfaceFile
))
{
...
...
@@ -153,10 +150,9 @@ const checkCMLFileSpecification = async (filepath) => {
* @return {Promise} promise
*/
const
checkInterfaceFileSpecification
=
async
(
filepath
)
=>
{
let
parts
=
utils
.
getInterfaceParts
(
filepath
);
let
{
parts
}
=
utils
.
getInterfaceParts
(
filepath
);
let
result
=
{};
let
keys
=
Object
.
keys
(
parts
);
if
(
keys
.
length
>
1
)
{
for
(
let
key
in
parts
)
{
...
...
@@ -170,7 +166,6 @@ const checkInterfaceFileSpecification = async (filepath) => {
result
[
key
].
platform
=
part
.
platformType
;
}
}
// 校验脚本
checkers
.
script
(
result
);
return
result
;
...
...
packages/chameleon-linter/index.js
浏览文件 @
e55aab4c
...
...
@@ -49,7 +49,6 @@ module.exports = async (currentWorkspace, needOutputWarnings = true) => {
console
.
log
(
chalk
.
red
(
'
[ERROR]
'
)
+
'
The current project is not a chameleon project!
'
);
return
;
}
let
results
=
[];
if
(
config
.
getRuleOption
(
'
core-files-check
'
))
{
...
...
packages/chameleon-linter/utils.js
浏览文件 @
e55aab4c
const
fs
=
require
(
'
fs
'
);
const
path
=
require
(
'
path
'
);
const
chalk
=
require
(
'
chalk
'
);
const
groupBy
=
require
(
'
lodash.groupby
'
);
const
filter
=
require
(
'
lodash.filter
'
);
const
map
=
require
(
'
lodash.map
'
);
const
cliUtils
=
require
(
'
chameleon-tool-utils
'
);
const
config
=
require
(
'
./config
'
);
const
Message
=
require
(
'
./classes/message
'
);
let
isCmlComponent
=
(
templatePath
,
usingPath
)
=>
{
let
currentWorkspace
=
config
.
getCurrentWorkspace
();
...
...
@@ -111,25 +113,72 @@ let getCmlParts = filepath => {
let
getInterfaceParts
=
filepath
=>
{
let
content
=
fs
.
readFileSync
(
filepath
,
'
utf8
'
);
let
result
=
{};
let
parts
=
cliUtils
.
splitParts
({
content
});
let
_result
=
{
parts
:
{},
messages
:
[]
};
if
(
parts
.
script
)
{
parts
.
script
.
forEach
(
item
=>
{
result
[
item
.
cmlType
]
=
item
;
_retrieveParts
(
filepath
);
Object
.
assign
(
result
[
item
.
cmlType
],
{
params
:
{},
line
:
item
.
startLine
,
file
:
filepath
,
rawContent
:
item
.
tagContent
,
platformType
:
item
.
cmlType
function
_retrieveParts
(
interfaceFilePath
)
{
// terminate condition
if
(
!
fs
.
existsSync
(
interfaceFilePath
))
{
return
;
}
const
content
=
fs
.
readFileSync
(
interfaceFilePath
,
'
utf8
'
);
const
parts
=
cliUtils
.
splitParts
({
content
});
// search parts.script array for interface defination and platform specific definations.
if
(
parts
.
script
)
{
parts
.
script
.
forEach
(
item
=>
{
let
extraPartInfo
=
{
params
:
{},
line
:
item
.
startLine
,
file
:
interfaceFilePath
,
rawContent
:
item
.
tagContent
,
platformType
:
item
.
cmlType
};
// for interface portion we should keep the origin filepath
if
(
item
.
cmlType
===
'
interface
'
)
{
extraPartInfo
.
file
=
filepath
;
}
// check src references for platform definations
if
(
item
.
cmlType
!=
'
interface
'
&&
item
.
attrs
&&
item
.
attrs
.
src
)
{
const
targetScriptPath
=
path
.
resolve
(
path
.
dirname
(
interfaceFilePath
),
item
.
attrs
.
src
);
if
(
!
fs
.
existsSync
(
targetScriptPath
))
{
_result
.
messages
.
push
(
new
Message
({
line
:
item
.
line
,
column
:
item
.
tagContent
.
indexOf
(
item
.
attrs
.
src
)
+
1
,
token
:
item
.
attrs
.
src
,
msg
:
`The file
${
item
.
attrs
.
src
}
specified with src attribute was not found`
}));
}
extraPartInfo
.
content
=
extraPartInfo
.
rawContent
=
extraPartInfo
.
tagContent
=
fs
.
readFileSync
(
targetScriptPath
,
'
utf8
'
);
extraPartInfo
.
file
=
targetScriptPath
;
}
// previous cmlType defination has a higher priority.
if
(
!
_result
.
parts
[
item
.
cmlType
])
{
_result
.
parts
[
item
.
cmlType
]
=
{...
item
,
...
extraPartInfo
};
}
});
});
}
// search parts.customBlocks array for include defination which may contains another interface file.
let
include
=
null
;
if
(
parts
.
customBlocks
)
{
parts
.
customBlocks
.
forEach
(
item
=>
{
if
(
item
.
type
===
'
include
'
)
{
include
=
item
;
}
});
}
if
(
include
&&
include
.
attrs
&&
include
.
attrs
.
src
)
{
let
newFilePath
=
path
.
resolve
(
path
.
dirname
(
interfaceFilePath
),
include
.
attrs
.
src
);
return
_retrieveParts
(
newFilePath
);
}
return
;
}
return
result
;
return
_
result
;
}
...
...
@@ -165,7 +214,10 @@ let outputWarnings = (result) => {
item
.
messages
.
sort
((
preMsg
,
nextMsg
)
=>
{
return
preMsg
.
line
-
nextMsg
.
line
;
if
(
preMsg
.
line
==
undefined
||
preMsg
.
column
==
undefined
||
nextMsg
.
line
==
undefined
||
nextMsg
.
column
==
undefined
)
{
return
0
;
}
return
(
preMsg
.
line
-
nextMsg
.
line
)
*
10000
+
(
preMsg
.
column
-
nextMsg
.
column
);
})
.
forEach
((
message
)
=>
{
if
(
message
.
line
!==
undefined
&&
item
.
start
!==
undefined
&&
message
.
column
!==
undefined
)
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录