',
opts: {'class-style': '/^([a-z][A-Z])+(-[a-z]*)*$/'},
output: 1
}, {
desc: 'should ignore classes matching ignore regex',
input: '
',
opts: {'class-style': 'lowercase', 'id-class-ignore-regex': '{{.*?}}'},
output: 0
}, {
desc: 'should fail classes not matching ignore regex',
input: '
',
opts: {'class-style': 'lowercase', 'id-class-ignore-regex': '{{.*?}}'},
output: 1
}, {
desc: 'should give an error if id-class-ignore-regex is empty',
input: '',
opts: { 'class-style': 'lowercase', 'id-class-ignore-regex': '' },
output: 1
}, {
desc: 'should use id-class-style option if class-style is false',
input: '
',
opts: { 'class-style': false, 'id-class-style': 'lowercase' },
output: 1
}, {
desc: 'should not use id-class-style option if class-style is "none"',
input: '
',
opts: { 'class-style': 'none', 'id-class-style': 'lowercase' },
output: 0
}
]
================================================
FILE: packages/cml-htmllinter/test/functional/component-attr-check.js
================================================
module.exports = [
{
desc: 'should pass component-attr-check',
input: '
',
opts: {
'component-event-regex': /(?:c-bind:|c-catch:)(\w+)/,
'component-prop-regex': false,
'component-allow-attr': [{
name: 'component',
allowAttrs: {
vars: ['is', 'shrinkcomponents'],
methods: [],
props: [{
name: 'is',
required: true
}, {
name: 'shrinkcomponents',
required: false
}],
events: []
}
}]
},
output: 0
},
{
desc: 'should not pass component-attr-check',
input: '
',
opts: {
'component-event-regex': /(?:c-bind:|c-catch:)(\w+)/,
'component-prop-regex': false,
'component-allow-attr': {
component:{
vars: ['is', 'shrinkcomponents'],
methods: [],
props: [{
name: 'is',
required: true
}, {
name: 'shrinkcomponents',
required: false
}],
events: []
}
}
},
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/doctype-first.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'doctype-first': false },
output: 0
}, {
desc: 'should pass when set to any falsy value',
input: '
',
opts: { 'doctype-first': 0 },
output: 0
}, {
desc: 'should pass when !DOCTYPE is first',
input: '
',
opts: { 'doctype-first': true },
output: 0
}, {
desc: 'should pass case-insensitive !DOCTYPE',
input: '
',
opts: { 'doctype-first': true },
output: 0
}, {
desc: 'should fail when !DOCTYPE is not present',
input: '
',
opts: { 'doctype-first': true },
output: 1
}, {
desc: 'should fail when !DOCTYPE is not first',
input: '
',
opts: { 'doctype-first': true },
output: 1
}, {
desc: 'should pass when multiple !DOCTYPEs are present, as long as a !DOCTYPE is first',
input: '
',
opts: { 'doctype-first': true },
output: 0
}, {
desc: 'should pass in smart mode when !DOCTYPE and head are not present',
input: '
',
opts: { 'doctype-first': 'smart' },
output: 0
}, {
desc: 'should fail in smart mode when !DOCTYPE is not present but head is',
input: '
',
opts: { 'doctype-first': 'smart' },
output: 1
}, {
desc: 'should fail but not crash when first element is not a tag',
input: 'foobar',
opts: { 'doctype-first': true },
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/doctype-html5.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '',
opts: { 'doctype-html5': false },
output: 0
}, {
desc: 'should fail when given a html4 doctype',
input: '',
opts: { 'doctype-html5': true },
output: 1
}, {
desc: 'should pass when given a non-obsolete non-legacy doctype',
input: '',
opts: { 'doctype-html5': true },
output: 0
}, {
desc: 'should fail when given a legacy doctype',
input: '',
opts: { 'doctype-html5': true },
output: 1
}, {
desc: 'should pass a random directive',
input: '',
opts: { 'doctype-html5': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/fig-req-figcaption.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'fig-req-figcaption': false },
output: 0
},
{
desc: 'should fail when no figcaption is present',
input: '
',
opts: { 'fig-req-figcaption': true },
output: 1
},
{
desc: 'should fail when a caption is not a child',
input: '
',
opts: { 'fig-req-figcaption': true },
output: 1
},
{
desc: 'should fail twice when a figcaption is a sibling',
input: '
',
opts: { 'fig-req-figcaption': true },
output: 2
},
{
desc: 'should continue checking after a pass',
input: '
',
opts: { 'fig-req-figcaption': true },
output: 2
},
{
desc: 'should pass even if it is the last child',
input: '
',
opts: { 'fig-req-figcaption': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/focusable-tabindex-style.js
================================================
module.exports = [
{
desc: 'should pass two inputs with positive tabindexes',
input: [
'
'
].join(''),
opts: { 'focusable-tabindex-style': true },
output: 0
}, {
desc: 'should pass inputs without positive tabindexes',
input: [
'
'
].join(''),
opts: { 'focusable-tabindex-style': true },
output: 0
}, {
desc: 'should fail mixed inputs',
input: [
'
'
].join(''),
opts: { 'focusable-tabindex-style': true },
output: 2
}, {
desc: 'should fail inputs with both positive and negative tabindexes',
input: [
'
'
].join(''),
opts: { 'focusable-tabindex-style': true },
output: 3
}, {
desc: 'should ignore disabled inputs',
input: [
'
'
].join(''),
opts: { 'focusable-tabindex-style': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/head-req-title.js
================================================
module.exports = [
{
desc: 'head-req-title should pass when set to false',
input: '',
opts: { 'head-req-title': false },
output: 0
}, {
desc: 'head-req-title should pass when a title is present',
input: '
',
opts: { 'head-req-title': true },
output: 0
}, {
desc: 'head-req-title should fail when no title is present',
input: '',
opts: { 'head-req-title': true },
output: 1
}, {
desc: 'head-req-title should fail when title is empty',
input: '
',
opts: { 'head-req-title': true },
output: 1
}, {
desc: 'head-req-title should pass when some title is nonempty',
input: '
',
opts: { 'head-req-title': true },
output: 0
}
]
================================================
FILE: packages/cml-htmllinter/test/functional/head-valid-content-model.js
================================================
module.exports = [
{
desc: 'head-div should pass when head-valid-content-model set to false',
input: '
',
opts: { 'head-valid-content-model': false },
output: 0
}, {
desc: 'head-div should fail when head-valid-content-model set to true',
input: '
',
opts: { 'head-valid-content-model': true },
output: 1
}, {
desc: 'should pass when no head is present',
input: '',
opts: { 'head-valid-content-model': true },
output: 0
}, {
desc: 'legal elements in head should pass',
input: '
',
opts: { 'head-valid-content-model': true },
output: 0
}, {
desc: 'illegal elements in head should fail',
input: '
',
opts: { 'head-valid-content-model': true },
output: 1
}, {
desc: 'multiple illegal elements should all fail',
input: '
',
opts: { 'head-valid-content-model': true },
output: 3
}, {
desc: 'empty head should pass',
input: '',
opts: { 'head-valid-content-model': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/href-style.js
================================================
module.exports = [
{
desc: 'should not match absolute links given absolute option',
input: '
',
opts: { 'href-style': 'absolute' },
output: 0
}, {
desc: 'should match relative links given absolute option',
input: '
',
opts: { 'href-style': 'absolute' },
output: 1
}, {
desc: 'should not match relative links given relative option',
input: '
',
opts: { 'href-style': 'relative' },
output: 0
}, {
desc: 'should match absolute links given relative option',
input: '
',
opts: { 'href-style': 'relative' },
output: 1
}, {
desc: 'should not match any links given false option',
input: '
',
opts: { 'href-style': false },
output: 0
}, {
desc: 'should not match on
',
opts: { 'href-style': 'absolute' },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/html-valid-content-model.js
================================================
module.exports = [
{
desc: 'should pass html-div when set to false',
input: '
',
opts: { 'html-valid-content-model': false },
output: 0
}, {
desc: 'should fail html-div when set to true',
input: '
',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should pass head when no html is present',
input: '',
opts: { 'html-valid-content-model': true },
output: 0
}, {
desc: 'should pass html-head-body',
input: '',
opts: { 'html-valid-content-model': true },
output: 0
}, {
desc: 'should fail html-head-div-body',
input: '
',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should fail for each illegal element',
input: '
',
opts: { 'html-valid-content-model': true },
output: 2
}, {
desc: 'should fail html-head-head',
input: '',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should fail html-body-body',
input: '',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should fail html-body-head',
input: '',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should fail html-head-body-head once',
input: '',
opts: { 'html-valid-content-model': true },
output: 1
}, {
desc: 'should fail html-body-head-head twice',
input: '',
opts: { 'html-valid-content-model': true },
output: 2
}, {
desc: 'should pass html-head-body',
input: '
',
opts: { 'html-valid-content-model': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/id-class-no-ad.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'id-class-no-ad': false },
output: 0
}, {
desc: 'should pass id and class values not containing "ad"',
input: '
',
opts: { 'id-class-no-ad': true },
output: 0
}, {
desc: 'should pass id and class values containing "ad" but not as a word',
input: '
',
opts: { 'id-class-no-ad': true },
output: 0
}, {
desc: 'should fail id values containing "ad"',
input: '
',
opts: { 'id-class-no-ad': true },
output: 1
}, {
desc: 'should fail class values containing "ad"',
input: '
',
opts: { 'id-class-no-ad': true },
output: 1
}, {
desc: 'should fail values containing "banner" or "social"',
input: '
',
opts: { 'id-class-no-ad': true },
output: 2
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/id-no-dup.js
================================================
module.exports = [
{
desc: 'should pass html with no duplicate ids',
input: '
',
opts: { 'id-no-dup': true },
output: 0
}, {
desc: 'should fail html with a duplicate id',
input: '
',
opts: { 'id-no-dup': true },
output: 1
}, {
desc: 'should pass any html when disabled',
input: '
',
opts: { 'id-no-dup': false },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/id-style.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'id-class-style': false },
output: 0
}, {
desc: 'should fail when given an unknown option value',
input: '',
opts: { 'id-class-style': 'inconceivable' },
output: 1
}, {
desc: 'should pass correctly styled id',
input: '
',
opts: { 'id-class-style': 'lowercase' },
output: 0
}, {
desc: 'should fail incorrectly styled id names',
input: '
',
opts: { 'id-class-style': 'lowercase' },
output: 1
}, {
desc: 'should accept a "dash" option',
input: '
',
opts: { 'id-class-style': 'dash' },
output: 0
}, {
desc: 'should accept ids that use the BEM format',
input: '
',
opts: {'id-class-style': 'bem'},
output: 0
}, {
desc: 'should not accept ids that aren\'t compatible the BEM format',
input: '
',
opts: {'id-class-style': 'bem'},
output: 2
}, {
desc: 'should accept a custom format RegExp',
input: '
',
opts: {'id-class-style': /^[-_]+$/},
output: 1
}, {
desc: 'should ignore ids matching ignore regex',
input: '
',
opts: {'id-class-style': 'lowercase', 'id-class-ignore-regex': '{{.*?}}'},
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/img-req-alt.js
================================================
module.exports = [
{
desc: 'should not match images with fallbacks',
input: '

',
opts: { 'img-req-alt': true },
output: 0
}, {
desc: 'should match images without fallbacks',
input: '

',
opts: { 'img-req-alt': true },
output: 1
}, {
desc: 'should fail when img alt is empty',
input: '

',
opts: { 'img-req-alt': true },
output: 1
}, {
desc: 'should not fail when img alt is null but value is "allownull"',
input: '

',
opts: { 'img-req-alt': 'allownull' },
output: 0
}, {
desc: 'should fail when img alt is not given even if value is "allownull"',
input: '

',
opts: { 'img-req-alt': 'allownull' },
output: 0
}, {
desc: 'should not match non image elements without fallbacks',
input: '
this should return 0
',
opts: { 'img-req-alt': true },
output: 0
}, {
desc: 'should not match when disabled',
input: '

',
opts: { 'img-req-alt': false },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/img-req-src.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
![]()
',
opts: { 'img-req-src': false },
output: 0
}, {
desc: 'should pass when image tags have set src attribs',
input:'

',
opts: { 'img-req-src': true },
output: 0
}, {
desc: 'should fail when the img src is not set',
input: '
![]()
',
opts: { 'img-req-src': true },
output: 1
}, {
desc: 'should fail when the img src is empty',
input: '
![]()
',
opts: { 'img-req-src': true },
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/indent-delta.js
================================================
module.exports = [
{
desc: 'should work with tabs',
input: [
'',
'\t
hello
',
''
].join('\n'),
opts: {'indent-delta':true },
output: 0
}, {
desc: 'should detect bad tab indent',
input: [
'',
'\t\t\t
hello
',
'\t\t\t
hello
',
''
].join('\n'),
opts: { 'indent-delta':true },
output: 2
}, {
desc: 'should work with tabs and spaces',
input: [
'',
'\t
hello
',
'
hello
',
''
].join('\n'),
opts: { 'indent-delta':true, 'indent-width':2 },
output: 0
}, {
desc: 'should tab to next multiple of indent-width',
input: [
'',
'\t
hello
', // 1 tab
' \t \t
hello
', // 2 tabs
'
hello
', // 1 tab
''
].join('\n'),
opts: { 'indent-delta':true, 'indent-width':4 },
output: 1 // From indent-width; none from indent-delta
}, {
desc: 'should ignore empty lines',
input: [
'',
'\t
break with
',
'\t\t
empty line
',
'',
'\t
is fine
',
''
].join('\r\n'),
opts: { 'indent-delta':true, 'indent-width':6 },
output: 0
}, {
desc: 'should detect bad indent with tabs and spaces',
input: [
'',
'\t
hello
',
'
hello
',
'\t\t\t
hello
',
'
hello
',
''
].join('\n'),
opts: { 'indent-delta':true, 'indent-width':2 },
output: 3
},
]
================================================
FILE: packages/cml-htmllinter/test/functional/indent-style.js
================================================
module.exports = [
{
desc: 'should match spaces when configured for tabs',
input: [
'',
'
hello
',
''
].join('\n'),
opts: { 'indent-style': 'tabs' },
output: 1
}, {
desc: 'should match tabs when configured for spaces',
input: [
'',
'\t
ello
',
''
].join('\n'),
opts: { 'indent-style': 'spaces' },
output: 1
}, {
desc: 'should match tabs when configured for spaces',
input: [
'',
'\t
ello
',
'\t
ello
',
'\t
ello
',
''
].join('\n'),
opts: { 'indent-style': 'spaces' },
output: 3
},
{
desc: 'should match inmixed indents',
input: [
'',
'\t
this not okay
',
''
].join('\n'),
opts: { 'indent-style': 'spaces' },
output: 1
}, {
desc: 'should match mixed indents starting with spaces',
input: [
'',
'
this not okay
',
'
this not okay
',
'\t\t\t\t
this not okay
',
'\t\t\t\t
this not okay
',
''
].join('\n'),
opts: { 'indent-style': 'nonmixed' },
output: 2
}, {
desc: 'should match mixed indents starting with tabs',
input: [
'',
'\t\t
this not okay
',
'\t\t
this not okay
',
'
Hey
',
''
].join('\n'),
opts: { 'indent-style': 'nonmixed' },
output: 1
}, {
desc: 'should not match on false',
input: [
'',
'\t\t
this not okay
',
'\t\t
this not okay
',
'
Hey
',
''
].join('\n'),
opts: { 'indent-style': false },
output: 0
}, {
desc: 'should work with lines containing only tabs',
input: '\t',
opts: { 'indent-style': false },
output: 0
},
{
desc: 'indent-width should match indents with the wrong number of spaces',
input: [
'',
'
this not okay
',
' \t
this not okay
',
'
Hey
',
''
].join('\n'),
opts: { 'indent-width': 4 },
output: 2
}, {
desc: 'indent-width should not match when set to false',
input: [
'',
'
this not okay
',
' \t
this not okay
',
'
Hey
',
''
].join('\n'),
opts: { 'indent-width': false },
output: 0
}, {
desc: 'indent-width should not fail with all-whitespace lines',
input: [
'',
'
okay
',
'',
' \t',
' ',
'',
' '
].join('\n'),
opts: { 'indent-width': 4 },
output: 1
}, {
desc: 'indent-width should work with strange indent widths',
input: [
'',
'
this is fine
',
'
this not okay
',
'\t
Hey
',
''
].join('\n'),
opts: { 'indent-width': 5 },
output: 2
},
{
desc: 'indent-width-cont should match indents with the wrong number of spaces starting with <',
input: [
'',
'
eight spaces
',
'
ten spaces
',
'
five spaces
',
''
].join('\n'),
opts: { 'indent-width': 4, 'indent-width-cont': true },
output: 2
}, {
desc: 'indent-width-cont should work with strange indent widths',
input: [
'',
'
eight spaces
',
'
ten spaces
',
'
five spaces
',
''
].join('\n'),
opts: { 'indent-width': 5, 'indent-width-cont': true },
output: 1
}, {
desc: 'indent-width-cont should not match indents that do not start with <',
input: [
'',
'
eight spaces',
' ten spaces',
' five spaces
',
''
].join('\n'),
opts: { 'indent-width': 4, 'indent-width-cont': true },
output: 0
}, {
desc: 'indent-width-cont should allow spaces after tabs but not before',
input: [
'',
'\t
',
'\t partial tabbing',
'\t
',
' \t
illegal
',
''
].join('\n'),
opts: { 'indent-style': 'tabs', 'indent-width-cont': true },
output: 2
}, {
desc: 'indent-width-cont should check lines with only spaces',
input: [
'
',
' ',
' ',
'
',
''
].join('\n'),
opts: { 'indent-width': 4, 'indent-width-cont': true },
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/input-radio-req-name.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'input-radio-req-name': false },
output: 0
},
{
desc: 'should pass when input has no type',
input: '
',
opts: { 'input-radio-req-name': true },
output: 0
},
{
desc: 'should pass when input has no type value',
input: '
',
opts: { 'input-radio-req-name': true },
output: 0
},
{
desc: 'should pass when input has type text',
input: '
',
opts: { 'input-radio-req-name': true },
output: 0
},
{
desc: 'should fail when radio input has no name',
input: '
',
opts: { 'input-radio-req-name': true },
output: 1
},
{
desc: 'should fail when radio input has empty name',
input: '
',
opts: { 'input-radio-req-name': true },
output: 1
},
{
desc: 'should fail when radio input has name with no length',
input: '
',
opts: { 'input-radio-req-name': true },
output: 1
},
{
desc: 'should pass when radio input has a name',
input: '
',
opts: { 'input-radio-req-name': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/input-req-label.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: {'input-req-label': false },
output: 0
},
{
desc: 'should do nothing with just a label',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should do nothing with a label with a "for" attrib',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should do nothing with just a label',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should do nothing with just an input',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should do nothing with an input of the wrong type',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should return an error if the text input has no label parent and no id/name',
input: '
',
opts: {'input-req-label': true },
output: 1
},
{
desc: 'should return an error if the radio input has no label parent and no id/name',
input: '
',
opts: {'input-req-label': true },
output: 1
},
{
desc: 'should pass if text input has a label parent anywhere up the DOM',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should pass if radio input has a label parent anywhere up the DOM',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should fail if the text input has a name not matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 1
},
{
desc: 'should pass if the text input has a name matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should fail if the text input has an id not matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 1
},
{
desc: 'should pass if the text input has an id matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 0
},
{
desc: 'should fail if the radio input has a name matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 1
},
{
desc: 'should pass if the radio input has an id matching the label for',
input: '
',
opts: {'input-req-label': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/label-req-for.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: [
'
'
].join(''),
opts: { 'label-req-for': false },
output: 0
}, {
desc: 'should pass valid for attr in strict mode',
input: [
'
',
'
'
].join(''),
opts: { 'label-req-for': 'strict' },
output: 0
}, {
desc: 'should pass with multiple valid labels',
input: [
'
',
'
',
'
',
'
'
].join(''),
opts: { 'label-req-for': 'strict' },
output: 0
}, {
desc: 'should pass even with out-of-order and duplicated elements',
input: [
'
',
'
',
'
',
'
',
'
'
].join(''),
opts: { 'label-req-for': 'strict' },
output: 0
}, {
desc: 'should fail label with only child in strict mode',
input: [
'
'
].join(''),
opts: { 'label-req-for': 'strict' },
output: 1
}, {
desc: 'should pass label with only child in nonstrict mode',
input: [
'
'
].join(''),
opts: { 'label-req-for': true },
output: 0
}, {
desc: 'should fail label with nonexistant for id',
input: [
'
'
].join(''),
opts: { 'label-req-for': true },
output: 1
}, {
desc: 'should pass label for with child id',
input: [
'
'
].join(''),
opts: { 'label-req-for': true },
output: 0
}, {
desc: 'should fail an unlabeable child in nonstrict',
input: [
'
'
].join(''),
opts: { 'label-req-for': true },
output: 1
}, {
desc: 'should fail an id that points to an unlabeable element',
input: [
'
'
].join(''),
opts: { 'label-req-for': true },
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/lang.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '\n',
opts: { 'html-req-lang': false },
output: 0
}, {
desc: 'should pass html with lang',
input: '\n',
opts: { 'html-req-lang': true },
output: 0
}, {
desc: 'should fail on html with no lang',
input: '\n',
opts: { 'html-req-lang': true },
output: 1
}, {
desc: 'should fail on invalid lang if lang-style is true',
input: '\n',
opts: { 'lang-style': true },
output: 1
}, {
desc: 'should pass on valid lang if lang-style is true',
input: '\n',
opts: { 'lang-style': true },
output: 0
}, {
desc: 'should allow empty lang tag',
input: '\n',
opts: { 'lang-style': true },
output: 0
}, {
desc: 'should fail on wrong-case lang if lang-style is "case"',
input: '\n',
opts: { 'lang-style': 'case' },
output: 1
}, {
desc: 'should pass correct lang if lang-style is "case"',
input: '\n',
opts: { 'lang-style': 'case' },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/line-end-style.js
================================================
var input = [
'\n',
'
\n',
' some\r',
' text\n',
'
\r\n',
'\r'
].join('');
module.exports = [
{
desc: 'should not match anything when set to false',
input: input,
opts: { 'line-end-style': false },
output: 0
}, {
desc: 'should fail on unknown style',
input: '',
opts: { 'line-end-style': 'inadmissible' },
output: 1
}, {
desc: 'should match CR and CRLF when set to LF',
input: input,
opts: { 'line-end-style': 'lf' },
output: 3
}, {
desc: 'should match LF and CRLF when set to CR',
input: input,
opts: { 'line-end-style': 'cr' },
output: 4
}, {
desc: 'should match CR and LF when set to CRLF',
input: input,
opts: { 'line-end-style': 'crlf' },
output: 5
}, {
desc: 'should not match empty lines',
input: ['
','','
'].join('\n') + '\n',
opts: { 'line-end-style': 'lf' },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/line-max-len.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '-----------------------------------------------------------------------------------',
opts: { 'line-max-len': false },
output: 0
}, {
desc: 'should fail when given a non-number',
input: '',
opts: { 'line-max-len': 'its-a-string' },
output: 1
}, {
desc: 'should fail when given a negative number',
input: '1234',
opts: { 'line-max-len': -5 },
output: 1
}, {
desc: 'should pass when line length not exceeds the maximum value',
input: '1234',
opts: { 'line-max-len': 5 },
output: 0
}, {
desc: 'should pass when line length equals to the maximum value',
input: '12345',
opts: { 'line-max-len': 5 },
output: 0
}, {
desc: 'should ignore line breaks',
input: '12345\r\n12345\n',
opts: { 'line-max-len': 5 },
output: 0
}, {
desc: 'should fail when line length exceeds the maximum value',
input: '123456',
opts: { 'line-max-len': 5 },
output: 1
}, {
desc: 'should pass when line length matches ignore-regex',
input: '
12345',
opts: { 'line-max-len': 5, 'line-max-len-ignore-regex': 'href' },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/line-no-trailing-whitespace.js
================================================
module.exports = [
{
desc: 'should not match anything when set to false',
input: 's p a c e \n\t\r',
opts: { 'line-no-trailing-whitespace': false },
output: 0
}, {
desc: 'should match trailing spaces',
input: 's p a c e \n',
opts: { 'line-no-trailing-whitespace': true },
output: 1
}, {
desc: 'should match multiple times',
input: 's p a c e \n a n d\t\nl i n e',
opts: { 'line-no-trailing-whitespace': true },
output: 2
}, {
desc: 'should match only once for multiple spaces on one line',
input: 's p a c e\n a n d \t \t',
opts: { 'line-no-trailing-whitespace': true },
output: 1
}, {
desc: 'should match unicode spaces',
input: 's p a c e\u00a0\r a n d\u2007\rl i n e\u205f',
opts: { 'line-no-trailing-whitespace': true },
output: 3
}, {
desc: 'should not match empty lines with CRLF',
input: ['
','','
'].join('\r\n') + '\r\n',
opts: { 'line-no-trailing-whitespace': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/link-req-noopener.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
link',
opts: {'link-req-noopener': false},
output: 0
}, {
desc: 'should pass when there is no target="_blank" attribute',
input: '
index',
opts: {'link-req-noopener': true},
output: 0
}, {
desc: 'should fail on a link with target="_blank" but no rel attribute',
input: '
',
opts: {'link-req-noopener': true},
output: 1
}, {
desc: 'should fail on a link with target="_blank" but no rel attribute',
input: '',
opts: {'link-req-noopener': true},
output: 1
}, {
desc: 'should pass a link with rel="noopener"',
input: '',
opts: {'link-req-noopener': true},
output: 0
}, {
desc: 'should pass a link with rel=noreferrer',
input: '',
opts: {'link-req-noopener': true},
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/raw-ignore-regex.js
================================================
// We set line-end-style to lf for each option at the end of this file
// for a predictable rule that will fail on each instance of \r.
module.exports = [
{
desc: 'should not ignore text if set to false',
input: '\r\r\r\r[[\r\n\t fjq\r\n\r]]\r\r',
opts: { 'raw-ignore-regex': false },
output: 9
}, {
desc: 'should remove matching text',
input: '\r\r\r\r[[\r\n\t fjq\r\n\r]]\r\r\n',
opts: { 'raw-ignore-regex': /\r/ },
output: 0
}, {
desc: 'should work across line breaks',
input: '\r\r\r\r[[\r\n\t fjq\r\n\r]]\r\r',
opts: { 'raw-ignore-regex': /\[\[[^]*?\]\]/ },
output: 6
}, {
desc: 'should remove multiple matches',
input: '\r{\r\r}\r[[\r\n\t fjq\r\n\r]]\r\r',
opts: { 'raw-ignore-regex': /(\{[^]*?\}|\[\[[^]*?\]\])/ },
output: 4
}
];
module.exports.forEach(function(test) {
test.opts['line-end-style'] = 'lf';
});
================================================
FILE: packages/cml-htmllinter/test/functional/runner.test.js
================================================
const chai = require('chai');
const assert = chai.assert;
const should = chai.should;
const expect = chai.expect;
var fs = require('fs'),
path = require('path'),
lodash = require('lodash'),
htmllint = require('../../lib'),
presets = require('../../lib/presets');
// find all files in this directory that are .js files
var testFiles = fs.readdirSync(__dirname)
.filter(function (filepath) {
return (path.extname(filepath) === '.js' &&
path.basename(filepath) !== 'runner.test.js');
})
.map(function (filepath) {
var basename = path.basename(filepath, '.js');
return {
name: basename,
mod: require('./' + basename)
};
});
function createLinter(names) {
return new htmllint.Linter(htmllint.rules);
}
function doTest(funcTest, testFile) {
it(funcTest.desc, function (cb) {
// configure a new linter
var linter = createLinter(funcTest.rules),
opts = funcTest.opts || testFile.defaults;
var promise = linter.lint(funcTest.input, presets.presets.none, opts),
expected = funcTest.output;
promise.then(function (output) {
if (lodash.isNumber(expected)) {
// test expects a certain number of issues
expect(output).to.have.length(expected);
} else {
// TODO: order probably shouldn't matter
// TODO: better assertion messages
expected.forEach(function (expectedIssue, index) {
if (output.length <= index) {
// only validate if the length is right,
// length is tested later
return;
}
var actual = output[index];
// validate on each property specified in the expected issue
Object.keys(expectedIssue)
.forEach(function (key) {
expect(actual[key]).to.be.equal(expectedIssue[key]);
});
});
expect(output).to.have.length(expected.length);
}
}).then(cb, cb);
});
}
// for each test file, create a test suite
testFiles.forEach(function (testFile) {
describe(testFile.name, function () {
testFile.mod.forEach(function (test) {
if (!test.hasOwnProperty('rules')) {
// if no rules are specified, use the filename
test.rules = [testFile.name];
if (!test.defaults) {
test.defaults = {};
}
}
doTest(test, testFile);
});
});
});
================================================
FILE: packages/cml-htmllinter/test/functional/spec-char-escape.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '',
opts: { 'spec-char-escape': false },
output: 0
}, {
desc: 'should recognize special characters inside of text elements',
input: '',
opts: { 'spec-char-escape': true },
output: 1
}, {
desc: 'should recognize special characters in attribute values',
input: '',
opts: { 'spec-char-escape': true },
output: 1
}, {
desc: 'should return an error for each invalid escape',
input: '',
opts: { 'spec-char-escape': true },
output: 3
}, {
desc: 'should ignore text matching text-ignore-regex',
input: 'Some\n{{>>angular&!!\\}}\n{{!!!!}}\ntext
',
opts: { 'spec-char-escape': true , 'text-ignore-regex': /{{.*?}}/ },
output: 0
}, {
desc: 'should check sections of text not matching text-ignore-regex',
input: 'Some&\n{{>>angular&!!\\}}\n>text
',
opts: { 'spec-char-escape': true , 'text-ignore-regex': /{{.*?}}/ },
output: 2
}, {
desc: 'text-ignore-regex should maintain case-insensitive flag',
input: 'AA&Bb aa&bb',
opts: { 'spec-char-escape': true , 'text-ignore-regex': /aa.*?bb/i },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/table-req-caption.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '',
opts: { 'table-req-caption': false },
output: 0
},
{
desc: 'should fail when no caption is present',
input: '
',
opts: { 'table-req-caption': true },
output: 1
},
{
desc: 'should fail when a caption is a grand-child',
input: '
',
opts: { 'table-req-caption': true },
output: 1
},
{
desc: 'should fail when a caption is a sibling',
input: '
Hello',
opts: { 'table-req-caption': true },
output: 1
},
{
desc: 'should fail twice with two tables',
input: '
',
opts: { 'table-req-caption': true },
output: 2
},
{
desc: 'should pass even if it is the last child',
input: '
',
opts: { 'table-req-caption': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/table-req-header.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
',
opts: { 'table-req-header': false },
output: 0
},
{
desc: 'should fail when no header is present',
input: '
',
opts: { 'table-req-header': true },
output: 1
},
{
desc: 'should pass when header is present',
input: '
',
opts: { 'table-req-header': true },
output: 0
},
{
desc: 'should pass when th is not first cell in tr',
input: '
',
opts: { 'table-req-header': true },
output: 0
},
{
desc: 'should fail when th not encapsulated in a tr because that\'s technically not valid',
input: '
',
opts: { 'table-req-header': true },
output: 1
},
{
desc: 'should fail when header isn\'t at the top of the table',
input: '
',
opts: { 'table-req-header': true },
output: 1
},
{
desc: 'should pass when table has caption and th',
input: '
',
opts: { 'table-req-header': true },
output: 0
},
{
desc: 'should pass when table has caption and thead',
input: '
',
opts: { 'table-req-header': true },
output: 0
},
{
desc: 'should pass when table has caption and text before thead',
input: '
',
opts: { 'table-req-header': true },
output: 0
},
{
desc: 'should pass when table has caption and text before th',
input: '
\n\tDOGS\n \n\t | Shh | \n \tDoges | \n
\n
',
opts: { 'table-req-header': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/tag-bans.js
================================================
module.exports = [
{
desc: 'should not match div by default',
input: '
',
output: 0
}, {
desc: 'should match options if user sets them',
input: '
',
opts: { 'tag-bans': ['button', 'main'] },
output: 2
}, {
desc: 'should not match defaults if user sets new elements in options',
input: '
',
opts: { 'tag-bans': ['button', 'main'] },
output: 0
}, {
desc: 'should not match when disabled',
input: '
',
opts: { 'tag-bans': false },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/tag-name-lowercase.js
================================================
module.exports = [
{
desc: 'should match attributes with mixed case',
input: '',
opts: { 'tag-name-lowercase': true },
output: 1
}, {
desc: 'should not match attributes with lowered case',
input: '',
opts: { 'tag-name-lowercase': true },
output: 0
}, {
desc: 'should multiple mixed-case elements',
input: '
',
opts: { 'tag-name-lowercase': true },
output: 2
}, {
desc: 'should not match when disabled',
input: '
',
opts: { 'tag-name-lowercase': false },
output: 0
}, {
desc: 'should not match directives',
input: '',
opts: { 'tag-name-lowercase': true },
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/tag-req-attr.js
================================================
module.exports = [
{
desc: 'should pass when set to false',
input: '
![]()
',
opts: {
'tag-req-attr': false
},
output: 0
},
{
desc: 'should pass when set to empty object',
input: '
![]()
',
opts: {
'tag-req-attr': {}
},
output: 0
}, {
desc: 'should pass when tag has correct attributes set',
input: '

',
opts: {
'tag-req-attr': {
'img': [
{
'name': 'src'
},
{
'name': 'alt'
}
]
}
},
output: 0
}, {
desc: 'should fail when tag has no correct attributes set',
input: '
![]()
',
opts: {
'tag-req-attr': {
'img': [
{
'name': 'src'
}
]
}
},
output: 1
}, {
desc: 'should fail when tag has no correct attributes set',
input: '
![]()
',
opts: {
'tag-req-attr': {
'img': [
{
'name': 'src',
'allowEmpty': false
}
]
}
},
output: 1
}, {
desc: 'should fail when tag attribute is empty',
input: '
![]()
',
opts: {
'tag-req-attr': {
'img': [
{
'name': 'src'
}
]
}
},
output: 1
}, {
desc: 'should pass when tag has correct attributes set',
input: '

',
opts: {
'tag-req-attr': {
'img': [
{
'name': 'src'
},
{
'name': 'alt',
'allowEmpty': true
}
]
}
},
output: 0
}, {
desc: 'should pass when there is no configuration for the tag',
input: '

',
opts: {
'tag-req-attr': {
'input': [
{
'name': 'type'
}
]
}
},
output: 0
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/title-max-len.js
================================================
module.exports = [
{
desc: 'title-max-len should pass when set to false',
input: '
looooooooooooooooooooooooooooooooooooooooo'
+ 'oooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'oooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
+ 'ooooooooooooooooooooooooooooooooooooooong',
opts: { 'title-max-len': false },
output: 0
}, {
desc: 'title-max-len should pass when the title fits requirements',
input: '
Title!',
opts: { 'title-max-len': 60 },
output: 0
}, {
desc: 'title-max-len should fail for long titles',
input: '
Tiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'
+ 'iiiiiiiiiiiiiiiiiiiitle',
opts: { 'title-max-len': 60 },
output: 1
}, {
desc: 'title-max-len should fail for short maximum lengths',
input: '
Title!',
opts: { 'title-max-len': 4 },
output: 1
}
];
================================================
FILE: packages/cml-htmllinter/test/functional/title-no-dup.js
================================================
module.exports = [
{
desc: 'title-no-dup should pass when set to false',
input: '
TileNo wait title'
+ '
More titles for good measure',
opts: { 'title-no-dup': false },
output: 0
}, {
desc: 'title-no-dup should pass when only one title is present',
input: '
Title!',
opts: { 'title-no-dup': true },
output: 0
}, {
desc: 'title-no-dup should fail when multiple titles are present',
input: '
TitleAnother title',
opts: { 'title-no-dup': true },
output: 1
}, {
desc: 'title-no-dup should fail only once for many titles',
input: '
Mr.Dr.'
+ '
Professor',
opts: { 'title-no-dup': true },
output: 1
}
]
================================================
FILE: packages/cml-htmllinter/test/unit/htmllint.js
================================================
describe('htmllint', function () {
var htmllint = require('../../');
it('should be a function', function () {
expect(htmllint).to.be.an.instanceOf(Function);
});
it('should return a thenable', function () {
var thenable = htmllint('');
expect(thenable).to.have.property('then');
});
it('should eventually return an array', function () {
var result = htmllint('');
return result.then(function (output) {
expect(output).to.be.an.instanceOf(Array);
});
});
it('should not throw on sanity.html', function () {
var fs = require('fs');
var filePath = '../fixtures/sanity.html';
var path = require('path');
var sanityHtml = fs.readFileSync(path.resolve(__dirname, filePath), {encoding: 'utf8'});
expect(function () {
htmllint(sanityHtml);
}).to.not.throw(Error);
});
describe('use', function () {
it('should register a plugin on the defaultLinter', function () {
var rule = {
name: 'testrule'
}, plugins = [
'chai',
{ rules: [rule] }
];
htmllint.use(plugins);
expect(htmllint.defaultLinter.rules.getRule(rule.name)).to.be.eql(rule);
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/inline-config-html/inline-all.html
================================================
Hello, World!
Heading
Paragraph
This inside that
This inside that
================================================
FILE: packages/cml-htmllinter/test/unit/knife.apply_rules.js
================================================
describe('knife.apply_rules', function () {
var knife = require('../../lib/knife');
it('should return [] for no rules', function () {
var output = knife.applyRules(null);
expect(output).to.be.eql([]);
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/knife.is_labeable.js
================================================
describe('knife.is_labeable', function () {
var knife = require('../../lib/knife');
it('should return false for hidden input element', function () {
var ele = {
type: 'tag',
name: 'input',
attribs: {
type: {
value: 'hidden'
}
}
};
var output = knife.isLabeable(ele);
expect(output).to.be.eql(false);
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/knife.relative_line_col.js
================================================
describe('knife.relative_line_col', function () {
var knife = require('../../lib/knife');
it('should throw when called with an index behind the last', function () {
var posFunc = knife.getLineColFunc('the html', [0, 0]);
expect(posFunc.bind(this, -10)).to.throw();
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/linter.js
================================================
var knife = require('../../lib/knife');
describe('linter', function () {
var Linter = require('../../lib/linter');
var linter = null;
it('should be a function', function () {
expect(Linter).to.be.an.instanceOf(Function);
});
beforeEach(function () {
linter = new Linter([
{name: 'dom', lint: function () { return []; }},
{name: 'line', lint: function () { return []; }},
require('../../lib/rules/free-options.js')
]);
});
describe('lint', function () {
var ConstRule = require('../fixtures/const_rule');
var rule = new ConstRule([{
msg: 'this is a test',
index: 4,
line: 2,
column: 3
}, {
msg: 'this is a test',
index: 2
}]), output;
it('should output an issue when given a nonexistent option', function (cb) {
linter.lint('f\nfff', { nonopt: 7 }, 'nodefault')
.then(function (output) {
expect(output).to.have.length(1);
}).then(cb, cb);
});
it('should return correct line and column numbers', function (cb) {
linter.rules.addRule(rule);
linter.lint('f\nfff', 'nodefault')
.then(function (output) {
expect(output[0].line).to.be.eql(2);
expect(output[0].column).to.be.eql(3);
}).then(cb, cb);
});
it('should not truncate output if maxerr is -1', function (cb) {
linter.rules.addRule(rule);
linter.lint('f\nfff', { maxerr: -1 }, 'nodefault')
.then(function (output) {
expect(output).to.have.length(2);
}).then(cb, cb);
});
it('should not return more than the maxerr', function (cb) {
linter.rules.addRule(rule);
linter.lint('f\nfff', { maxerr: 1 }, 'nodefault')
.then(function (output) {
expect(output).to.have.length(1);
}).then(cb, cb);
});
it('should output an issue for non-integer maxerr', function (cb) {
linter.lint('', { maxerr: 'five' }, 'nodefault')
.then(function (output) {
expect(output).to.have.length(1);
}).then(cb, cb);
});
});
describe('resetRules', function () {
it('should return an array of issues', function () {
var issue = { msg: 'hit' };
linter.rules.addRule({
end: function () { return issue; }
});
var output = linter.resetRules();
expect(output[0]).to.be.eql(issue);
});
});
// TODO: move these out of this file...
describe('shred', function () {
it('should return an array', function () {
var output = knife.shred('');
expect(output).to.be.an.instanceOf(Array);
});
it('should return the proper number of lines', function () {
var lines = [
'Line1Line1Line1Line1',
'Line2Line2Line2Line2',
'Line3Line3Line3Line3'
];
var output = knife.shred(lines.join('\n').concat('\n'));
expect(output.length - 1).to.be.eql(lines.length);
});
it('should return the full line at the right index', function () {
var lines = [
'Line1Line1Line1Line1',
'Line2Line2Line2Line2',
'Line3Line3Line3Line3'
];
var concatted = lines.join('\n').concat('\n');
var output = knife.shred(concatted);
expect(output[lines.length].line).to.be.eql(lines[lines.length - 1].concat('\n'));
expect(output[lines.length].index).to.be.eql(concatted.indexOf(lines[lines.length - 1].concat('\n')));
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/messages.js
================================================
describe('messages', function () {
var messages = require('../../lib/messages');
describe('renderMsg', function () {
it('should return a string', function () {
var code = 'E000', data = {};
var output = messages.renderMsg(code, data);
expect(output).to.be.eql('not a valid error code');
});
});
describe('renderIssue', function () {
it('should return a string', function () {
var issue = { code: 'E000', data: {} };
var output = messages.renderIssue(issue);
expect(output).to.be.eql('not a valid error code');
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/parser.dom_builder.js
================================================
describe('parser.dom_builder', function () {
var DomBuilder = require('../../lib/parser/dom_builder'),
DomHandler = require('htmlparser2').DomHandler;
it('should be a constructor', function () {
expect(DomBuilder).to.be.an.instanceOf(Function);
});
it('should inherit from DomHandler', function () {
expect(DomBuilder.prototype).to.be.an.instanceOf(DomHandler);
});
it('should throw on addDomElement if not initialized with a parser', function () {
var builder = new DomBuilder();
/*eslint-disable no-underscore-dangle*/
expect(builder._addDomElement.bind(builder)).to.throw(Error);
/*eslint-enable no-underscore-dangle*/
});
describe('onerror', function () {
it('should throw the error', function () {
var builder = new DomBuilder();
expect(function () {
builder.onerror('error');
}).to.throw();
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/parser.js
================================================
describe('linter', function () {
var Parser = require('../../lib/parser');
it('should be a function', function () {
expect(Parser).to.be.an.instanceOf(Function);
});
describe('parse', function () {
var parser = null;
beforeEach(function () {
parser = new Parser();
});
it('should return correct line and column numbers', function () {
var output = parser.parse([
'\n'
,'
\n'
,' TextTextText\n'
,'
\n'
,'\n'
].join(''));
expect(output[0].openLineCol).to.be.eql([1,1]);
expect(output[0].closeLineCol).to.be.eql([5,1]);
expect(output[0].children[1].openLineCol).to.be.eql([2,3]);
expect(output[0].children[1].closeLineCol).to.be.eql([4,3]);
});
});
describe('onattribute', function () {
var parser = null;
beforeEach(function () {
parser = new Parser();
});
it('should correctly return an array of duplicates', function () {
var output = parser.parse([
'\n'
,'
\n'
,'
\n'
,'\n'
].join(''));
expect(output[0].children[1].dupes).to.be.eql(['class']);
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/raw-ignore-regex.js
================================================
var lodash = require('lodash'),
Linter = require('../../lib/linter'),
Issue = require('../../lib/issue');
// Each E will trigger an error. Lowercase letters are targets for
// raw-ignore-regex.
var defaultHtml = [
'E abb E ', // 1, 8
'c\tE ', // 3
'bEbEba E' // 2, 4, 8
].join('\n') + '\n';
var errors = [
[1,1], [1,8],
[2,3],
[3,2], [3,4], [3,8]
];
function meetExpectations(issues, locs) {
if (issues.length !== locs.length) {
return false;
}
for (var i = 0; i < locs.length; i++) {
if (issues[i].line !== locs[i][0] ||
issues[i].column !== locs[i][1]) {
return false;
}
}
return true;
}
var linter = null;
function expectOutput(ignore, expected, html) {
html = html || defaultHtml;
return linter.lint(html, { 'raw-ignore-regex': ignore }, 'nodefault')
.then(function (output) {
var pos = output.map(
function(issue){return [issue.line,issue.column];}
);
expect(pos).to.be.eql(expected);
});
}
describe('raw-ignore-regex', function () {
function findE(lines) {
var issues = [];
lines.forEach(function (line) {
var regex = /E/gm,
match;
while (match = regex.exec(line.line)) {
issues.push(new Issue('EE', [line.row, match.index + 1]));
}
});
return issues;
}
beforeEach(function () {
linter = new Linter([
{name: 'dom', lint: function () { return []; }},
{name: 'line', lint: findE},
require('../../lib/rules/free-options.js')
]);
});
it('should work with no regex given', function () {
return expectOutput(false, errors);
});
it('should correct row and column numbers', function () {
return expectOutput('a', errors);
});
it('should work with multiple ignores per line', function () {
return expectOutput('b', errors);
});
it('should work with tabs', function () {
return expectOutput('c', errors);
});
it('should work with a multi-character ignore', function () {
return expectOutput('abb', errors);
});
it('should work with a multi-line ignore', function () {
return expectOutput('a[^]*a', [errors[0], errors[5]]);
});
it('should work with multiple multi-line ignores', function () {
return expectOutput(
'{[^]*?}',
[[1,1], [3,4], [4,1], [7,4], [7,8]],
'E {\n\n} E\nE\n{ \n \n }E{ }E\n'
);
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/rules.doctype-first.js
================================================
describe('rules.doctype-first', function () {
var rule = require('../../lib/rules/doctype-first');
beforeEach(function () {
rule.end();
});
it('should not validate on a comment', function () {
var comment = { type: 'comment' },
opts = { 'doctype-first': true };
rule.lint(comment, opts);
expect(rule.passedFirst).to.be.eql(false);
});
it('should not validate on spaces', function () {
var spaces = { type: 'text', data: ' \t\n\f\r' },
opts = { 'doctype-first': true };
rule.lint(spaces, opts);
expect(rule.passedFirst).to.be.eql(false);
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/rules.focusable-tabindex-style.js
================================================
describe('rules.focusable-tabindex-style', function () {
var rule = require('../../lib/rules/focusable-tabindex-style');
beforeEach(function () {
rule.end();
});
describe('end', function () {
it('should reset the detected style', function () {
rule.detectedStyle = true;
rule.end();
expect(rule.detectedStyle).to.be.eql(null);
});
});
describe('lint', function () {
it('should return [] when not enabled', function () {
var issues = rule.lint({}, { 'focusable-tabindex-style': false });
expect(issues).to.be.eql([]);
});
});
describe('getTabIndexStyle', function () {
[
{
desc: 'should return null for elements with no tabindex',
element: {
attribs: {}
},
style: false
}, {
desc: 'should return false for elements with tabindex 0',
element: {
attribs: { tabindex: { value: 0 } }
},
style: false
}, {
desc: 'should return false for elements with negative tabindex',
element: {
attribs: { tabindex: { value: -1 } }
},
style: false
}, {
desc: 'should return true for elements with positive tabindex',
element: {
attribs: { tabindex: { value: 1 } }
},
style: true
}
].forEach(function (testCase) {
it(testCase.desc, function () {
var style = rule.getTabIndexStyle(testCase.element);
expect(style).to.be.eql(testCase.style);
});
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/rules.js
================================================
describe('htmllint.rules', function () {
var rules = require('../../').rules;
rules.forEach(function (rule) {
describe(rule.name, function () {
it('should have a name', function () {
expect(rule).to.have.property('name');
});
if (rule.name === 'free-options') return;
it('should have a lint function', function () {
expect(rule).to.have.property('lint');
});
if (['line', 'dom'].indexOf(rule.name) === -1) {
it('should subscribe to something', function () {
expect(rule).to.have.property('on');
expect(rule.on.length > 0).to.equal(true);
});
}
});
});
});
================================================
FILE: packages/cml-htmllinter/test/unit/runner.test.js
================================================
const fs = require('fs');
const path = require('path');
global.chai = require('chai');
global.chai.use(require('chai-as-promised'));
global.expect = global.chai.expect;
var testFiles = fs.readdirSync(__dirname)
.filter(function (filepath) {
return (path.extname(filepath) === '.js' &&
path.basename(filepath) !== 'runner.test.js');
})
.map(function (filepath) {
var basename = path.basename(filepath, '.js');
return {
name: basename,
mod: require('./' + basename)
};
});
================================================
FILE: packages/cml-interface-parser/.eslintrc
================================================
{
"parser": "babel-eslint",
"env": {
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"globalReturn": true,
"impliedStrict": true,
"jsx": true,
"modules": true
}
},
"extends": [
"eslint:recommended"
],
"rules": {
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 2,
"no-control-regex": 2,
"comma-dangle": [1, "never"],
"no-debugger": 2,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 2,
"no-empty-character-class": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-func-assign": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-negated-in-lhs": 2,
"no-obj-calls": 2,
"no-prototype-builtins": 0,
"no-regex-spaces": 2,
"no-sparse-arrays": 2,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"accessor-pairs": 2,
"array-callback-return": 0,
"block-scoped-var": 0,
"complexity": [2, 20],
"consistent-return": 0,
"curly": [2, "all"],
"default-case": 2,
"dot-location": [2, "property"],
"dot-notation": [2, {
"allowKeywords": false
}],
"eqeqeq": [0, "allow-null"],
"guard-for-in": 0,
"no-alert": 0,
"no-caller": 2,
"no-case-declarations": 2,
"no-div-regex": 2,
"no-else-return": 0,
"no-empty-function": 2,
"no-empty-pattern": 2,
"no-eq-null": 1,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-label:": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implicit-coercion": 0,
"no-implicit-globals": 1,
"no-implied-eval": 2,
"no-invalid-this": 0,
"no-iterator": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-loop-func": 0,
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-new": 2,
"no-new-func": 0,
"no-new-wrappers": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-return-assign": 0,
"no-script-url": 0,
"no-self-assign": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-unmodified-loop-condition": 2,
"no-unused-expressions": 0,
"no-unused-labels": 2,
"no-useless-call": 2,
"no-useless-concat": 0,
"no-useless-escape": 0,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"radix": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"yoda": [2, "never"],
"strict": 0,
"init-declarations": 0,
"no-catch-shadow": 0,
"no-delete-var": 2,
"no-label-var": 2,
"no-restricted-globals": 0,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
"no-use-before-define": 0,
"callback-return": 0,
"global-require": 0,
"handle-callback-err": [2, "^(err|error)$"],
"no-mixed-requires": 0,
"no-new-require": 2,
"no-path-concat": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-sync": 0,
"array-bracket-spacing": [2, "never"],
"block-spacing": [1, "never"],
"brace-style": [0, "1tbs", {
"allowSingleLine": true
}],
"camelcase": 2,
"comma-spacing": [2, {
"before": false,
"after": true
}],
"comma-style": [2, "last"],
"computed-property-spacing": [2, "never"],
"consistent-this": [1, "that"],
"func-names": 0,
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
"linebreak-style": [1, "unix"],
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
"func-style": 0,
"max-nested-callbacks": [1, 5],
"id-blacklist": 0,
"id-length": 0,
"id-match": 0,
"jsx-quotes": 0,
"keyword-spacing": 2,
"max-len": [1, 200],
"max-lines": 0,
"max-params": [1, 7],
"max-statements": [1, 200],
"max-statements-per-line": 0,
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
"new-parens": 2,
"newline-after-var": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"newline-before-return": 0,
"newline-per-chained-call": 1,
"no-continue": 0,
"no-inline-comments": 0,
"no-lonely-if": 0,
"no-mixed-operators": 0,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": [2, {
"max": 2
}],
"no-negated-condition": 0,
"no-nested-ternary": 0,
"no-new-object": 2,
"no-plusplus": 0,
"no-restricted-syntax": 0,
"no-spaced-func": 2,
"no-ternary": 0,
"no-trailing-spaces": 2,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 2,
"no-whitespace-before-property": 0,
"object-curly-newline": 0,
"object-curly-spacing": 0,
"object-property-newline": 0,
"one-var": [2, {
"initialized": "never"
}],
"one-var-declaration-per-line": 0,
"operator-assignment": 0,
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"require-jsdoc": 0,
"semi": [0, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [0, "always"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
"unicode-bom": 0,
"wrap-regex": 0,
"arrow-body-style": 0,
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
"constructor-super": 0,
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
"no-class-assign": 2,
"no-confusing-arrow": 0,
"no-const-assign": 2,
"no-dupe-class-members": 2,
"no-duplicate-imports": 0,
"no-new-symbol": 2,
"no-restricted-imports": 0,
"no-this-before-super": 2,
"no-useless-computed-key": 0,
"no-var": 0,
"object-shorthand": 0,
"prefer-arrow-callback": 0,
"prefer-const": 0,
"prefer-reflect": 0,
"prefer-spread": 0,
"prefer-template": 0,
"prefer-rest-params": 0,
"require-yield": 0,
"rest-spread-spacing": 0,
"sort-imports": 0,
"template-curly-spacing": 1,
"yield-star-spacing": 2
}
}
================================================
FILE: packages/cml-interface-parser/.gitignore
================================================
.vscode
================================================
FILE: packages/cml-interface-parser/config/babel-parser-config.js
================================================
module.exports = {
sourceType: 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
ranges: true,
tokens: true,
plugins: [
'flow',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'doExpressions',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'throwExpressions'
]
}
================================================
FILE: packages/cml-interface-parser/index.js
================================================
const parserConfig = require('./config/babel-parser-config');
const babelParser = require('@babel/parser');
const astTreeParser = require('./src/ast-tree-parser');
const fileReader = require('./src/file-reader');
class InterfaceParser {
/**
* Constructor
* @param {Object} {
* filePath, // file that contains javascript context you wanna to parse.
* astTree // an ast tree object got from bable parser.
* }
*/
constructor({filePath = null, astTree = null}, options = null, currentWorkspace = '') {
this._astTree = null;
this._currentWorkspace = currentWorkspace;
if (filePath) {
this._astTree = this.getAstTreeFromFile(filePath);
}
if (astTree) {
this._astTree = astTree;
}
if (options) {
this._options = options;
}
}
getAstTreeFromFile(filePath) {
let content = fileReader.getContent(filePath, this._currentWorkspace);
let astTree = null;
try {
astTree = babelParser.parse(content, this._options || parserConfig);
} catch (err) {
console.error(err);
}
return astTree;
}
getParseResults() {
let results = {vars: [], methods: [], props: [], events: []};
if (this._astTree) {
results = astTreeParser.parse(this._astTree);
}
return results;
}
}
module.exports = InterfaceParser;
================================================
FILE: packages/cml-interface-parser/package.json
================================================
{
"name": "cml-interface-parser",
"version": "1.0.8",
"description": "A tool that can parse interface file and return all properties and methods it has.",
"main": "index.js",
"scripts": {
"test": "nyc ./node_modules/mocha/bin/mocha ./test/*.js",
"test-brk": "./node_modules/mocha/bin/mocha --inspect-brk ./test/*.js"
},
"mail": "ChameleonCore@didiglobal.com",
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"@babel/parser": "^7.1.3",
"@babel/traverse": "^7.2.2",
"chameleon-tool-utils": "1.0.8",
"commander": "^2.19.0"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^5.2.0",
"nyc": "^13.1.0"
},
"gitHead": "5ddcde4330774710f7646559446e008f7785ce00"
}
================================================
FILE: packages/cml-interface-parser/src/ast-tree-parser.js
================================================
const genericTypes = ['string', 'number', 'object', 'boolean', 'void', 'null'];
const genericAnnotationTypes = ['ObjectTypeAnnotation'];
const composedAnnotationTypes = ['TupleTypeAnnotation', 'UnionTypeAnnotation', 'IntersectionTypeAnnotation'];
function getTypeAliasInfo(bodyItem = {}) {
let aliasInfo = {};
aliasInfo.typeName = bodyItem.id.name;
aliasInfo.flowType = bodyItem.right.type;
if (~genericAnnotationTypes.indexOf(bodyItem.right.type) && bodyItem.right && bodyItem.right.properties) {
aliasInfo.props = bodyItem.right.properties.map((prop) => {
return {
name: prop.key.name,
valueType: prop.value.id.name
};
});
} else if (~composedAnnotationTypes.indexOf(bodyItem.right.type)) {
aliasInfo.props = bodyItem.right.types.map((typeItem) => {
return {
name: typeItem.id.name, valueType: typeItem.id.name
};
});
} else {
aliasInfo.props = [];
}
return aliasInfo;
}
function getTypeAliasChain(typeDefinations, propItem) {
let _savedTypeChain = [];
travelTypes(typeDefinations, propItem);
return _savedTypeChain;
function travelTypes(typeDefinations, propItem) {
if (typeDefinations && propItem) {
propItem.props.forEach((prop) => {
let nextProp = typeDefinations[prop.valueType];
nextProp && _savedTypeChain.push(nextProp) && travelTypes(typeDefinations, nextProp);
});
}
}
}
function parse(astTree) {
let typeDefinations = {}; let results = {vars: [], methods: [], props: [], events: []};
astTree && astTree.type === 'File' && astTree.program.body.filter((bodyItem) => {
return ['TypeAlias', 'InterfaceDeclaration'].indexOf(bodyItem.type) > -1;
}).forEach((bodyItem) => {
if (bodyItem.type === 'TypeAlias') {
bodyItem._typeAliasInfo = getTypeAliasInfo(bodyItem);
bodyItem._typeAliasInfo.typeName && (typeDefinations[bodyItem._typeAliasInfo.typeName] = bodyItem._typeAliasInfo);
delete bodyItem._typeAliasInfo;
}
(bodyItem.type === 'InterfaceDeclaration') && bodyItem.body.properties.forEach((propertyItem) => {
if (propertyItem.method) {
results.methods.push(propertyItem.key.name);
results.events.push({
name: propertyItem.key.name,
paramsNum: propertyItem.value.params.length,
params: propertyItem.value.params ? propertyItem.value.params.filter((param) => {
return param.typeAnnotation.type === 'GenericTypeAnnotation';
}).map((param) => {
let typeParam = typeDefinations[param.typeAnnotation.id.name] || {typeName: param.typeAnnotation.id.name};
typeParam.varName = param.name.name;
if (typeParam.props) {typeParam.typeChain = getTypeAliasChain(typeDefinations, typeParam);}
return typeParam;
}) : []
});
} else {
results.vars.push(propertyItem.key.name);
propertyItem._formattedPropertyItem = {
name: propertyItem.key.name,
valueType: propertyItem.value.id.name,
props: (genericTypes.indexOf(propertyItem.value.id.name.toLowerCase()) === -1) ? [{
name: propertyItem.value.id.name, valueType: propertyItem.value.id.name
}] : []
};
propertyItem._formattedPropertyItem.typeChain = getTypeAliasChain(typeDefinations, propertyItem._formattedPropertyItem);
results.props.push(propertyItem._formattedPropertyItem);
delete propertyItem._formattedPropertyItem;
}
});
});
return results;
}
module.exports = {
parse
};
================================================
FILE: packages/cml-interface-parser/src/file-reader.js
================================================
const fs = require('fs');
const path = require('path');
const cliUtils = require('chameleon-tool-utils');
const partRegExp = /<\s*(script)\s*([^>]*?)\s*>([\s\S]*?)<\s*\/\s*\1\s*>/g;
const paramRegExp = /([^\s\=]+)=?(['"])?([^\s\=\'\"]*)\2/g;
function _retrieveInterfaceContent(filePath = null, currentWorkspace = '') {
let fileContent = '';
let splitParts = {};
let include = null;
try {
fileContent = fs.readFileSync(filePath, 'utf8');
} catch (err) {
// console.warn("cml-interface-parser:", err.message);
}
if (fileContent) {
splitParts = cliUtils.splitParts({ content: fileContent });
}
if (splitParts.customBlocks && splitParts.customBlocks.length) {
splitParts.customBlocks.forEach(part => {
if (part && (part.type === 'include')) {
include = part;
}
});
}
if (include && include.attrs && include.attrs.src) {
let nextPath = path.resolve(path.dirname(filePath), include.attrs.src);
if (currentWorkspace) {
let pathObj = path.parse(include.attrs.src);
pathObj.base = pathObj.name;
nextPath = cliUtils.findInterfaceFile(currentWorkspace, filePath, path.format(pathObj));
if (nextPath.filePath) {
nextPath = nextPath.filePath;
}
}
return _retrieveInterfaceContent(nextPath, currentWorkspace);
}
return fileContent;
}
function getContent(filePath = null, currentWorkspace = '') {
let fileRawContent = ''; let interfaceContent = '';
fileRawContent = _retrieveInterfaceContent(filePath, currentWorkspace);
fileRawContent.replace(partRegExp, (match, type, rawAttribute, definationContent) => {
!interfaceContent && rawAttribute.replace(paramRegExp, (attrMatch, attrName, mark, attrValue) => {
if (attrName === 'cml-type' && attrValue === 'interface') {
interfaceContent = definationContent;
}
});
});
return interfaceContent;
}
module.exports = {
getContent
}
================================================
FILE: packages/cml-interface-parser/test/docs/include-interface.interface
================================================
================================================
FILE: packages/cml-interface-parser/test/docs/index.interface
================================================
================================================
FILE: packages/cml-interface-parser/test/test.js
================================================
const path = require('path');
const chai = require('chai');
const expect = chai.expect;
const Parser = require('../index');
describe('parser check', function() {
it('normal interface file: should pass check', function() {
let parser = new Parser({filePath: path.resolve(__dirname, './docs/index.interface')});
let results = parser.getParseResults();
expect(results).to.have.deep.property('vars', ['cstyle', 'bottomOffset', 'scrollDirection']);
expect(results).to.have.deep.property('methods', ['customscroll', 'scrolltobottom']);
expect(results).to.have.deep.property('props', [{
name: 'cstyle', valueType: 'String', props: [], typeChain: []
}, {
name: 'bottomOffset', valueType: 'Number', props: [], typeChain: []
}, {
name: 'scrollDirection', valueType: 'String', props: [], typeChain: []
}]);
});
it('include interface file: should pass check', function() {
let parser = new Parser({filePath: path.resolve(__dirname, './docs/include-interface.interface')});
let results = parser.getParseResults();
expect(results).to.have.deep.property('vars', ['cstyle', 'bottomOffset', 'scrollDirection']);
expect(results).to.have.deep.property('methods', ['customscroll', 'scrolltobottom']);
expect(results).to.have.deep.property('props', [{
name: 'cstyle', valueType: 'String', props: [], typeChain: []
}, {
name: 'bottomOffset', valueType: 'Number', props: [], typeChain: []
}, {
name: 'scrollDirection', valueType: 'String', props: [], typeChain: []
}]);
});
});
================================================
FILE: packages/cml-js-parser/.eslintrc
================================================
{
"parser": "babel-eslint",
"env": {
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"globalReturn": true,
"impliedStrict": true,
"jsx": true,
"modules": true
}
},
"extends": [
"eslint:recommended"
],
"rules": {
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 2,
"no-control-regex": 2,
"comma-dangle": [1, "never"],
"no-debugger": 2,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 2,
"no-empty-character-class": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-func-assign": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-negated-in-lhs": 2,
"no-obj-calls": 2,
"no-prototype-builtins": 0,
"no-regex-spaces": 2,
"no-sparse-arrays": 2,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"accessor-pairs": 2,
"array-callback-return": 0,
"block-scoped-var": 0,
"complexity": [2, 20],
"consistent-return": 0,
"curly": [2, "all"],
"default-case": 2,
"dot-location": [2, "property"],
"dot-notation": [2, {
"allowKeywords": false
}],
"eqeqeq": [0, "allow-null"],
"guard-for-in": 0,
"no-alert": 0,
"no-caller": 2,
"no-case-declarations": 2,
"no-div-regex": 2,
"no-else-return": 0,
"no-empty-function": 2,
"no-empty-pattern": 2,
"no-eq-null": 1,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-label:": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implicit-coercion": 0,
"no-implicit-globals": 1,
"no-implied-eval": 2,
"no-invalid-this": 0,
"no-iterator": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-loop-func": 0,
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-new": 2,
"no-new-func": 0,
"no-new-wrappers": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-param-reassign": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-return-assign": 0,
"no-script-url": 0,
"no-self-assign": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-unmodified-loop-condition": 2,
"no-unused-expressions": 0,
"no-unused-labels": 2,
"no-useless-call": 2,
"no-useless-concat": 0,
"no-useless-escape": 0,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"radix": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"yoda": [2, "never"],
"strict": 0,
"init-declarations": 0,
"no-catch-shadow": 0,
"no-delete-var": 2,
"no-label-var": 2,
"no-restricted-globals": 0,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
"no-use-before-define": 0,
"callback-return": 0,
"global-require": 0,
"handle-callback-err": [2, "^(err|error)$"],
"no-mixed-requires": 0,
"no-new-require": 2,
"no-path-concat": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-sync": 0,
"array-bracket-spacing": [2, "never"],
"block-spacing": [1, "never"],
"brace-style": [0, "1tbs", {
"allowSingleLine": true
}],
"camelcase": 2,
"comma-spacing": [2, {
"before": false,
"after": true
}],
"comma-style": [2, "last"],
"computed-property-spacing": [2, "never"],
"consistent-this": [1, "that"],
"func-names": 0,
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
"linebreak-style": [1, "unix"],
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
"func-style": 0,
"max-nested-callbacks": [1, 5],
"id-blacklist": 0,
"id-length": 0,
"id-match": 0,
"jsx-quotes": 0,
"keyword-spacing": 2,
"max-len": [1, 200],
"max-lines": 0,
"max-params": [1, 7],
"max-statements": [1, 200],
"max-statements-per-line": 0,
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
"new-parens": 2,
"newline-after-var": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"newline-before-return": 0,
"newline-per-chained-call": 1,
"no-continue": 0,
"no-inline-comments": 0,
"no-lonely-if": 0,
"no-mixed-operators": 0,
"no-mixed-spaces-and-tabs": 2,
"no-multiple-empty-lines": [2, {
"max": 2
}],
"no-negated-condition": 0,
"no-nested-ternary": 0,
"no-new-object": 2,
"no-plusplus": 0,
"no-restricted-syntax": 0,
"no-spaced-func": 2,
"no-ternary": 0,
"no-trailing-spaces": 2,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 2,
"no-whitespace-before-property": 0,
"object-curly-newline": 0,
"object-curly-spacing": 0,
"object-property-newline": 0,
"one-var": [2, {
"initialized": "never"
}],
"one-var-declaration-per-line": 0,
"operator-assignment": 0,
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
"padded-blocks": 0,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"require-jsdoc": 0,
"semi": [0, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [0, "always"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
"unicode-bom": 0,
"wrap-regex": 0,
"arrow-body-style": 0,
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
"constructor-super": 0,
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
"no-class-assign": 2,
"no-confusing-arrow": 0,
"no-const-assign": 2,
"no-dupe-class-members": 2,
"no-duplicate-imports": 0,
"no-new-symbol": 2,
"no-restricted-imports": 0,
"no-this-before-super": 2,
"no-useless-computed-key": 0,
"no-var": 0,
"object-shorthand": 0,
"prefer-arrow-callback": 0,
"prefer-const": 0,
"prefer-reflect": 0,
"prefer-spread": 0,
"prefer-template": 0,
"prefer-rest-params": 0,
"require-yield": 0,
"rest-spread-spacing": 0,
"sort-imports": 0,
"template-curly-spacing": 1,
"yield-star-spacing": 2
}
}
================================================
FILE: packages/cml-js-parser/.gitignore
================================================
demo/
================================================
FILE: packages/cml-js-parser/config/babel-parser-config.js
================================================
module.exports = {
sourceType: 'module',
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
ranges: true,
tokens: true,
plugins: [
'flow',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'doExpressions',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'throwExpressions'
]
}
================================================
FILE: packages/cml-js-parser/index.js
================================================
const parserConfig = require('./config/babel-parser-config');
const babelParser = require('@babel/parser');
const traverse = require('@babel/traverse')['default'];
const visitors = require('./src/visitors');
const fileReader = require('./src/file-reader');
class CmlJSParser {
/**
* Constructor
* @param {Object} {
* filePath, // file that contains javascript context you wanna to parse.
* astTree // an ast tree object got from bable parser.
* }
*/
constructor({filePath = null, astTree = null}, options = null) {
this._astTree = null;
if (filePath) {
this._astTree = this.getAstTreeFromFile(filePath);
}
if (astTree) {
this._astTree = astTree;
}
if (options) {
this._options = options;
}
}
getAstTreeFromFile(filePath) {
let content = fileReader.getContent(filePath);
let astTree = null;
try {
astTree = babelParser.parse(content, this._options || parserConfig);
} catch (err) {
console.error(err);
}
return astTree;
}
getParseResults() {
let results = {vars: [], methods: [], props: [], events: []};
if (this._astTree) {
traverse(this._astTree, {
ExportDefaultDeclaration(path) {
let containerPath = visitors.exportPathVisitor(path);
if (containerPath) {
results = visitors.containerPathVisitor(containerPath);
}
}
});
}
return results;
}
}
module.exports = CmlJSParser;
================================================
FILE: packages/cml-js-parser/package.json
================================================
{
"name": "cml-js-parser",
"version": "1.0.8",
"description": "A tool used to get variable names and methods that can be used in templates.",
"main": "index.js",
"scripts": {
"test": "nyc ./node_modules/mocha/bin/mocha ./test/*.js",
"test-brk": "./node_modules/mocha/bin/mocha --inspect-brk ./test/*.js"
},
"mail": "ChameleonCore@didiglobal.com",
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"@babel/parser": "^7.2.2",
"@babel/traverse": "^7.2.2",
"bulk-require": "^1.0.1",
"chameleon-tool-utils": "1.0.8"
},
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^5.2.0",
"nyc": "^13.1.0"
},
"gitHead": "5ddcde4330774710f7646559446e008f7785ce00"
}
================================================
FILE: packages/cml-js-parser/src/file-reader.js
================================================
const fs = require('fs');
const cliUtils = require('chameleon-tool-utils');
function getContent(filePath = null) {
let fileRawContent = ''; let parts = {}; let scriptContent = '';
try {
filePath && (fileRawContent = fs.readFileSync(filePath, 'utf8'));
} catch (err) {
// console.warn("cml-interface-parser:", err.message);
}
parts = cliUtils.splitParts({content: fileRawContent});
if (parts.script && parts.script.length) {
scriptContent = parts.script.filter((item) => Object.keys(item.attrs).length === 0).map((item) => {
return item.content;
});
}
return scriptContent[0] || '';
}
module.exports = {
getContent
}
================================================
FILE: packages/cml-js-parser/src/visitors/containerPathVisitor.js
================================================
function getObjectKeyName(node) {
return node.key.name;
}
function isValidProp(node) {
const validKeys = ['props', 'data', 'computed', 'methods'];
let keyName = getObjectKeyName(node);
let idx = validKeys.indexOf(keyName);
return ~idx ? validKeys[idx] : '';
}
function adoptPropResults(res, container) {
res.forEach((prop) => {
// fillKey is one of 'vars', 'methods', 'props' or 'events'
container[prop.fillKey].push(prop.value);
});
}
const propFuns = {
props: function(path) {
let props = [];
path.get('value').get('properties')
.forEach((prop) => {
if (prop.isObjectProperty()) {
let propName = getObjectKeyName(prop.node);
props.push({
fillKey: 'vars',
value: propName
});
if (prop.get('value').isObjectExpression()) {
let valueInfo = {};
prop.get('value').get('properties')
.forEach((childProp) => {
let childName = getObjectKeyName(childProp.node);
if (childName === 'type') {
valueInfo[childName] = childProp.get('value').isIdentifier() ? childProp.node.value.name : '';
}
if (childName === 'default') {
if (childProp.isObjectProperty()) {
valueInfo[childName] = ~childProp.node.value.type.indexOf('Literal') ? (childProp.node.value.value || '') : '';
} else {
valueInfo[childName] = '';
}
}
if (childName === 'required') {
valueInfo[childName] = childProp.get('value').isBooleanLiteral() ? childProp.node.value.value : false;
}
});
props.push({
fillKey: 'props',
value: {
name: propName,
valueType: valueInfo.type || '',
required: !!valueInfo.required || false,
default: valueInfo['default']
}
});
}
if (prop.get('value').isIdentifier()) {
props.push({
fillKey: 'props',
value: {
name: propName,
valueType: prop.node.value.name,
required: false,
default: ''
}
});
}
}
});
return props;
},
data: function(path) {
let props = [];
path.get('value').get('properties')
.forEach((prop) => {
if (prop.isObjectProperty()) {
props.push({
fillKey: 'vars',
value: getObjectKeyName(prop.node)
});
}
});
return props;
},
computed: function(path) {
let props = [];
path.get('value').get('properties')
.forEach((prop) => {
if (prop.isObjectMethod()) {
props.push({
fillKey: 'vars',
value: getObjectKeyName(prop.node)
});
}
if (prop.isObjectProperty() && prop.get('value').isFunctionExpression()) {
props.push({
fillKey: 'vars',
value: getObjectKeyName(prop.node)
});
}
if (prop.isSpreadElement()) {
prop.get('argument').isCallExpression() && prop.get('argument').get('arguments')
.forEach((argItem) => {
argItem.isObjectExpression() && argItem.get('properties').forEach((argProp) => {
props.push({
fillKey: 'vars',
value: getObjectKeyName(argProp.node)
});
});
});
}
});
return props;
},
methods: function(path) {
let props = [];
path.get('value').get('properties')
.forEach((prop) => {
if (prop.isObjectMethod()) {
props.push({
fillKey: 'methods',
value: getObjectKeyName(prop.node)
});
}
if (prop.isObjectProperty() && prop.get('value').isFunctionExpression()) {
props.push({
fillKey: 'methods',
value: getObjectKeyName(prop.node)
});
}
});
return props;
}
};
module.exports.containerPathVisitor = function(path) {
let results = {vars: [], methods: [], props: [], events: []};
path && path.traverse({
'ClassProperty|ObjectProperty'(path) {
let propName = isValidProp(path.node);
if (propName && path.get('value').isObjectExpression()) {
adoptPropResults(propFuns[propName](path), results);
}
},
MemberExpression(path) {
if (!path.node.computed && path.get('object').isThisExpression() && path.get('property').isIdentifier()) {
if (path.node.property.name === '$cmlEmit') {
let parentNode = path.findParent(path => path.isCallExpression());
if (parentNode && parentNode.get('arguments')) {
let nameArg = parentNode.get('arguments')[0];
if (nameArg.isStringLiteral()) {
results.events.push({
name: parentNode.get('arguments')[0].node.value,
paramsNum: -1,
params: []
});
} else if (nameArg.isIdentifier()) {
let argBinding = nameArg.scope.getBinding(nameArg.node.name);
let possibleInit = argBinding ? argBinding.path.node.init : null;
// For now, we only check just one jump along its scope chain.
if (possibleInit && possibleInit.type === 'StringLiteral') {
results.events.push({
name: possibleInit.value,
paramsNum: -1,
params: []
});
}
}
}
}
}
}
});
return results;
}
================================================
FILE: packages/cml-js-parser/src/visitors/exportPathVisitor.js
================================================
module.exports.exportPathVisitor = function(path) {
let declarationPath = path.get('declaration');
if (declarationPath.isObjectExpression()) {
return declarationPath;
}
if (declarationPath.isNewExpression()) {
let className = declarationPath.get('callee').isIdentifier() ? declarationPath.node.callee.name : '';
let container = path.container;
return className && container.map((eleNode, index) => {
return path.getSibling(index);
}).filter((elePath) => {
return elePath.node.type === 'ClassDeclaration' && elePath.node.id.name === className;
})[0];
}
}
================================================
FILE: packages/cml-js-parser/src/visitors/index.js
================================================
const bulk = require('bulk-require');
const visitorExports = bulk(__dirname, '!(index).js');
let visitors = {};
Object.values(visitorExports).forEach(visitor => {
Object.assign(visitors, visitor);
});
module.exports = visitors;
================================================
FILE: packages/cml-js-parser/test/docs/export-class.cml
================================================
================================================
FILE: packages/cml-js-parser/test/docs/export-default.cml
================================================
================================================
FILE: packages/cml-js-parser/test/test.js
================================================
const path = require('path');
const chai = require('chai');
const expect = chai.expect;
const Parser = require('../index');
describe('parser check', function() {
it('should pass export class check', function() {
let parser = new Parser({filePath: path.resolve(__dirname, './docs/export-class.cml')});
let results = parser.getParseResults();
expect(results).to.have.deep.property('vars', ['propOne', 'propTwo', 'dataOne', 'dataTwo', 'computedOne', 'computedTwo'], 'failed at checking vars');
expect(results).to.have.deep.property('methods', ['onTap', 'onClick'], 'failed on checking methods');
expect(results).to.have.deep.property('props', [{
name: 'propOne', valueType: 'Object', required: true, default: ''
}, {
name: 'propTwo', valueType: 'Boolean', required: false, default: true
}], 'failed at checking props');
expect(results).to.have.deep.property('events', [{
name: 'eventOne', paramsNum: -1, params: []
}]);
});
it('should pass export default check', function() {
let parser = new Parser({filePath: path.resolve(__dirname, './docs/export-default.cml')});
let results = parser.getParseResults();
expect(results).to.have.deep.property('vars', ['propOne', 'propTwo', 'dataOne', 'dataTwo', 'computedOne', 'computedTwo', 'computedShow', 'getterHide'], 'failed at checking vars');
expect(results).to.have.deep.property('methods', ['onTap', 'onClick'], 'failed on checking methods');
expect(results).to.have.deep.property('props', [{
name: 'propOne', valueType: 'Object', required: true, default: ''
}, {
name: 'propTwo', valueType: 'Boolean', required: false, default: true
}], 'failed at checking props');
expect(results).to.have.deep.property('events', [{
name: 'eventOne', paramsNum: -1, params: []
}, {
name: 'topevent', paramsNum: -1, params: []
}]);
});
});
================================================
FILE: packages/cml-vue-loader/.gitignore
================================================
.options-cache-*
================================================
FILE: packages/cml-vue-loader/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015-present Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: packages/cml-vue-loader/README.md
================================================
- 基于vue-loader@14.2.3改造
- css的插入顺序有bug,不采用cml-extract-css-webpack-plugin是
- injectStyle方法在Vue实例的beforeCreated阶段插入导致父组件先插入
- 再插入子组件,所有css也是子组件在后,子组件的优先级高
- 解决办法是在执行模块时就执行injectStyle,模块依赖的组件都是import,import优先执行,就会比本组件的injectStyle之前先执行
================================================
FILE: packages/cml-vue-loader/index.js
================================================
module.exports = require('./lib/loader')
================================================
FILE: packages/cml-vue-loader/lib/helpers.js
================================================
const querystring = require('querystring')
const loaderUtils = require('loader-utils')
const normalize = require('./utils/normalize')
const styleCompilerPath = normalize.lib('style-compiler/index')
// internal lib loaders
const selectorPath = normalize.lib('selector')
const templateCompilerPath = normalize.lib('template-compiler/index')
const templatePreprocessorPath = normalize.lib('template-compiler/preprocessor')
// dep loaders
const styleLoaderPath = normalize.dep('vue-style-loader')
const hasBabel = true
const hasBuble = false
const rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/
const defaultLang = {
template: 'html',
styles: 'css',
script: 'js'
}
const postcssExtensions = [
'postcss', 'pcss', 'sugarss', 'sss'
]
// When extracting parts from the source vue file, we want to apply the
// loaders chained before vue-loader, but exclude some loaders that simply
// produces side effects such as linting.
function getRawRequest (
{ resource, loaderIndex, loaders },
excludedPreLoaders = /eslint-loader/
) {
return loaderUtils.getRemainingRequest({
resource: resource,
loaderIndex: loaderIndex,
loaders: loaders.filter(loader => !excludedPreLoaders.test(loader.path))
})
}
// sass => sass-loader
// sass-loader => sass-loader
// sass?indentedSyntax!css => sass-loader?indentedSyntax!css-loader
function ensureLoader (lang) {
return lang
.split('!')
.map(loader =>
loader.replace(
/^([\w-]+)(\?.*)?/,
(_, name, query) =>
(/-loader$/.test(name) ? name : name + '-loader') + (query || '')
)
)
.join('!')
}
function ensureBang (loader) {
if (loader.charAt(loader.length - 1) !== '!') {
return loader + '!'
} else {
return loader
}
}
function resolveLoaders (
optionsId,
options,
moduleId,
isProduction,
hasScoped,
hasComment,
hasFunctionalTemplate,
needCssSourceMap
) {
let cssLoaderOptions = ''
if (needCssSourceMap) {
cssLoaderOptions += '?sourceMap'
}
if (isProduction) {
cssLoaderOptions += (cssLoaderOptions ? '&' : '?') + 'minimize'
}
const bubleTemplateOptions = Object.assign({}, options.buble)
bubleTemplateOptions.transforms = Object.assign({}, bubleTemplateOptions.transforms)
bubleTemplateOptions.transforms.stripWithFunctional = hasFunctionalTemplate
const bubleOptions = hasBuble && options.buble
? '?' + JSON.stringify(options.buble)
: ''
const templateCompilerOptions =
'?' +
JSON.stringify({
id: moduleId,
hasScoped,
hasComment,
optionsId,
buble: bubleTemplateOptions
})
const defaultLoaders = {
html: templateCompilerPath + templateCompilerOptions,
css: styleLoaderPath + '!' + 'css-loader' + cssLoaderOptions,
js: hasBuble
? 'buble-loader' + bubleOptions
: hasBabel ? 'babel-loader' : ''
}
return {
defaultLoaders,
loaders: Object.assign({}, defaultLoaders, options.loaders),
preLoaders: options.preLoaders || {},
postLoaders: options.postLoaders || {}
}
}
module.exports = function createHelpers (
optionsId,
loaderContext,
options,
moduleId,
parts,
isProduction,
hasScoped,
hasComment,
hasFunctionalTemplate,
needCssSourceMap
) {
const rawRequest = getRawRequest(loaderContext, options.excludedPreLoaders)
const {
defaultLoaders,
loaders,
preLoaders,
postLoaders
} = resolveLoaders(
optionsId,
options,
moduleId,
isProduction,
hasScoped,
hasComment,
hasFunctionalTemplate,
needCssSourceMap
)
function getRequire (type, part, index, scoped) {
return 'require(' + getRequestString(type, part, index, scoped) + ')'
}
function getImport (type, part, index, scoped) {
return (
'import __vue_' + type + '__ from ' +
getRequestString(type, part, index, scoped)
)
}
function getNamedExports (type, part, index, scoped) {
return (
'export * from ' +
getRequestString(type, part, index, scoped)
)
}
function getRequestString (type, part, index, scoped) {
return loaderUtils.stringifyRequest(
loaderContext,
// disable all configuration loaders
'!!' +
// get loader string for pre-processors
getLoaderString(type, part, index, scoped) +
// select the corresponding part from the vue file
getSelectorString(type, index || 0) +
// the url to the actual vue file, including remaining requests
rawRequest
)
}
function getRequireForSrc (type, impt, scoped) {
return 'require(' + getSrcRequestString(type, impt, scoped) + ')'
}
function getImportForSrc (type, impt, scoped) {
return (
'import __vue_' + type + '__ from ' +
getSrcRequestString(type, impt, scoped)
)
}
function getNamedExportsForSrc (type, impt, scoped) {
return (
'export * from ' +
getSrcRequestString(type, impt, scoped)
)
}
function getSrcRequestString (type, impt, scoped) {
return loaderUtils.stringifyRequest(
loaderContext,
'!!' + getLoaderString(type, impt, -1, scoped) + impt.src
)
}
function addCssModulesToLoader (loader, part, index) {
if (!part.module) return loader
const option = options.cssModules || {}
const DEFAULT_OPTIONS = {
modules: true
}
const OPTIONS = {
localIdentName: '[local]_[hash:base64:8]',
importLoaders: 1
}
return loader.replace(/((?:^|!)css(?:-loader)?)(\?[^!]*)?/, (m, $1, $2) => {
// $1: !css-loader
// $2: ?a=b
const query = loaderUtils.parseQuery($2 || '?')
Object.assign(query, OPTIONS, option, DEFAULT_OPTIONS)
if (index > 0) {
// Note:
// Class name is generated according to its filename.
// Different `)
}
if (parts.style && parts.style[0]) {
let part = parts.style[0];
let lang = part.attrs && part.attrs.lang || 'less';
output += `
var style = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'style', lang, loaders, resourcePath})});
`
}
output += `var template = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'template', loaders, resourcePath})});\n`
output += `var json = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'json', loaders, resourcePath})});\n`
output += `var script = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'script', loaders, resourcePath})});\n`
this._module._nodeType = fileType;
// app添加page依赖
if (fileType === 'app') {
self.addDependency(path.join(context, './src/router.config.json'));
let {routerConfig, hasError} = cmlUtils.getRouterConfig();
if (hasError) {
throw new Error('router.config.json read error')
} else {
routerConfig.routes.forEach(item => {
output += ` require("$PROJECT/src${item.path}.cml").default;`
})
}
// subProject 中的页面
let subProject = cml.config.get().subProject;
if (subProject && subProject.length > 0) {
subProject.forEach(function(item) {
let npmName = cmlUtils.isString(item) ? item : item.npmName;
let npmRouterConfig = cml.utils.readsubProjectRouterConfig(cml.projectRoot, npmName);
npmRouterConfig.routes && npmRouterConfig.routes.forEach(item => {
let cmlFilePath = path.join(cml.projectRoot, 'node_modules', npmName, 'src', item.path + '.cml');
output += `require("${cmlFilePath}").default`;
})
})
}
}
let jsonObject = cmlUtils.getJsonFileContent(resourcePath, cmlType);
let coms = jsonObject.usingComponents = jsonObject.usingComponents || {};
let customComKeys = Object.keys(coms); // 用户自定义组件key
let usingComponentsAndFilePath = {}; // 记录文件依赖的组件名称及文件位置
let usingComponents = prepareParseUsingComponents({loaderContext: self, context, originObj: coms, cmlType});
const isBuildInFile = cmlUtils.isBuildIn(self.resourcePath, cmlType, context);
let buildInComponents = {};
// 内置组件库中的cml文件不进行内置组件的替换
if (!isBuildInFile) {
buildInComponents = cmlUtils.getBuildinComponents(cmlType, context).compileTagMap;
}
let parseTemplate = parts.template && parts.template[0];
let templateContent = (parseTemplate && parseTemplate.content) || '';
let {source: compiledTemplate, usedBuildInTagMap} = templateParse(templateContent, {
buildInComponents, // 对内置组件做替换 并返回用了哪个内置组件
usingComponents // 判断是否是原生组件
});
const currentUsedBuildInTagMap = {};
// currentUsedBuildInTagMap 中 key为 cml-builtin-button
Object.keys(usedBuildInTagMap).forEach(key => {
let value = usedBuildInTagMap[key];
currentUsedBuildInTagMap[value] = key;
})
// 先遍历usingComponents中的
Object.keys(coms).forEach(key => {
let comPath = coms[key];
let splitInfo = comPath.split('?');
comPath = splitInfo[0];
let {filePath, refUrl} = cmlUtils.handleComponentUrl(context, self.resourcePath, comPath, cmlType);
if (filePath) {
coms[key] = refUrl;
usingComponentsAndFilePath[key] = filePath;
// 建立依赖进行编译
output += `
import ${helper.toUpperCase(key)} from "${cmlUtils.handleRelativePath(self.resourcePath, filePath)}"\n`;
} else {
if (coms[key].indexOf('plugin://') !== 0) {
delete coms[key];
cmlUtils.log.error(`can't find component:${coms[key]} in ${self.resourcePath}`);
}
}
})
let npmComponents = cmlUtils.getTargetInsertComponents(self.resourcePath, cmlType, context) || [];
npmComponents = npmComponents.filter(item => {
// 如果是内置组件 选择模板中使用了的组件
if (item.isBuiltin) {
return !!currentUsedBuildInTagMap[item.name];
// 如果是其他的npm库组件 选择与用户自定义名称不同的组件
} else if (!~customComKeys.indexOf(item.name)) {
return true;
}
})
npmComponents.forEach(item => {
output += `import ${helper.toUpperCase(item.name)} from "${cmlUtils.handleRelativePath(self.resourcePath, item.filePath)}" \n`;
usingComponentsAndFilePath[item.name] = item.filePath;
let refPath = cmlUtils.npmRefPathToRelative(item.refPath, self.resourcePath, context);
coms[item.name] = refPath;
})
Object.keys(coms).forEach(key => {
coms[key] = cmlUtils.handleSpecialChar(coms[key])
});
this._compiler._mvvmCmlInfo = this._compiler._mvvmCmlInfo || {};
this._compiler._mvvmCmlInfo[self.resourcePath] = {
compiledTemplate,
usingComponents,
currentUsedBuildInTagMap,
compiledJson: jsonObject,
componentFiles: usingComponentsAndFilePath
}
this._module._cmlExtra = {
componentFiles: usingComponentsAndFilePath
}
return output;
}
================================================
FILE: packages/mvvm-cml-loader/package.json
================================================
{
"name": "mvvm-cml-loader",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"chameleon-loader": "1.0.8",
"chameleon-tool-utils": "1.0.8",
"loader-utils": "1.1.0",
"mvvm-template-parser": "1.0.8"
}
}
================================================
FILE: packages/mvvm-cml-loader/selector.js
================================================
const loaderUtils = require('loader-utils')
const cmlUtils = require('chameleon-tool-utils');
const { getScriptCode } = require('chameleon-loader/src/interface-check/getScriptCode.js');
const helper = require('./helper.js');
module.exports = function(source) {
const self = this;
const rawOptions = loaderUtils.getOptions(this) || {};
const resourcePath = this.resourcePath;
let {partType, cmlType, fileType, media, check} = rawOptions;
const context = (
this.rootContext ||
(this.options && this.options.context) ||
process.cwd()
)
let parts = cmlUtils.splitParts({content: source});
this._module._nodeType = 'module';
this._module._moduleType = partType;
this._module._parentNodeType = fileType;
let cmlInfo = this._compiler._mvvmCmlInfo[this.resourcePath];
let output = '';
switch (partType) {
case 'style':
output = parts.style[0].content; // 交给后面loader
break;
case 'script':
parts.script.forEach(item => { // 交给后面loader
if (item.cmlType !== 'json') {
let content = item.content;
let {runtimeNpmName, runtimeNeedComponents} = this._compiler._platformPlugin;
let insertMethodMap = {
app: 'createApp',
page: 'createPage',
component: 'createComponent',
}
let runtimeScript = '';
runtimeScript += `
import ${helper.toUpperCase(runtimeNpmName)} from '${runtimeNpmName}';\n
`
// runtime方法需要组件
if (runtimeNeedComponents) {
let {componentFiles} = cmlInfo;
let components = ['{'];
let comKeys = Object.keys(componentFiles);
comKeys.forEach((comName, index) => {
content += `
import ${helper.toUpperCase(comName)} from "${cmlUtils.handleRelativePath(self.resourcePath, componentFiles[comName])}"\n`;
if (comKeys.length - 1 === index) {
components.push(`${helper.toUpperCase(comName)}`);
} else {
components.push(`${helper.toUpperCase(comName)},`);
}
})
components.push('}');
runtimeScript += `
exports.default = ${helper.toUpperCase(runtimeNpmName)}.${insertMethodMap[fileType]}(exports.default, ${components.join('')}).getOptions();\n
`
} else {
runtimeScript += `
exports.default = ${helper.toUpperCase(runtimeNpmName)}.${insertMethodMap[fileType]}(exports.default).getOptions();\n
`
}
content += runtimeScript;
output = getScriptCode(self, cmlType, content, media, check);
}
})
break;
case 'template':
this._module._cmlSource = cmlInfo.compiledTemplate || '';
this._module._cmlExtra = {
usingComponents: cmlInfo.usingComponents || [],
currentUsedBuildInTagMap: cmlInfo.currentUsedBuildInTagMap || []
}
output = `module.exports = ${JSON.stringify(cmlInfo.compiledTemplate)}`;
break;
case 'json':
this._module._cmlSource = JSON.stringify(cmlInfo.compiledJson || {}, '', 4);
output = `module.exports = ${this._module._cmlSource}`;
break;
default:
break;
}
return output;
}
================================================
FILE: packages/mvvm-file-loader/README.md
================================================
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![deps][deps]][deps-url]
[![tests][tests]][tests-url]
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]
File Loader
Instructs webpack to emit the required object as file and to return its public URL
Install
```bash
npm install --save-dev file-loader
```
By default the filename of the resulting file is the MD5 hash of the file's contents with the original extension of the required resource.
```js
import img from './file.png'
```
**webpack.config.js**
```js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {}
}
]
}
]
}
}
```
Emits `file.png` as file in the output directory and returns the public URL
```
"/public/path/0dcbbaa7013869e351f.png"
```
Options
|Name|Type|Default|Description|
|:--:|:--:|:-----:|:----------|
|**`name`**|`{String\|Function}`|`[hash].[ext]`|Configure a custom filename template for your file|
|**`regExp`**|`{RegExp}`|`'undefined'`|Let you extract some parts of the file path to reuse them in the `name` property|
|**`context`**|`{String}`|`this.options.context`|Configure a custom file context, defaults to `webpack.config.js` [context](https://webpack.js.org/configuration/entry-context/#context)|
|**`publicPath`**|`{String\|Function}`|[`__webpack_public_path__ `](https://webpack.js.org/api/module-variables/#__webpack_public_path__-webpack-specific-)|Configure a custom `public` path for your file|
|**`outputPath`**|`{String\|Function}`|`'undefined'`|Configure a custom `output` path for your file|
|**`useRelativePath`**|`{Boolean}`|`false`|Should be `true` if you wish to generate a `context` relative URL for each file|
|**`emitFile`**|`{Boolean}`|`true`|By default a file is emitted, however this can be disabled if required (e.g. for server side packages)|
### `name`
You can configure a custom filename template for your file using the query parameter `name`. For instance, to copy a file from your `context` directory into the output directory retaining the full directory structure, you might use
#### `{String}`
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]'
}
}
```
#### `{Function}`
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name (file) {
if (env === 'development') {
return '[path][name].[ext]'
}
return '[hash].[ext]'
}
}
}
```
### `regExp`
Defines a `regExp` to match some parts of the file path. These capture groups can be reused in the `name` property using `[N]` placeholder. Note that `[0]` will be replaced by the entire tested string, whereas `[1]` will contain the first capturing parenthesis of your regex and so on...
```js
import img from './customer01/file.png'
```
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
regExp: /\/([a-z0-9]+)\/[a-z0-9]+\.png$/,
name: '[1]-[name].[ext]'
}
}
```
```
customer01-file.png
```
#### `placeholders`
|Name|Type|Default|Description|
|:--:|:--:|:-----:|:----------|
|**`[ext]`**|`{String}`|`file.extname`|The extension of the resource|
|**`[name]`**|`{String}`|`file.basename`|The basename of the resource|
|**`[path]`**|`{String}`|`file.dirname`|The path of the resource relative to the `context`|
|**`[hash]`**|`{String}`|`md5`|The hash of the content, hashes below for more info|
|**`[N]`**|`{String}`|``|The `n-th` match obtained from matching the current file name against the `regExp`|
#### `hashes`
`[
:hash::]` optionally you can configure
|Name|Type|Default|Description|
|:--:|:--:|:-----:|:----------|
|**`hashType`**|`{String}`|`md5`|`sha1`, `md5`, `sha256`, `sha512`|
|**`digestType`**|`{String}`|`hex`|`hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`|
|**`length`**|`{Number}`|`9999`|The length in chars|
By default, the path and name you specify will output the file in that same directory and will also use that same URL path to access the file.
### `context`
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
context: ''
}
}
```
You can specify custom `output` and `public` paths by using `outputPath`, `publicPath` and `useRelativePath`
### `publicPath`
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
publicPath: 'assets/'
}
}
```
### `outputPath`
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]',
outputPath: 'images/'
}
}
```
### `useRelativePath`
`useRelativePath` should be `true` if you wish to generate a relative URL to the for each file context.
```js
{
loader: 'file-loader',
options: {
useRelativePath: process.env.NODE_ENV === "production"
}
}
```
### `emitFile`
By default a file is emitted, however this can be disabled if required (e.g. for server side packages).
```js
import img from './file.png'
```
```js
{
loader: 'file-loader',
options: {
emitFile: false
}
}
```
> ⚠️ Returns the public URL but does **not** emit a file
```
`${publicPath}/0dcbbaa701328e351f.png`
```
Examples
```js
import png from 'image.png'
```
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: 'dirname/[hash].[ext]'
}
}
```
```
dirname/0dcbbaa701328ae351f.png
```
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[sha512:hash:base64:7].[ext]'
}
}
```
```
gdyb21L.png
```
```js
import png from 'path/to/file.png'
```
**webpack.config.js**
```js
{
loader: 'file-loader',
options: {
name: '[path][name].[ext]?[hash]'
}
}
```
```
path/to/file.png?e43b20c069c4a01867c31e98cbce33c9
```
Maintainers
[npm]: https://img.shields.io/npm/v/file-loader.svg
[npm-url]: https://npmjs.com/package/file-loader
[node]: https://img.shields.io/node/v/file-loader.svg
[node-url]: https://nodejs.org
[deps]: https://david-dm.org/webpack-contrib/file-loader.svg
[deps-url]: https://david-dm.org/webpack-contrib/file-loader
[tests]: http://img.shields.io/travis/webpack-contrib/file-loader.svg
[tests-url]: https://travis-ci.org/webpack-contrib/file-loader
[cover]: https://img.shields.io/codecov/c/github/webpack-contrib/file-loader.svg
[cover-url]: https://codecov.io/gh/webpack-contrib/file-loader
[chat]: https://badges.gitter.im/webpack/webpack.svg
[chat-url]: https://gitter.im/webpack/webpack
================================================
FILE: packages/mvvm-file-loader/dist/cjs.js
================================================
'use strict';
var loader = require('./index');
module.exports = loader.default;
module.exports.raw = loader.raw;
================================================
FILE: packages/mvvm-file-loader/dist/index.js
================================================
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.raw = undefined;
exports.default = loader;
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _loaderUtils = require('loader-utils');
var _loaderUtils2 = _interopRequireDefault(_loaderUtils);
var _schemaUtils = require('schema-utils');
var _schemaUtils2 = _interopRequireDefault(_schemaUtils);
var _options = require('./options.json');
var _options2 = _interopRequireDefault(_options);
const mime = require('mime');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/* eslint-disable
multiline-ternary,
*/
function loader(content) {
var options = _loaderUtils2.default.getOptions(this) || {};
(0, _schemaUtils2.default)(_options2.default, options, 'File Loader');
var context = options.context || this.rootContext || this.options && this.options.context;
const file = this.resourcePath;
// Set limit for resource inlining (file size)
let limit = options.limit;
let limitSize = options.limitSize;
if (limit) {
limitSize = parseInt(limitSize, 10);
}
// Get MIME type
const mimetype = options.mimetype || mime.getType(file);
const hasInline = !!~this.resourceQuery.indexOf('__inline');
// No limit or within the specified limit
if (limit && content.length < limitSize || hasInline) {
if (typeof content === 'string') {
content = Buffer.from(content);
}
this._module._nodeType = 'module';
this._module._moduleType = 'asset';
this._module._cmlSource = `module.exports = ${JSON.stringify(base64)}`;
let base64 = `data:${mimetype || ''};base64,${content.toString('base64')}`
this._module._publicPath = base64;
return `module.exports = ${JSON.stringify(base64)}`;
} else {
var url = _loaderUtils2.default.interpolateName(this, options.name, {
context,
content,
regExp: options.regExp
});
var outputPath = url;
if (options.outputPath) {
if (typeof options.outputPath === 'function') {
outputPath = options.outputPath(url);
} else {
outputPath = _path2.default.posix.join(options.outputPath, url);
}
}
let publicPath = outputPath;
if(typeof options.publicPath === 'function') {
publicPath = options.publicPath(outputPath)
} else if(typeof options.publicPath === 'string') {
publicPath = options.publicPath + outputPath;
}
this._module._nodeType = 'module';
this._module._moduleType = 'asset';
this._module._cmlSource = `module.exports = "${publicPath}";`;
this._module._bufferSource = content;
this._module._outputPath = outputPath;
this._module._publicPath = publicPath;
return `module.exports = "${publicPath}";`;
}
}
var raw = exports.raw = true;
================================================
FILE: packages/mvvm-file-loader/dist/options.json
================================================
{
"type": "object",
"properties": {
"name": {},
"regExp": {},
"context": {
"type": "string"
},
"publicPath": {},
"outputPath": {},
"useRelativePath": {
"type": "boolean"
},
"emitFile": {
"type": "boolean"
}
},
"additionalProperties": true
}
================================================
FILE: packages/mvvm-file-loader/package.json
================================================
{
"name": "mvvm-file-loader",
"version": "1.0.8",
"description": "mvvm file loader",
"main": "dist/cjs.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"loader-utils": "^1.0.2",
"mime": "^2.4.2",
"schema-utils": "^0.4.5"
},
"author": "Chameleon-Team",
"license": "Apache"
}
================================================
FILE: packages/mvvm-interface-parser/.eslintrc
================================================
{
// 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写
"globals": {
"cml": false
},
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion": 9,
"sourceType": "module", //设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
//想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn": true,
// impliedStric
"impliedStrict": true,
// 启用 JSX
"jsx": true,
"modules": true
}
},
//-----让eslint支持 JSX start
"plugins": [
],
"extends": [
"eslint:recommended"
],
//-----让eslint支持 JSX end
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
////////////////
// 可能的错误 //
////////////////
// 禁止条件表达式中出现赋值操作符
"no-cond-assign": 2,
// 禁用 console
"no-console": 0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition": 2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex": 2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1, "never"],
// 禁用 debugger
"no-debugger": 2,
// 禁止 function 定义中出现重名参数
"no-dupe-args": 2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁止空语句块
"no-empty": 2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class": 2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign": 2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast": 2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens": 0,
// 禁止不必要的分号
"no-extra-semi": 2,
// 禁止对 function 声明重新赋值
"no-func-assign": 2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2, "functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp": 2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace": 2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs": 2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls": 2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins": 0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces": 2,
// 禁用稀疏数组
"no-sparse-arrays": 2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline": 2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
"no-unreachable": 2,
// 要求使用 isNaN() 检查 NaN
"use-isnan": 2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc": 0,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof": 2,
//////////////
// 最佳实践 //
//////////////
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs": 2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return": 0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var": 0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2, 20],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return": 0,
// 强制所有控制语句使用一致的括号风格
"curly": [2, "all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case": 2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2, "property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {
"allowKeywords": false
}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [0, "allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in": 2,
// 禁用 alert、confirm 和 prompt
"no-alert": 0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller": 2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations": 2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex": 2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern": 2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null": 1,
// 禁用 eval()
"no-eval": 2,
// 禁止扩展原生类型
"no-extend-native": 2,
// 禁止不必要的 .bind() 调用
"no-extra-bind": 2,
// 禁用不必要的标签
"no-extra-label:": 0,
// 禁止 case 语句落空
"no-fallthrough": 2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal": 2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion": 0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals": 1,
// 禁止使用类似 eval() 的方法
"no-implied-eval": 2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this": 0,
// 禁用 __iterator__ 属性
"no-iterator": 2,
// 禁用标签语句
"no-labels": 2,
// 禁用不必要的嵌套块
"no-lone-blocks": 2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func": 1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
// 禁止使用多个空格
"no-multi-spaces": 2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str": 2,
// 禁止对原生对象赋值
"no-native-reassign": 2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
================================================
FILE: packages/mvvm-interface-parser/.gitignore
================================================
# 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
# ///////////////////////////
================================================
FILE: packages/mvvm-interface-parser/README.md
================================================
#### 使用
chameleon-loader和interface-loader中获取interface文件内容时使用
================================================
FILE: packages/mvvm-interface-parser/index.js
================================================
const cmlUtils = require('chameleon-tool-utils');
const {getCode} = require('./lib/check.js');
const getInterfaceCode = require('./lib/getInterfaceCode.js');
const getMethodCode = require('./lib/getMethodCode.js');
const path = require('path');
// resolve 用于处理interface中include文件中的引用
module.exports = function({cmlType, media, source, filePath, check }) {
let interfaceResut = getInterfaceCode({interfacePath: filePath, content: source})
let methodResult = getMethodCode({interfacePath: filePath, content: source, cmlType})
let {content: interfaceContent, devDeps: interfacedevDeps} = interfaceResut;
let {content: methodContent, devDeps: methoddevDeps} = methodResult;
let devDeps = [].concat(interfacedevDeps).concat(methoddevDeps);
if (!interfaceContent) {
throw new Error(`文件: ${filePath}未定义`)
}
if (!methodContent) {
throw new Error(`文件: ${filePath}未定义`)
}
let result = `
${interfaceContent}
${methodContent}
`;
if (media === 'dev' && check.enable === true) {
try {
result = getCode(result, {
cmlType,
filePath,
enableTypes: check.enableTypes || []
});
} catch (e) {
// 当有语法错误 babel parse会报错,报错信息不友好
cmlUtils.log.error(`mvvm-interface-parser: ${filePath} syntax error!`)
}
}
// 将对象原型上的方法属性拷贝到对象上 解决...扩展运算符取不到值的问题
const copyProtoPath = path.join(__dirname, './runtime/copyProto.js');
const copyProtoProperty = `
var copyProtoProperty = require('${cmlUtils.handleRelativePath(filePath, copyProtoPath)}');
copyProtoProperty(exports.default);
`
result = `
${result}
${copyProtoProperty}
`
return {result, devDeps};
}
================================================
FILE: packages/mvvm-interface-parser/lib/check.js
================================================
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const generate = require("@babel/generator");
const {getDefines, parsePlugins} = require('runtime-check');
const path = require('path');
const cmlUtils = require('chameleon-tool-utils');
/**
* 处理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, '__OBJECT__WRAPPER__(' + declarationCode.code + ', __CML_ERROR__, __enableTypes__, __CHECK__DEFINES__ )');
// 转成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 {string} code 代码片段
* @return {string} 代码片段
*/
const getCode = function (code, options) {
let {filePath, cmlType, enableTypes} = options;
const defines = getDefines(code, filePath);
/* eslint-disable no-magic-numbers */
const defineStr = JSON.stringify(defines.defines, null, 2);
/* eslint-disable no-magic-numbers */
const newCode = generate["default"](handlExport(defines.ast)).code;
let result = '';
let wrapperCode = '';
if (filePath) {
filePath = path.resolve(filePath);
filePath = cmlUtils.handleWinPath(filePath);
result += `const __INTERFACE__FILEPATH="${filePath}"`;
}
/* eslint-disable no-inner-declarations */
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()}
`
}
const wrapperPath = path.join(__dirname, '../runtime/checkWrapper.js');
wrapperCode = `require('${cmlUtils.handleRelativePath(filePath, wrapperPath)}')`
result += `
const __enableTypes__ = "${enableTypes}"
const __CHECK__DEFINES__ = ${defineStr};
const __OBJECT__WRAPPER__ = ${wrapperCode};
${newCode}
`;
return result;
};
module.exports = {
getCode
};
================================================
FILE: packages/mvvm-interface-parser/lib/getInterfaceCode.js
================================================
const cmlUtils = require('chameleon-tool-utils');
const fs = require('fs');
/**
* 1 先找到所有interface的定义部分
* 2 校验是否一致 如果多个路径指向同一interface 则合并依赖
*/
module.exports = function({interfacePath, content}) {
let result;
let devDeps = []; // 所有interface上的路径都可能影响获取
function getInterface(filePath, content) {
if (filePath !== interfacePath) {
devDeps.push(filePath);
}
if (!content) {
content = fs.readFileSync(filePath, {encoding: 'utf-8'});
}
let parts = cmlUtils.splitParts({content});
let include = [];
for (let i = 0;i < parts.customBlocks.length;i++) {
if (parts.customBlocks[i].type === 'include') {
include.push(parts.customBlocks[i]);
}
}
let interfaceScript = null;
for (let i = 0;i < parts.script.length;i++) {
if (parts.script[i].cmlType === 'interface') {
if (interfaceScript) {
throw new Error(`multi has define in : ${filePath}`)
} else {
interfaceScript = parts.script[i];
}
}
}
if (interfaceScript) {
if (!result) {
if (interfaceScript.attrs && interfaceScript.attrs.src) {
let newFilePath = cmlUtils.resolveSync(filePath, interfaceScript.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
devDeps.push(newFilePath);
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'});
result = {
content: newContent,
contentFilePath: newFilePath
};
} else {
result = {
content: interfaceScript.content,
contentFilePath: filePath
};
}
} else {
if (result.contentFilePath !== filePath) {
throw new Error(`multi has define in : ${filePath} and ${result.filePath}`)
}
}
}
include.forEach(item => {
if (!item.attrs.src) {
throw new Error(`not define include src attribute in : ${filePath}`)
}
let newFilePath = cmlUtils.resolveSync(filePath, item.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
// 递归include的文件
getInterface(newFilePath);
})
}
getInterface(interfacePath, content);
if (result) {
return {
content: result.content,
devDeps,
contentFilePath: result.contentFilePath
};
} else {
throw new Error(`not find in ${interfacePath}`)
}
}
================================================
FILE: packages/mvvm-interface-parser/lib/getInterfaceCode_old.js
================================================
const cmlUtils = require('chameleon-tool-utils');
const fs = require('fs');
module.exports = function({interfacePath, content}) {
let devDeps = [];
function getInterface(filePath, content) {
if (filePath !== interfacePath) {
devDeps.push(filePath);
}
if (!content) {
content = fs.readFileSync(filePath, {encoding: 'utf-8'});
}
let parts = cmlUtils.splitParts({content});
let include = null;
for (let i = 0;i < parts.customBlocks.length;i++) {
if (parts.customBlocks[i].type === 'include') {
if (include) {
throw new Error(`file just allow has only one : ${filePath}`)
}
include = parts.customBlocks[i];
}
}
let interfaceScript = null;
for (let i = 0;i < parts.script.length;i++) {
if (parts.script[i].cmlType === 'interface') {
interfaceScript = parts.script[i];
}
}
if (include && interfaceScript) {
throw new Error(`file has not allow has : ${filePath}`)
} else if (include === null && interfaceScript) {
return {
filePath: filePath,
part: interfaceScript
}
} else if (include && interfaceScript === null) {
if (!include.attrs.src) {
throw new Error(`not define src attribute: ${filePath}`)
}
let newFilePath = cmlUtils.resolveSync(filePath, include.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
// 递归include的文件
return getInterface(newFilePath);
}
return null;
}
let result = getInterface(interfacePath, content);
if (result) {
let {part, filePath} = result;
// script 有src属性的
if (part.attrs && part.attrs.src) {
let newFilePath = cmlUtils.resolveSync(filePath, part.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
devDeps.push(newFilePath);
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'});
return {
content: newContent,
devDeps,
contentFilePath: newFilePath
};
} else {
return {
content: result.part.content,
devDeps,
contentFilePath: result.filePath
};
}
} else {
throw new Error(`not find in ${interfacePath}`)
}
}
================================================
FILE: packages/mvvm-interface-parser/lib/getMethodCode.js
================================================
const cmlUtils = require('chameleon-tool-utils');
const fs = require('fs');
const {resolveRequire} = require('./resolveRequire.js');
module.exports = function({interfacePath, content, cmlType}) {
let devDeps = [];
function getMethod(filePath, content) {
if (filePath !== interfacePath) {
devDeps.push(filePath);
}
let parts = cmlUtils.splitParts({content});
let include = [];
for (let i = 0;i < parts.customBlocks.length;i++) {
if (parts.customBlocks[i].type === 'include') {
include.push(parts.customBlocks[i]);
}
}
let methodScript = null;
for (let i = 0;i < parts.script.length;i++) {
if (~parts.script[i].cmlType.split(',').indexOf(cmlType)) {
methodScript = parts.script[i];
}
}
if (methodScript) {
return {
filePath,
part: methodScript
}
}
for (let i = include.length - 1; i >= 0; i--) {
let item = include[i];
if (item) {
if (!item.attrs.src) {
throw new Error(`not define include src attribute: ${filePath}`)
}
let newFilePath = cmlUtils.resolveSync(filePath, item.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'})
return getMethod(newFilePath, newContent);
}
}
return null;
}
function getRawContent() {
let result = getMethod(interfacePath, content);
if (result) {
let {part, filePath} = result;
// script 有src属性的
if (part.attrs && part.attrs.src) {
let newFilePath = cmlUtils.resolveSync(filePath, part.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
devDeps.push(newFilePath);
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'});
return {
content: newContent,
devDeps,
contentFilePath: newFilePath
};
} else {
return {
content: result.part.content,
devDeps,
contentFilePath: result.filePath
};
}
} else {
throw new Error(`not find in ${interfacePath}`)
}
}
let {content: newContent, contentFilePath} = getRawContent();
// 需要对原有content中的所有引用路径做解析 解析为绝对路径。
return {
content: resolveRequire({content: newContent, oldFilePath: contentFilePath, newFilePath: interfacePath}),
devDeps,
contentFilePath
}
}
================================================
FILE: packages/mvvm-interface-parser/lib/getMethodCode_old.js
================================================
const cmlUtils = require('chameleon-tool-utils');
const fs = require('fs');
const {resolveRequire} = require('./resolveRequire.js');
module.exports = function({interfacePath, content, cmlType}) {
let devDeps = [];
function getMethod(filePath, content) {
if (filePath !== interfacePath) {
devDeps.push(filePath);
}
let parts = cmlUtils.splitParts({content});
let include = null;
for (let i = 0;i < parts.customBlocks.length;i++) {
if (parts.customBlocks[i].type === 'include') {
if (include) {
throw new Error(`file just allow has only one : ${filePath}`)
}
include = parts.customBlocks[i];
}
}
let methodScript = null;
for (let i = 0;i < parts.script.length;i++) {
if (~parts.script[i].cmlType.split(',').indexOf(cmlType)) {
methodScript = parts.script[i];
}
}
if (methodScript) {
return {
filePath,
part: methodScript
}
}
if (include) {
if (!include.attrs.src) {
throw new Error(`not define src attribute: ${filePath}`)
}
let newFilePath = cmlUtils.resolveSync(filePath, include.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'})
return getMethod(newFilePath, newContent);
}
return null;
}
function getRawContent() {
let result = getMethod(interfacePath, content);
if (result) {
let {part, filePath} = result;
// script 有src属性的
if (part.attrs && part.attrs.src) {
let newFilePath = cmlUtils.resolveSync(filePath, part.attrs.src);
if (!cmlUtils.isFile(newFilePath)) {
throw new Error(`not find file: ${newFilePath}`)
}
devDeps.push(newFilePath);
let newContent = fs.readFileSync(newFilePath, {encoding: 'utf-8'});
return {
content: newContent,
devDeps,
contentFilePath: newFilePath
};
} else {
return {
content: result.part.content,
devDeps,
contentFilePath: result.filePath
};
}
} else {
throw new Error(`not find in ${interfacePath}`)
}
}
let {content: newContent, contentFilePath} = getRawContent();
// 需要对原有content中的所有引用路径做解析 解析为绝对路径。
return {
content: resolveRequire({content: newContent, oldFilePath: contentFilePath, newFilePath: interfacePath}),
devDeps,
contentFilePath
}
}
================================================
FILE: packages/mvvm-interface-parser/lib/resolveRequire.js
================================================
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const t = require('@babel/types');
const generator = require("@babel/generator");
const {parsePlugins} = require('runtime-check');
const cmlUtils = require('chameleon-tool-utils');
// resolve 解析路径的方法
exports.resolveRequire = function({content, oldFilePath, newFilePath}) {
let resolve = cmlUtils.resolveInterfaceRequire;
let ast = exports.getAST(content);
exports.replaceRequire({ast, oldFilePath, newFilePath, resolve});
return generator["default"](ast).code;
}
// 获取dependencies
exports.replaceRequire = function({ast, oldFilePath, newFilePath, resolve}) {
traverse["default"](ast, {
enter: (path) => {
let node = path.node;
if (t.isImportDeclaration(node) && node.source.value) {
let realPath = resolve(oldFilePath, newFilePath, node.source.value);
node.source.value = realPath;
node.source.raw = `'${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 = resolve(oldFilePath, newFilePath, item.init.arguments[0].value);
item.init.arguments[0].value = realPath;
item.init.arguments[0].raw = `'${realPath}'`;
}
})
}
if (t.isExpressionStatement(node) && node.expression && node.expression.callee && node.expression.callee.name === 'require' && node.expression.arguments && node.expression.arguments[0]) {
let realPath = resolve(oldFilePath, newFilePath, node.expression.arguments[0].value);
node.expression.arguments[0].value = realPath;
node.expression.arguments[0].raw = `'${realPath}'`;
}
}
})
}
// 获取ast
exports.getAST = function(content) {
const ast = parser.parse(content, {
sourceType: 'module',
plugins: parsePlugins
});
return ast;
}
================================================
FILE: packages/mvvm-interface-parser/package.json
================================================
{
"name": "mvvm-interface-parser",
"version": "1.0.8",
"main": "index.js",
"scripts": {
"cover": "istanbul cover --report lcov _mocha -- -R spec --recursive",
"test": "mocha --recursive --reporter spec"
},
"author": "Chameleon-Team",
"license": "Apache",
"description": "",
"dependencies": {
"@babel/generator": "^7.3.4",
"@babel/parser": "^7.3.4",
"@babel/traverse": "^7.3.4",
"@babel/types": "^7.3.4",
"chameleon-tool-utils": "1.0.8",
"runtime-check": "1.0.8"
},
"devDependencies": {
"chai": "^4.2.0",
"eslint": "^5.9.0",
"istanbul": "^0.4.5",
"mocha": "^5.2.0"
}
}
================================================
FILE: packages/mvvm-interface-parser/runtime/checkWrapper.js
================================================
/**
* 对象包裹器
*运行时的错误信息,根据端传入不同的方法,
* @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} 校验结果
*/
/**
* var __CHECK__DEFINES__ = {
"types": {
"Callback": {
"input": [],
"output": "Undefined"
}
},
"interfaces": {
"MultiInterface": {
"getMsg": {
"input": ["String", "Object_cml_nullable_lmc_", "Callback_cml_nullable_lmc_"],
"output": "String"
}
}
},
"classes": {
"Method": ["MultiInterface"]
}
};
*/
const checkArgsType = function (methodName, argValues) {
let argList;
if (getType(methodName) == 'Array') { // methodName:['getMsg',2];
// 回调函数的校验 methodName[0] 方法的名字 methodName[1]该回调函数在方法的参数索引
// 如上,对于可传可不传的回调函数来说,Callback_cml_nullable_lmc_,所以需要将其去掉
let funcKey = methods[methodName[0]].input[methodName[1]].replace('_cml_nullable_lmc_', '');
argList = types[funcKey].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') {
// 回调函数的校验 methodName[0] 方法的名字 methodName[1]该回调函数在方法的参数索引
// 如上,对于可传可不传的回调函数来说,Callback_cml_nullable_lmc_,所以需要将其去掉
let funcKey = methods[methodName[0]].input[methodName[1]].replace('_cml_nullable_lmc_', '');
output = types[funcKey].output;
// 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;
};
================================================
FILE: packages/mvvm-interface-parser/runtime/copyProto.js
================================================
module.exports = function copyProtoProperty(obj) {
var EXPORT_OBJ = obj || {};
var EXPORT_PROTO = Object.getPrototypeOf(EXPORT_OBJ);
if (EXPORT_PROTO.constructor !== Object) {
Object.getOwnPropertyNames(EXPORT_PROTO).forEach(function(key) {
if (!/constructor|prototype|length/ig.test(key)) {
// 原型上有自身没有的属性 放到自身上
if (!EXPORT_OBJ.hasOwnProperty(key)) {
EXPORT_OBJ[key] = EXPORT_PROTO[key]
}
}
})
}
return EXPORT_OBJ;
}
================================================
FILE: packages/mvvm-interface-parser/test/check.test.js
================================================
const {getCode} = require('../lib/check.js');
const expect = require('chai').expect;
let code = `
interface Interface1Interface {
getMsg(msg: String): String;
}
class Method implements Interface1Interface {
getMsg(msg) {
return 'web:' + msg;
}
}
export default new Method();
`
let result = getCode(code,{
cmlType: 'wx',
filePath: '/user/name.cml',
enableTypes: []
})
describe('check.js wx', function() {
it('getCode', function() {
expect(!!~result.indexOf('export default __OBJECT__WRAPPER__')).to.be.equal(true);
})
})
let result2 = getCode(code,{
cmlType: 'weex',
filePath: '/user/name.cml',
enableTypes: []
})
describe('check.js weex', function() {
it('getCode', function() {
expect(!!~result2.indexOf('export default __OBJECT__WRAPPER__')).to.be.equal(true);
})
})
================================================
FILE: packages/mvvm-interface-parser/test/checkWrapper.test.js
================================================
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)
}
})
})
================================================
FILE: packages/mvvm-interface-parser/test/copyProto.test.js
================================================
const _ = require('../runtime/copyProto.js');
const expect = require('chai').expect;
class A {
fun1() {
console.log('fun1')
}
}
describe('mvvm-interface-parser/getMethodCode', function() {
it('copyProto.js', function() {
var obj = new A();
_(obj);
expect(obj.hasOwnProperty('fun1'))
})
})
================================================
FILE: packages/mvvm-interface-parser/test/getInterfaceCode.test.js
================================================
const expect = require('chai').expect;
const getInterfaceCode = require('../lib/getInterfaceCode.js');
const path = require('path');
const fs = require('fs');
describe('mvvm-interface-parser/getInterfaceCode', function() {
it('getInterfaceCode', function() {
const interfacePath = path.join(__dirname, './lib/components/second/second.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'})
let result1 = getInterfaceCode({interfacePath, content});
let firstInterfacePath = path.join(__dirname, './lib/components/first/first.interface')
expect(result1.content).to.be.equal('\ninterface FirstInterface {\n getMsg(msg: String): String;\n}\n\n')
expect(result1.contentFilePath).to.be.equal(firstInterfacePath)
expect(!!~result1.devDeps.indexOf(firstInterfacePath)).to.be.equal(true)
})
it('getInterfaceCode src', function() {
const interfacePath = path.join(__dirname, './lib/components/third.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'})
let result1 = getInterfaceCode({interfacePath, content});
let contentPath = path.join(__dirname, './lib/components/thirdinterface.js')
expect(result1.contentFilePath).to.be.equal(contentPath)
expect(!!~result1.devDeps.indexOf(contentPath)).to.be.equal(true)
})
it('getInterfaceCode not has interface', function() {
const interfacePath = path.join(__dirname, './lib/components/third/third.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('not find ')).to.be.equal(true)
}
})
it('multi interface', function() {
const interfacePath = path.join(__dirname, './lib/components/third/multi.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('multi ')).to.be.equal(true)
}
})
it('not has src interface', function() {
const interfacePath = path.join(__dirname, './lib/components/third/not.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('not find file: ')).to.be.equal(true)
}
})
it('mutli interface', function() {
const interfacePath = path.join(__dirname, './lib/components/third/double.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('multi has define in ')).to.be.equal(true)
}
})
it('include src error', function() {
const interfacePath = path.join(__dirname, './lib/components/third/includea.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('not define include src attribute in')).to.be.equal(true)
}
})
it('include src not file', function() {
const interfacePath = path.join(__dirname, './lib/components/third/includeb.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getInterfaceCode({interfacePath, content});
} catch (e) {
expect(!!~e.message.indexOf('not find file:')).to.be.equal(true)
}
})
})
================================================
FILE: packages/mvvm-interface-parser/test/getMethodCode.test.js
================================================
const expect = require('chai').expect;
const getMethodCode = require('../lib/getMethodCode.js');
const path = require('path');
const fs = require('fs');
describe('mvvm-interface-parser/getMethodCode', function() {
it('getMethodCode', function() {
const interfacePath = path.join(__dirname, './lib/components/second/second.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'})
let result1 = getMethodCode({interfacePath, content, cmlType: 'web'});
let firstInterfacePath = path.join(__dirname, './lib/components/first/first.interface');
expect(result1.contentFilePath).to.be.equal(firstInterfacePath)
expect(!!~result1.devDeps.indexOf(firstInterfacePath)).to.be.equal(true)
expect(!!~result1.content.indexOf('FirstInterface')).to.be.equal(true);
})
it('getMethodCode src', function() {
const interfacePath = path.join(__dirname, './lib/components/third.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'})
let result1 = getMethodCode({interfacePath, content, cmlType: 'weex'});
let contentPath = path.join(__dirname, './lib/components/thirdmethod.js');
expect(result1.contentFilePath).to.be.equal(contentPath)
expect(!!~result1.devDeps.indexOf(contentPath)).to.be.equal(true)
expect(!!~result1.content.indexOf('thirdmethods')).to.be.equal(true);
})
it('getMethodCode include not src', function() {
const interfacePath = path.join(__dirname, './lib/components/methodinclude.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getMethodCode({interfacePath, content, cmlType: 'weex'});
} catch (e) {
expect(!!~e.message.indexOf(`not define include src attribute:`)).to.be.equal(true)
}
})
it('getMethodCode include src error', function() {
const interfacePath = path.join(__dirname, './lib/components/methodsrcerror.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getMethodCode({interfacePath, content, cmlType: 'weex'});
} catch (e) {
expect(!!~e.message.indexOf(`not find file:`)).to.be.equal(true)
}
})
it('getMethodCode not find', function() {
const interfacePath = path.join(__dirname, './lib/components/third.interface');
const content = fs.readFileSync(interfacePath, {encoding: 'utf-8'});
try {
getMethodCode({interfacePath, content, cmlType: 'demo'});
} catch (e) {
expect(!!~e.message.indexOf(`not find
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/coma/coma.baidu.cml
================================================
baidu端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/coma/coma.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/coma/coma.web.cml
================================================
web端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/coma/coma.weex.cml
================================================
weex端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/coma/coma.wx.cml
================================================
wx端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/comb/comb.baidu.cml
================================================
baidu端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/comb/comb.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/demo-com/demo-com.cml
================================================
{{title}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/first/first.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/first/test.js
================================================
module.exports = {
}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/methodinclude.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/methodsrcerror.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/partsrcerror.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.alipay.cml
================================================
alipay端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.baidu.cml
================================================
baidu端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.web.cml
================================================
web端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.weex.cml
================================================
weex端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/ph-com/ph-com.wx.cml
================================================
wx端自定义组件
props:{{name}}
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/second/second.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/double.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/includea.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/includeb.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/multi.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/not.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third/third.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/third.interface
================================================
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/thirdinterface.js
================================================
`interface FirstInterface {
getMsg(msg: String): String;
}`
================================================
FILE: packages/mvvm-interface-parser/test/lib/components/thirdmethod.js
================================================
`thirdmethods`
================================================
FILE: packages/mvvm-miniapp-loader/.eslintrc
================================================
{
// 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写
"globals": {
"cml": false
},
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion": 9,
"sourceType": "module", //设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
//想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn": true,
// impliedStric
"impliedStrict": true,
// 启用 JSX
"jsx": true,
"modules": true
}
},
//-----让eslint支持 JSX start
"plugins": [
],
"extends": [
"eslint:recommended"
],
//-----让eslint支持 JSX end
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
////////////////
// 可能的错误 //
////////////////
// 禁止条件表达式中出现赋值操作符
"no-cond-assign": 2,
// 禁用 console
"no-console": 0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition": 2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex": 2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1, "never"],
// 禁用 debugger
"no-debugger": 2,
// 禁止 function 定义中出现重名参数
"no-dupe-args": 2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁止空语句块
"no-empty": 2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class": 2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign": 2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast": 2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens": 0,
// 禁止不必要的分号
"no-extra-semi": 2,
// 禁止对 function 声明重新赋值
"no-func-assign": 2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2, "functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp": 2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace": 2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs": 2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls": 2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins": 0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces": 2,
// 禁用稀疏数组
"no-sparse-arrays": 2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline": 2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
"no-unreachable": 2,
// 要求使用 isNaN() 检查 NaN
"use-isnan": 2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc": 0,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof": 2,
//////////////
// 最佳实践 //
//////////////
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs": 2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return": 0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var": 0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2, 40],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return": 0,
// 强制所有控制语句使用一致的括号风格
"curly": [2, "all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case": 2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2, "property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {
"allowKeywords": false
}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [0, "allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in": 2,
// 禁用 alert、confirm 和 prompt
"no-alert": 0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller": 2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations": 2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex": 2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern": 2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null": 1,
// 禁用 eval()
"no-eval": 2,
// 禁止扩展原生类型
"no-extend-native": 2,
// 禁止不必要的 .bind() 调用
"no-extra-bind": 2,
// 禁用不必要的标签
"no-extra-label:": 0,
// 禁止 case 语句落空
"no-fallthrough": 2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal": 2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion": 0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals": 1,
// 禁止使用类似 eval() 的方法
"no-implied-eval": 2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this": 0,
// 禁用 __iterator__ 属性
"no-iterator": 2,
// 禁用标签语句
"no-labels": 2,
// 禁用不必要的嵌套块
"no-lone-blocks": 2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func": 1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
// 禁止使用多个空格
"no-multi-spaces": 2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str": 2,
// 禁止对原生对象赋值
"no-native-reassign": 2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single","double", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
================================================
FILE: packages/mvvm-miniapp-loader/.gitignore
================================================
# Editor directories and files
.DS_Store
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# 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
dist/
# ///////////////////////////
================================================
FILE: packages/mvvm-miniapp-loader/helper.js
================================================
const path = require('path');
const loaderUtils = require('loader-utils');
exports.stringifyLoaders = function (loaders) {
return loaders
.map(
obj =>
obj && typeof obj === 'object' && typeof obj.loader === 'string'
? obj.loader +
(obj.options ? '?' + JSON.stringify(obj.options) : '')
: obj
)
.join('!')
}
exports.getSelector = function() {
return path.resolve(__dirname, './selector.js')
}
exports.getPartLoaders = function({loaderContext, selectorOptions, partType, lang, loaders, resourcePath}) {
selectorOptions.partType = partType;
let resultLoaders = [
{
loader: exports.getSelector(),
options: selectorOptions
}
]
switch (partType) {
case 'style':
var styleLoader = loaders[lang];
if (!(styleLoader instanceof Array)) {
styleLoader = [styleLoader];
}
resultLoaders = styleLoader.concat(resultLoaders);
break;
case 'script':
var jsLoader = loaders.js;
if (!(jsLoader instanceof Array)) {
jsLoader = [jsLoader];
}
resultLoaders = jsLoader.concat(resultLoaders);
break;
default:
break;
}
let stringLoaders = exports.stringifyLoaders(resultLoaders);
let loaderString = '!!' + stringLoaders + '!' + resourcePath;
return loaderUtils.stringifyRequest(
loaderContext,
loaderString
)
}
exports.toUpperCase = function (content) {
return content.replace(/-(\w)/ig, function (m, s1) {
return s1.toUpperCase()
})
}
================================================
FILE: packages/mvvm-miniapp-loader/index.js
================================================
/**
* 针对小程序的loader 将小程序文件结构变成cml文件结构
*/
const loaderUtils = require('loader-utils');
const helper = require('./helper.js');
module.exports = function(content) {
let output = "";
this._module._nodeType = "component";
let self = this;
const rawOptions = loaderUtils.getOptions(this) || {};
let {loaders, cmlType, media, mapping} = rawOptions;
const resourcePath = this.resourcePath;
let selectorOptions = {
cmlType,
media,
mapping
}
output += `var template = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'template', loaders, resourcePath})});\n`
output += `var style = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'style', lang: 'css', loaders, resourcePath})});\n`
output += `var json = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'json', loaders, resourcePath})});\n`
output += `var script = require(${helper.getPartLoaders({loaderContext: self, selectorOptions, partType: 'script', loaders, resourcePath})});\n`
return output;
}
================================================
FILE: packages/mvvm-miniapp-loader/package.json
================================================
{
"name": "mvvm-miniapp-loader",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"loader-utils": "^1.2.3"
}
}
================================================
FILE: packages/mvvm-miniapp-loader/selector.js
================================================
const loaderUtils = require('loader-utils');
const path = require('path');
const fs = require('fs');
module.exports = function() {
const self = this;
let output = '';
const rawOptions = loaderUtils.getOptions(this) || {};
const resourcePath = this.resourcePath;
let {mapping, partType} = rawOptions;
this._module._nodeType = 'module';
this._module._moduleType = partType;
this._module._parentNodeType = 'components';
let partFilePath = resourcePath.replace(path.extname(resourcePath), mapping[partType]);
self.addDependency(partFilePath);
switch (partType) {
case 'json':
this._module._cmlSource = JSON.stringify(JSON.parse(fs.readFileSync(partFilePath, {encoding: 'utf8'})) || {}, '', 4);
output = `module.exports = ${this._module._cmlSource}`;
break;
case 'template':
this._module._cmlSource = fs.readFileSync(partFilePath, {encoding: 'utf8'});
output = `module.exports = ${JSON.stringify(this._module._cmlSource)}`;
break;
case 'script':
case 'style':
output = fs.readFileSync(partFilePath, {encoding: 'utf8'});
break;
default:
break;
}
return output;
}
================================================
FILE: packages/mvvm-pack/.eslintrc
================================================
{
// 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写
"globals": {
"cml": false
},
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion": 9,
"sourceType": "module", //设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
//想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn": true,
// impliedStric
"impliedStrict": true,
// 启用 JSX
"jsx": true,
"modules": true
}
},
//-----让eslint支持 JSX start
"plugins": [
],
"extends": [
"eslint:recommended"
],
//-----让eslint支持 JSX end
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
////////////////
// 可能的错误 //
////////////////
// 禁止条件表达式中出现赋值操作符
"no-cond-assign": 2,
// 禁用 console
"no-console": 0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition": 2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex": 2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1, "never"],
// 禁用 debugger
"no-debugger": 2,
// 禁止 function 定义中出现重名参数
"no-dupe-args": 2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁止空语句块
"no-empty": 2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class": 2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign": 2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast": 2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens": 0,
// 禁止不必要的分号
"no-extra-semi": 2,
// 禁止对 function 声明重新赋值
"no-func-assign": 2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2, "functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp": 2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace": 2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs": 2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls": 2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins": 0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces": 2,
// 禁用稀疏数组
"no-sparse-arrays": 2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline": 2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
"no-unreachable": 2,
// 要求使用 isNaN() 检查 NaN
"use-isnan": 2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc": 0,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof": 2,
//////////////
// 最佳实践 //
//////////////
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs": 2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return": 0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var": 0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2, 40],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return": 0,
// 强制所有控制语句使用一致的括号风格
"curly": [2, "all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case": 2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2, "property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {
"allowKeywords": false
}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [0, "allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in": 2,
// 禁用 alert、confirm 和 prompt
"no-alert": 0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller": 2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations": 2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex": 2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern": 2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null": 1,
// 禁用 eval()
"no-eval": 2,
// 禁止扩展原生类型
"no-extend-native": 2,
// 禁止不必要的 .bind() 调用
"no-extra-bind": 2,
// 禁用不必要的标签
"no-extra-label:": 0,
// 禁止 case 语句落空
"no-fallthrough": 2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal": 2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion": 0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals": 1,
// 禁止使用类似 eval() 的方法
"no-implied-eval": 2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this": 0,
// 禁用 __iterator__ 属性
"no-iterator": 2,
// 禁用标签语句
"no-labels": 2,
// 禁用不必要的嵌套块
"no-lone-blocks": 2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func": 1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
// 禁止使用多个空格
"no-multi-spaces": 2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str": 2,
// 禁止对原生对象赋值
"no-native-reassign": 2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
================================================
FILE: packages/mvvm-pack/cmlNode.js
================================================
class CMLNode {
constructor(options = {}) {
this.ext;
this.realPath; // 文件物理地址
this.nodeType; // app/page/component/module // 节点类型 app/page/component 其他的为module cml文件中的每一个部分也是一个Node节点
this.moduleType; // template/style/script/json/asset
this.dependencies = []; // 该节点的直接依赖 app.cml依赖pages.cml pages.cml依赖components.cml js依赖js
this.childrens = []; // 子模块 cml文件才有子模块
this.parent; // 父模块 cml文件中的子模块才有
this.originSource; // 模块源代码 script节点
this.source; // 模块标准编译后代码
this.convert; // 源代码的格式化形式
this.output; // 模块输出 各种过程操作该字段
this.identifier; // 节点唯一标识
this.modId; // 模块化的id requirejs
this.extra; // 节点的额外信息
Object.keys(options).forEach(key => {
this[key] = options[key];
})
}
}
module.exports = CMLNode;
================================================
FILE: packages/mvvm-pack/compiler.js
================================================
const CMLNode = require('./cmlNode.js');
const path = require('path');
const Log = require('./log.js');
const EventEmitter = require('events');
const cmlUtils = require('chameleon-tool-utils');
const {cmlparse} = require('mvvm-template-parser');
const amd = require('./lib/amd.js');
const {handleScript, chameleonIdHandle} = require('./lib/handleScript.js');
const UglifyJs = require('./minimize/uglifyjs.js');
const UglifyCSS = require('./minimize/uglifycss.js');
class Compiler {
constructor(webpackCompiler, plugin, options) {
this.moduleRules = [ // 文件后缀对应module信息
{
test: /\.css|\.less|\.stylus|\.styls$/,
moduleType: 'style'
},
{
test: /\.js|\.interface$/,
moduleType: 'script'
},
{
test: /\.json$/,
moduleType: 'json'
},
{
test: /\.(png|jpe?g|gif|svg|mp4|webm|ogg|mp3|wav|flac|aac|woff|woff2?|eot|ttf|otf)(\?.*)?$/,
moduleType: 'asset'
}
]
this.outputFiles = []; // 输出文件 filePath为文件路径 conent为输出文件内容 addHash 是否添加hash
this.projectGraph = null;
this.log = new Log({
level: plugin.logLevel || 2
});
this.event = new EventEmitter();
this.webpackCompiler = webpackCompiler;
// 用户扩展文件类型
if (plugin.moduleRules && plugin.moduleRules instanceof Array) {
this.moduleRules = this.moduleRules.concat(plugin.moduleRules);
}
this.amd = amd; // amd的工具方法
this.hasCompiledNode = []; // 记录已经编译的模块 避免重复编译
this.cmlType = options.cmlType;
this.media = options.media;
this.userPlugin = plugin;
this.outputPath = this.webpackCompiler.options.output.path;
this.definitions = {}; //
this.getDefinePlugins();
}
run(modules) {
this.projectGraph = null;
this.outputFiles = [];
this.hasCompiledNode = [];
this.module2Node(modules);
this.customCompile();
this.emit('pack', this.projectGraph);
this.emitFiles();
}
emit(eventName, ...params) {
this.log.debug('emit log:' + eventName + 'params:' + params)
this.event.emit(eventName, ...params);
}
hook(eventName, cb) {
this.event.on(eventName, cb);
}
getDefinePlugins() {
let plugins = this.webpackCompiler.options.plugins || [];
plugins = plugins.filter(item => {
return 'definitions' in item
});
let definitions = {};
plugins.forEach(item => {
definitions = {
...definitions,
...item.definitions
}
})
this.definitions = definitions;
}
// 处理webpack modules
module2Node(modules) {
let appModule;
let styleModule = [];
// 资源的publicPath map对象
let assetPublicMap = {};
for (let i = 0; i < modules.length; i++) {
let item = modules[i];
if (item._nodeType === 'app') {
appModule = item;
}
// 静态资源的写入
if (item._nodeType === 'module' && item._moduleType === 'asset') {
// 写入资源文件
if (item._bufferSource && item._outputPath) {
// 用户插件中执行静态资源位置,而不影响publicPath
let outputPath = item._outputPath;
if (this.userPlugin.assetsPrePath) {
outputPath = this.userPlugin.assetsPrePath + outputPath;
}
this.writeFile(outputPath, item._bufferSource, false);
}
assetPublicMap[item.resource] = item._publicPath;
}
if (item._nodeType === 'module' && item._moduleType === 'style') {
styleModule.push(item);
}
}
// style模块中静态资源路径的处理
styleModule.forEach(item => {
if (item._cmlSource) {
item._cmlSource = item._cmlSource.replace(/__cml(.*?)__lmc/g, function(all, $1) {
if (assetPublicMap[$1]) {
return `url("${assetPublicMap[$1]}")`
} else {
cml.log.error(`not find asset module ${$1}`);
}
})
}
})
if (!appModule) {
throw new Error('not find app.cml node!')
}
// 记录已经创建的节点
let moduleNodeMap = new Map();
this.projectGraph = this.createGraph(appModule, null, moduleNodeMap);
}
// 创建依赖图
createGraph(targetModule, currentNode, moduleNodeMap) {
// 第一个app节点
if (!currentNode) {
currentNode = this.createNode(targetModule);
moduleNodeMap.set(targetModule, currentNode);
}
targetModule.dependencies.forEach(item => {
if (item.module) {
// 如果已经创建了节点
if (moduleNodeMap.has(item.module)) {
let subNode = moduleNodeMap.get(item.module);
// 如果 子节点的文件路径和父节点相同 ze是CML文件 放入childrens
if (subNode.realPath === currentNode.realPath) {
subNode.parent = currentNode;
currentNode.childrens.push(subNode);
} else {
currentNode.dependencies.push(subNode);
}
} else {
// 创建节点
let subNode = this.createNode(item.module);
moduleNodeMap.set(item.module, subNode);
if (subNode.realPath === currentNode.realPath) {
subNode.parent = currentNode;
currentNode.childrens.push(subNode);
} else {
currentNode.dependencies.push(subNode);
}
// 递归创建
this.createGraph(item.module, subNode, moduleNodeMap)
}
}
})
return currentNode;
}
// 创建单个节点
createNode(module) {
let options = {};
options.realPath = module.resource; // 会带参数 资源绝对路径
options.ext = path.extname(module.resource);
options.nodeType = module._nodeType || 'module';
// 新的modId
let modId = chameleonIdHandle(module.id + '');
options.identifier = modId;
options.modId = modId; // 模块化的id todo优化hash
if (options.nodeType === 'module') {
// loader中设置
if (module._moduleType) {
options.moduleType = module._moduleType;
} else {
// 根据后缀
this.moduleRules.forEach(rule => {
if (rule.test.test(module.resource)) {
options.moduleType = rule.moduleType;
}
})
options.moduleType = options.moduleType || 'other';
}
}
// 可能出现module._cmlSource为空字符串的情况
if (module._cmlSource !== undefined) {
options.source = module._cmlSource;
} else {
options.source = module._source && module._source._value;
}
if (module._cmlOriginSource !== undefined) {
options.originSource = module._cmlOriginSource;
}
if (options.moduleType === 'template') {
options.convert = cmlparse(options.source);
}
if (options.moduleType === 'json') {
// cml文件中的json部分
if (options.ext === '.cml' || (this.userPlugin.miniappExt && this.userPlugin.miniappExt.rule.test(options.realPath))) {
options.convert = JSON.parse(options.source);
} else {
// 其他json文件当成script模块
options.moduleType === 'script';
options.source = `module.exports = ${options.source}`
}
}
if (options.moduleType === 'script') {
// 要做js中require模块的处理 替换modId
options.source = handleScript(options.source, module, this.definitions);
}
options.extra = module._cmlExtra || undefined;
return new CMLNode(options)
}
// 开启用户自定义编译
customCompile() {
// 队列串行编译
// 递归编译
this.customCompileNode(this.projectGraph);
}
customCompileNode(currentNode) {
if (~this.hasCompiledNode.indexOf(currentNode)) {
return;
}
this.hasCompiledNode.push(currentNode);
if (~['app', 'page', 'component'].indexOf(currentNode.nodeType)) {
this.emit(`compile-preCML`, currentNode, currentNode.nodeType);
} else {
// template script style json
let parent = currentNode.parent || {};
this.emit(`compile-${currentNode.moduleType}`, currentNode, parent.nodeType);
}
currentNode.childrens.forEach(item => {
this.customCompileNode(item);
})
currentNode.dependencies.forEach(item => {
this.customCompileNode(item);
})
if (~['app', 'page', 'component'].indexOf(currentNode.nodeType)) {
this.emit(`compile-postCML`, currentNode, currentNode.nodeType);
}
}
/**
*
* @param {*} filePath
* @param {*} content
* @param {*} addHash 当配置文件中hash为true时 是否给该文件添加hash
*/
writeFile(filePath, content, addHash = true) {
this.outputFiles.push({
filePath,
content,
addHash
})
}
emitFiles() {
let self = this;
let config = (cml.config.get()[self.cmlType] && cml.config.get()[self.cmlType][self.media]) || {};
let {hash, minimize} = config;
let minimizeExt = this.userPlugin.minimizeExt;
let minimizeExtMap = {};
if (minimizeExt) {
Object.keys(minimizeExt).forEach(key => {
minimizeExt[key].forEach(ext => {
minimizeExtMap[ext] = key;
})
})
}
let outputPath = this.webpackCompiler.options.output.path;
this.outputFiles.forEach(item => {
let outFilePath = path.join(outputPath, item.filePath);
if (minimize === true && minimizeExt) {
let ext = path.extname(outFilePath);
let miniType = minimizeExtMap[ext]; // js or css
if (miniType === 'js') {
let result = UglifyJs(item.content, outFilePath);
if (result === undefined) {
throw new Error(`uglifyjs error from ${outFilePath}`);
} else {
item.content = result;
}
}
if (miniType === 'css') {
let result = UglifyCSS(item.content, outFilePath);
if (result === undefined) {
throw new Error(`uglifycss error from ${outFilePath}`);
} else {
item.content = result;
}
}
}
if (hash === true && item.addHash === true) {
outFilePath = cmlUtils.addHashName(outFilePath, cmlUtils.createMd5(item.content))
}
if (typeof item.content === 'string') {
cmlUtils.fse.outputFileSync(outFilePath, item.content)
} else {
cmlUtils.fse.outputFileSync(outFilePath, item.content, {
encoding: 'binary'
})
}
})
}
getRouterConfig() {
let {routerConfig} = cmlUtils.getRouterConfig();
let subProjectRouter = cmlUtils.getSubProjectRouter();
return {
projectRouter: routerConfig,
subProjectRouter: subProjectRouter
}
}
}
module.exports = Compiler;
================================================
FILE: packages/mvvm-pack/index.js
================================================
const MvvmGraphPlugin = require('./mvvmGraphPlugin.js');
const MvvMCompiler = require('./compiler.js');
module.exports = {
MvvmGraphPlugin,
MvvMCompiler
}
================================================
FILE: packages/mvvm-pack/lib/amd.js
================================================
const amdWrapModule = require('./amdwrapper.js');
const path = require('path');
const fs = require('fs');
const globalBootstrap = fs.readFileSync(path.join(__dirname, 'amdbootstrap.global.js'), {encoding: 'utf8'})
const moduleBootstrap = fs.readFileSync(path.join(__dirname, 'amdbootstrap.module.js'), {encoding: 'utf8'})
function getGlobalBootstrap(globalName) {
return globalBootstrap.replace('$GLOBAL', globalName);
}
function getModuleBootstrap() {
return moduleBootstrap;
}
module.exports = {
amdWrapModule,
getGlobalBootstrap,
getModuleBootstrap
}
================================================
FILE: packages/mvvm-pack/lib/amdbootstrap.global.js
================================================
/**
* 全局型
*/
(function(cmlglobal) {
cmlglobal = cmlglobal || {};
cmlglobal.cmlrequire;
var factoryMap = {};
var modulesMap = {};
cmlglobal.cmldefine = function(id, factory) {
factoryMap[id] = factory;
};
cmlglobal.cmlrequire = function(id) {
var mod = modulesMap[id];
if (mod) {
return mod.exports;
}
var factory = factoryMap[id];
if (!factory) {
throw new Error('[ModJS] Cannot find module "' + id + '"');
}
mod = modulesMap[id] = {
exports: {}
};
var ret = (typeof factory == 'function')
? factory.apply(mod, [cmlglobal.cmlrequire, mod.exports, mod])
: factory;
if (ret) {
mod.exports = ret;
}
return mod.exports;
};
})($GLOBAL); // 全局变量
================================================
FILE: packages/mvvm-pack/lib/amdbootstrap.module.js
================================================
/**
* 模块型
*/
(function() {
var factoryMap = {};
var modulesMap = {};
var cmldefine = function(id, factory) {
if (!factoryMap[id]) {
factoryMap[id] = factory;
}
};
var cmlrequire = function(id) {
var mod = modulesMap[id];
if (mod) {
return mod.exports;
}
var factory = factoryMap[id];
if (!factory) {
throw new Error('[ModJS] Cannot find module "' + id + '"');
}
mod = modulesMap[id] = {
exports: {}
};
var ret = (typeof factory == 'function')
? factory.apply(mod, [cmlrequire, mod.exports, mod])
: factory;
if (ret) {
mod.exports = ret;
}
return mod.exports;
};
module.exports = {
cmldefine: cmldefine,
cmlrequire: cmlrequire
}
})();
================================================
FILE: packages/mvvm-pack/lib/amdwrapper.js
================================================
module.exports = function({content, modId}) {
if (!/^\s*cmldefine\s*\(/.test(content)) {
content = `\ncmldefine('${modId}', function(require, exports, module) {
${content}
})`;
}
return content;
}
================================================
FILE: packages/mvvm-pack/lib/handleScript.js
================================================
const parser = require('@babel/parser');
const traverse = require('@babel/traverse');
const t = require('@babel/types');
const {parsePlugins} = require('runtime-check');
const {chameleonHandle} = require('chameleon-webpack-plugin/lib/utils');
const generator = require("@babel/generator");
exports.handleScript = function(source, cmlmodule, definitions) {
const ast = parser.parse(source, {
sourceType: 'module',
plugins: parsePlugins
});
traverse["default"](ast, {
enter: (path) => {
exports.replaceJsModId(source, cmlmodule, path);
exports.replaceDefines(source, cmlmodule, path, definitions);
}
})
return generator["default"](ast).code;
}
exports.replaceJsModId = function(source, cmlmodule, path) {
let node = path.node;
if (t.isImportDeclaration(node) && node.source.value) {
let modId = getJSModId(node.source.value);
node.source.value = modId;
node.source.raw = `'${modId}'`;
} else if (t.isCallExpression(node) && node.callee && t.isIdentifier(node.callee) && node.callee.name === 'require') {
if (node.arguments && node.arguments.length === 1 && node.arguments[0] && t.isLiteral(node.arguments[0])) {
let modId = getJSModId(node.arguments[0].value);
node.arguments[0].value = modId;
node.arguments[0].raw = `'${modId}'`;
} else if (node.arguments && node.arguments.length === 1 && node.arguments[0] && t.isStringLiteral(node.arguments[0])) {
let modId = getJSModId(node.arguments[0].value);
node.arguments[0].value = modId;
}
}
function getJSModId(rawRequest) {
let deps = cmlmodule.dependencies;
for (let i = 0; i < deps.length; i++) {
let dep = deps[i];
if (rawRequest === dep.request) {
if (dep.module && dep.module.request) {
return exports.chameleonIdHandle(dep.module.id + '')
}
}
}
cml.log.error('not find modId for' + rawRequest);
}
}
exports.getDefines = function(definitions, prefix, result) {
return getInnerDefines(definitions, prefix, result);
function getInnerDefines (definitions, prefix, result) {
Object.keys(definitions).forEach((key) => {
const code = definitions[key];
let newPrefix = prefix ? prefix + '.' + key : key;
if (code && typeof code === 'object') {
getInnerDefines(code, newPrefix, result);
} else {
result.push({
key: newPrefix,
value: code
})
}
})
return result;
}
}
exports.replaceDefines = function(source, cmlmodule, path, definitions) {
let defineResult = [];
exports.getDefines(definitions, '', defineResult);
let node = path.node;
function handleMember(memberList, node) {
if (t.isMemberExpression(node.object)) {
handleMember(memberList, node.object);
} else if (t.isIdentifier(node.object)) {
memberList.push(node.object.name);
}
if (t.isIdentifier(node.property)) {
memberList.push(node.property.name);
}
}
if (t.isIdentifier(node)) {
if (!t.isMemberExpression(path.parent)) {
// 只有一层变量
defineResult.forEach(item => {
if (item.key.length === 1 && item.key[0] === node.name) {
node.name = item.value;
}
})
}
}
// 多级变量,找到最上层的变量起点 process.env.media
else if (t.isMemberExpression(node) && !t.isMemberExpression(path.parent)) {
let memberList = [];
handleMember(memberList, node);
let tokenKey = memberList.join('.');
defineResult.forEach(item => {
if (item.key === tokenKey) {
path.replaceWith(
t.identifier(item.value)
);
}
})
}
}
exports.chameleonIdHandle = function(id) {
let result = chameleonHandle(id, 'chameleon-tool');
return result
}
================================================
FILE: packages/mvvm-pack/log.js
================================================
const chalk = require('chalk');
class Log {
constructor(options = {}) {
this.level = options.level || 2;
}
debug (msg) {
if (this.level >= 3) {
process.stdout.write('\n' + chalk.gray('[DEBUG]') + ' ' + msg + '\n');
}
}
notice (msg) {
if (this.level >= 2) {
process.stdout.write('\n' + chalk.cyan('[INFO]') + ' ' + msg + '\n');
}
}
warn (msg) {
if (this.level >= 1) {
process.stdout.write('\n' + chalk.yellow('[WARNI]') + ' ' + msg + '\n');
}
}
error(msg) {
if (this.level >= 0) {
process.stdout.write('\n' + chalk.red('[ERROR]') + ' ' + msg + '\n');
}
}
}
module.exports = Log;
================================================
FILE: packages/mvvm-pack/minimize/uglifycss.js
================================================
let csso = require('csso');
module.exports = function(content, filePath) {
let result = csso.minify(content, {});
return result.css;
}
================================================
FILE: packages/mvvm-pack/minimize/uglifyjs.js
================================================
let UglifyJS = require("uglify-js");
let cmlUtils = require('chameleon-tool-utils');
module.exports = function(code, filename, options = {}) {
let result = UglifyJS.minify(code, Object.assign(options, {
warnings: true
}));
if (result.error) {
let errorMessage = `file uglify error: ${filename}\n`;
Object.keys(result.error).forEach(key => {
errorMessage += `${key}: ${result.error[key]}\n`
})
throw new Error(errorMessage);
}
if (result.warnings) {
let errorMessage = `file uglify warning: ${filename}\n`;
result.warnings.forEach(item => {
errorMessage += `${item}\n`
})
cmlUtils.log.warn(errorMessage);
}
return result.code;
}
================================================
FILE: packages/mvvm-pack/mvvmGraphPlugin.js
================================================
const MvvmCompiler = require('./compiler.js');
class mvvmGraphPlugin {
constructor(options = {}, platformPlugin) {
this.options = options;
this.platformPlugin = platformPlugin;
}
apply(compiler) {
let self = this;
let mvvmCompiler = new MvvmCompiler(compiler, self.platformPlugin, self.options);
compiler._mvvmCompiler = mvvmCompiler;
compiler._platformPlugin = self.platformPlugin;
// 监听cml中查找组件
cml.event.on('find-component', function(result) {
let {cmlType, filePath} = result;
// 如果是当前端 则进行原生组件查找
if (cmlType === self.options.cmlType) {
let extList = self.platformPlugin.originComponentExtList;
for (let i = 0; i < extList.length; i++) {
let extFilePath = filePath + extList[i];
if (cml.utils.isFile(extFilePath)) {
result.extPath = extFilePath;
break;
}
}
}
})
self.platformPlugin.register(mvvmCompiler);
compiler.plugin('should-emit', function(compilation) {
try {
mvvmCompiler.run(compilation.modules);
} catch (e) {
cml.log.error(e);
}
// 返回false 不进入emit阶段
return false;
})
// 修改config.json的钩子
cml.event.on('config-json', function(jsonObj) {
mvvmCompiler.emit('config-json', jsonObj);
})
// 捕获错误
process.on('uncaughtException', function (err) {
cml.log.error(err);
});
}
}
module.exports = mvvmGraphPlugin;
================================================
FILE: packages/mvvm-pack/package.json
================================================
{
"name": "mvvm-pack",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive test/*.test.js test/**/*.test.js",
"test": "mocha --recursive --reporter spec test/*.test.js test/**/*.test.js",
"test-brk": "node --inspect-brk node_modules/mocha/bin/mocha --recursive --reporter spec test/*.test.js test/**/*.test.js"
},
"author": "Chameleon-Team",
"license": "Apache",
"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": "1.0.8",
"chameleon-webpack-plugin": "1.0.8",
"csso": "^3.5.1",
"mvvm-babel-parser": "1.0.8",
"mvvm-template-parser": "1.0.8",
"runtime-check": "1.0.8",
"uglify-js": "^3.5.10",
"webpack-merge": "^4.2.1"
},
"devDependencies": {
"chai": "^4.2.0",
"eslint": "^5.9.0",
"istanbul": "^0.4.5",
"mocha": "^5.2.0"
}
}
================================================
FILE: packages/mvvm-pack/test/cmlNode.test.js
================================================
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')
})
})
================================================
FILE: packages/mvvm-pack/test/demo-project/.gitignore
================================================
# 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
# ///////////////////////////
# dist
dist/
output/
.temp
================================================
FILE: packages/mvvm-pack/test/demo-project/chameleon.config.js
================================================
// 设置静态资源的线上路径
const publicPath = '//www.static.chameleon.com/cml';
// 设置api请求前缀
const apiPrefix = 'https://api.chameleon.com';
// cml.config.merge({
// templateLang: 'cml',
// templateType: 'html',
// subProject: ['cml-subproject'],
// platforms: ['web', 'weex', 'wx', 'baidu', 'alipay'],
// buildInfo: {
// wxAppId: '123456'
// },
// wx: {
// dev: {
// },
// build: {
// apiPrefix
// }
// },
// web: {
// dev: {
// analysis: false,
// console: false
// },
// build: {
// analysis: false,
// publicPath: `${publicPath}/web/`,
// apiPrefix
// }
// },
// weex: {
// dev: {
// },
// build: {
// publicPath: `${publicPath}/weex/`,
// apiPrefix
// },
// custom: {
// publicPath: `${publicPath}/wx/`,
// apiPrefix
// }
// }
// })
================================================
FILE: packages/mvvm-pack/test/demo-project/coma/coma.interface
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/comb/comb.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/index.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/package.json
================================================
{
"name": "fse",
"version": "1.0.0",
"description": "A chameleon project",
"author": "",
"private": true,
"scripts": {},
"license": "MIT",
"dependencies": {
"chameleon-runtime": "~1.1.0",
"chameleon-store": "~1.0.0",
"cml-ui": "~1.0.0",
"chameleon-ui-builtin": "~1.0.0",
"chameleon-api": "~0.1.0",
"chameleon-bridge": "~0.1.1"
}
}
================================================
FILE: packages/mvvm-pack/test/demo-project/src/app/app.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/components/coma/coma.cml
================================================
{{title}}
================================================
FILE: packages/mvvm-pack/test/demo-project/src/components/comb/comb.interface
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/components/comb/comb.web.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/components/comb/comb.wx.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/components/comc/comc.interface
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/pages/pagea/pagea.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/pages/pageb/pageb.cml
================================================
================================================
FILE: packages/mvvm-pack/test/demo-project/src/router.config.json
================================================
{
"mode": "history",
"domain": "https://www.chameleon.com",
"routes":[
{
"url": "/cml/h5/index",
"path": "/pages/pagea/pagea",
"name": "首页",
"mock": "index.php"
}
]
}
================================================
FILE: packages/mvvm-pack/test/demoPlugin.js
================================================
const path = require('path');
module.exports = class DemoPlugin {
constructor(options) {
let { cmlType, media} = options;
this.webpackRules = []; // webpack的rules设置 用于当前端特殊文件处理
this.moduleRules = []; // 文件后缀对应的节点moduleType
this.logLevel = 3;
this.originComponentExtList = ['.wxml']; // 用于扩展原生组件的文件后缀查找
this.runtimeNpmName = 'cml-demo-runtime'; // 指定当前端的运行时库
this.builtinUINpmName = 'cml-demo-ui-builtin'; // 指定当前端的内置组件库
this.cmlType = cmlType;
this.media = media;
this.miniappExt = { // 小程序原生组件处理
rule: /\.wxml$/,
mapping: {
'template': '.wxml',
'style': '.wxss',
'script': '.js',
'json': '.json'
}
}
// 需要压缩文件的后缀
this.minimizeExt = {
js: ['.js'],
css: ['.css', '.wxss']
}
}
/**
* @description 注册插件
* @param {compiler} 编译对象
* */
register(compiler) {
let self = this;
/**
* cml节点编译前
* currentNode 当前节点
* nodeType 节点的nodeType
*/
compiler.hook('compile-preCML', function(currentNode, nodeType) {
})
/**
* cml节点编译后
* currentNode 当前节点
* nodeType 节点的nodeType
*/
compiler.hook('compile-postCML', function(currentNode, nodeType) {
})
/**
* 编译script节点,比如做模块化
* currentNode 当前节点
* parentNodeType 父节点的nodeType
*/
compiler.hook('compile-script', function(currentNode, parentNodeType) {
})
/**
* 编译script节点,比如做模块化
* currentNode 当前节点
* parentNodeType 父节点的nodeType
*/
compiler.hook('compile-asset', function(currentNode, parentNodeType) {
})
/**
* 编译template节点 语法转义
* currentNode 当前节点
* parentNodeType 父节点的nodeType
*/
compiler.hook('compile-template', function(currentNode, parentNodeType) {
})
/**
* 编译style节点 比如尺寸单位转义
* currentNode 当前节点
* parentNodeType 父节点的nodeType
*/
compiler.hook('compile-style', function(currentNode, parentNodeType) {
})
/**
* 编译json节点
* currentNode 当前节点
* parentNodeType 父节点的nodeType
*/
compiler.hook('compile-json', function(currentNode, parentNodeType) {
})
/**
* 编译other类型节点
* currentNode 当前节点
*/
compiler.hook('compile-other', function(currentNode) {
})
/**
* 编译other类型节点
* currentNode 当前节点
*/
compiler.hook('config-json', function(jsonObj) {
jsonObj.name = 'chameleon';
})
/**
* 编译结束进入打包阶段
*/
compiler.hook('pack', function(projectGraph) {
compiler.writeFile('static/test.js', 'dd')
compiler.writeFile('static/css.css', '.class1 {color: red;}')
})
cml.projectRoot = path.join(__dirname, 'demo-project');
cml.config = {
get() {
return {
subProject: ['cml-subproject'],
wx: {
dev: {
minimize: true,
hash: true
}
}
}
}
}
let result = compiler.getRouterConfig();
console.log(JSON.stringify(result));
}
}
================================================
FILE: packages/mvvm-pack/test/lib/amd.test.js
================================================
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)
})
})
================================================
FILE: packages/mvvm-pack/test/lib/amdbootstrap.global.test.js
================================================
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');
})
})
================================================
FILE: packages/mvvm-pack/test/lib/amdbootstrap.module.test.js
================================================
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);
})
})
================================================
FILE: packages/mvvm-pack/test/lib/amdwrapper.test.js
================================================
let wrapper = require('../../lib/amdwrapper');
const expect = require('chai').expect;
describe('amdwrapper', function() {
it('not has cmldefine', function() {
let code = 'sdfsdfsdfsdfd'
let result = wrapper({content: code, modId: '123'})
expect(result).to.be.equal('\ncmldefine(\'123\', function(require, exports, module) {\n sdfsdfsdfsdfd\n})')
})
it('has cmldefine', function() {
let code = '\ncmldefine(\'123\', function(require, exports, module) {\n sdfsdfsdfsdfd\n})';
let result = wrapper({content: code, modId: '123'})
expect(result).to.be.equal(code)
})
})
================================================
FILE: packages/mvvm-pack/test/lib/handleScript.node.js
================================================
let _ = require('../../lib/handleScript.js');
const cmlUtils = require('chameleon-tool-utils');
global.cml = {
log: cmlUtils.log
}
let code = `
var b = require('../b.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'
}
},
{
request: '../d.js',
module: {
request: '../d.js',
id: 'd'
}
},
{
request: './src/interfaces/alert/index.js',
module: {
request: '../d.js',
id: 'index'
}
}
]
}
let result = _.handleScript(code, target, {});
console.log(result)
================================================
FILE: packages/mvvm-pack/test/lib/handleScript.test.js
================================================
let _ = require('../../lib/handleScript.js');
const expect = require('chai').expect;
describe('handleScript.js', function() {
it('handleScript', function() {
let code = `
import a from '../a.js';
var b = require('../b.js');
require('../c.js');
module.exports = require('../d.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'
}
},
{
request: '../d.js',
module: {
request: '../d.js',
id: 'd'
}
}
]
}
let result = _.handleScript(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);
expect(!!~result.indexOf('require("d");')).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 {
_.handleScript(code, target);
}
catch (e) {
}
})
it('getDefines', function() {
var defines = {
'process.env.media': JSON.stringify('dev'),
domain: {
domain1: '"domain1"'
},
a: 'avalue',
}
var result = [];
_.getDefines(defines, '', result);
var expectresult = [ { key: 'process.env.media', value: '"dev"' },
{ key: 'domain.domain1', value: '"domain1"' },
{ key: 'a', value: 'avalue' } ]
expect(result).to.deep.equal(expectresult);
})
it('replaceDefines', function() {
let definitions = {
CML: 'chameleon',
'process.env.media': 'dev'
}
let code = `
if(CML) {
}
if(process.env.media == 'dev') {
}
let a = 'a';
console.log(b.c);
`
let result = _.handleScript(code, {}, definitions);
console.log(result)
// expect(result).to.deep.equal(expectresult);
})
})
================================================
FILE: packages/mvvm-pack/test/log.test.js
================================================
var Log = require('../log.js')
describe('log', function() {
it('log.js', function() {
let log = new Log();
log.debug('debug')
log.notice('notice')
log.warn('warn')
log.error('error')
})
})
================================================
FILE: packages/mvvm-pack/test/mvvmGraphPlugin.test.js
================================================
var mvvmGraphPlugin = require('../mvvmGraphPlugin.js')
var demoPlugin = require('./demoPlugin.js');
const expect = require('chai').expect;
const EventEmitter = require('events');
const path = require('path');
const cmlUtils = require('chameleon-tool-utils');
describe('mvvmGraphPlugin.js', function() {
it('1', function() {
var oldCml;
if (global.cml) {
oldCml = global.cml;
}
global.cml = {
event: new EventEmitter(),
config: {
get() {
return {
wx: {}
}
}
}
};
let options = {
cmlType: 'wx',
media: 'dev'
}
let demoPluginInstance = new demoPlugin(options);
let pluginInstance = new mvvmGraphPlugin(options, demoPluginInstance);
let runcb;
let webpackCompiler = {
options: {
output: {
path: ''
}
},
plugin: function(key, func) {
runcb = func
},
run: function() {
let compilation = {
modules: [{
_nodeType: 'app',
_cmlSource: '',
dependencies: [],
request: 'request',
resource: 'resource',
rawRequest: 'rawRequest',
id: 'id'
}]
}
runcb(compilation)
}
}
pluginInstance.apply(webpackCompiler);
// 执行
webpackCompiler.run();
let result = {
cmlType: 'wx',
filePath: path.join(__dirname, 'test')
};
cml.utils = cmlUtils;
cml.event.emit('find-component', result);
var configJson = {};
cml.event.emit('config-json', configJson);
expect(result.extPath).to.be.equal(path.join(__dirname, 'test.wxml'));
expect(configJson.name).to.be.equal('chameleon');
global.cml = oldCml;
})
it('2', function() {
var oldCml;
if (global.cml) {
oldCml = global.cml;
}
global.cml = {
event: new EventEmitter(),
config: {
get() {
return {
wx: {
}
}
}
},
log: cmlUtils.log
};
let options = {
cmlType: 'wx',
media: 'dev'
}
let demoPluginInstance = new demoPlugin(options);
let pluginInstance = new mvvmGraphPlugin(options, demoPluginInstance);
let runcb;
let webpackCompiler = {
options: {
output: {
path: ''
}
},
plugin: function(key, func) {
runcb = func
},
run: function() {
let compilation = {
modules: []
}
runcb(compilation)
}
}
pluginInstance.apply(webpackCompiler);
try {
// 执行
webpackCompiler.run();
} catch (e) {
expect(!!~e.message.indexOf('not find app.cml node')).to.be.equal(true)
}
global.cml = oldCml;
})
it('compiler.js', function() {
var oldCml;
if (global.cml) {
oldCml = global.cml;
}
global.cml = {
event: new EventEmitter(),
config: {
get() {
return {
wx: {
dev: {
minimize: true,
hash: true
}
}
}
}
}
};
global.cml.log = cmlUtils.log;
let options = {
cmlType: 'wx',
media: 'dev'
}
let demoPluginInstance = new demoPlugin(options);
let pluginInstance = new mvvmGraphPlugin(options, demoPluginInstance);
let runcb;
let webpackCompiler = {
options: {
output: {
path: ''
}
},
plugin: function(key, func) {
runcb = func
},
run: function() {
let page1module = {
_nodeType: 'page',
_bufferSource: '123',
_outputPath: 'static/img.png',
_cmlSource: '',
dependencies: [],
_publicPath: 'static.chameleon.com',
request: 'request',
resource: '/pages/pagea.cml',
rawRequest: 'rawRequest',
id: 'id'
}
let page2module = {
_nodeType: 'page',
_bufferSource: '123',
_outputPath: 'static/img.png',
_cmlSource: '',
dependencies: [],
_publicPath: 'static.chameleon.com',
request: 'request',
resource: '/pages/pageb.cml',
rawRequest: 'rawRequest',
id: 'id'
}
let assetModule = {
_nodeType: 'module',
_moduleType: 'asset',
_bufferSource: '123',
_outputPath: 'static/img.png',
_cmlSource: '',
dependencies: [],
_publicPath: 'static.chameleon.com',
request: 'request',
resource: '/assets/chameleon.png',
rawRequest: 'rawRequest',
id: 'id'
}
let styleModule = {
_nodeType: 'module',
_moduleType: 'style',
_cmlSource: '__cml/assets/chameleon.png__lmc;',
dependencies: [],
request: 'request',
resource: 'resource',
rawRequest: 'rawRequest',
id: 'id'
}
let style2Module = {
_source: {
_value: '__cml/assets/chameleon.png__lmc;'
},
_cmlOriginSource: 'originsource',
dependencies: [],
request: 'request',
resource: 'resource.css',
rawRequest: 'rawRequest',
id: 'id'
}
let templateModule = {
_source: {
_value: ''
},
_moduleType: 'template',
dependencies: [],
request: 'request',
resource: 'resource.cml',
rawRequest: 'rawRequest',
id: 'id'
}
let json1Module = {
_source: {
_value: '{"a":"a"}'
},
_moduleType: 'json',
dependencies: [],
request: 'request',
resource: 'resource.cml',
rawRequest: 'rawRequest',
id: 'id'
}
let json2Module = {
_source: {
_value: 'module.exports = {a:\'a\'}'
},
_moduleType: 'json',
dependencies: [],
request: 'request',
resource: 'resource.cml',
rawRequest: 'rawRequest',
id: 'id'
}
let scriptModule = {
_source: {
_value: 'module.exports = {}'
},
_moduleType: 'script',
dependencies: [],
request: 'request',
resource: 'resource.js',
rawRequest: 'rawRequest',
id: 'id'
}
let compilation = {
modules: [{
_nodeType: 'app',
_cmlSource: '',
dependencies: [{
module: page1module
}, {
module: page2module
}, {
module: assetModule
}, {
module: styleModule
}, {
module: templateModule
}, {
module: json1Module
}, {
module: scriptModule
}],
request: 'request',
resource: '/pages/pagea.cml',
rawRequest: 'rawRequest',
id: 'id'
},
assetModule,
styleModule,
style2Module]
}
runcb(compilation)
}
}
pluginInstance.apply(webpackCompiler);
cml.utils = cmlUtils;
// 执行
webpackCompiler.run();
cmlUtils.fse.removeSync(path.join(webpackCompiler.options.output.path, 'static'))
global.cml = oldCml;
})
})
================================================
FILE: packages/mvvm-pack/test/test.wxml
================================================
================================================
FILE: packages/mvvm-pack/test/uglifycss.test.js
================================================
let _ = require('../minimize/uglifycss');
const expect = require('chai').expect;
describe('uglifycss', function() {
it('normal css', function() {
let code = `
.class1 {
font-size: 24px;
}
.class2 {
color: red;
-webkit-box-orient: block-axis;
}
`
let result = _(code)
expect(result).to.be.equal('.class1{font-size:24px}.class2{color:red;-webkit-box-orient:block-axis}')
})
})
================================================
FILE: packages/mvvm-pack/test/uglifyjs.test.js
================================================
const _ = require('../minimize/uglifyjs.js');
const expect = require('chai').expect;
describe('uglifyjs', function() {
it('normal', function() {
let code = `
function f(){ console.log('a') }
`;
let result = _(code, '1.js');
expect(result).to.be.equal('function f(){console.log("a")}')
})
it('warnings', function() {
let code = `
function f(){ var u; return 2 + 3; }
`;
let result = _(code, '1.js');
expect(result).to.be.equal('function f(){return 5}')
})
it('error', function() {
let code = `
function (){ var u; return 2 + 3; }
`;
try {
_(code, '1.js');
} catch (e) {
expect(!!~e.message.indexOf('message: Unexpected token: punc «(», expected: name')).to.be.equal(true)
}
})
it('not has ;', function() {
let commonjsContent = `var manifest = require('./manifest.js')\n`;
commonjsContent += `var cmldefine = manifest.cmldefine;\n`;
let result = _(commonjsContent, '2.js');
expect(result).to.be.equal('var manifest=require("./manifest.js"),cmldefine=manifest.cmldefine;')
})
})
================================================
FILE: packages/mvvm-style-loader/index.js
================================================
const {assets} = require('./lib.js');
/**
* 1.为style模块包装 防止webpack build moudle error
* 2.处理css中的静态资源 css中改成绝对路径 loader过后进行替换publicPath
*/
module.exports = function(content) {
this._module._nodeType = 'module';
this._module._moduleType = 'style';
let {source, deps} = assets({source: content,loaderContext: this});
this._module._cmlSource = source;
let output = '';
deps.forEach(item=>{
output += `require("${item}")`
})
return `
${output}
module.exports = {}`
}
================================================
FILE: packages/mvvm-style-loader/lib.js
================================================
const postcss = require('postcss');
const cmlUtils = require('chameleon-tool-utils');
exports.assets = function({source, loaderContext}) {
let resourcePath = loaderContext.resourcePath;
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 splitUrl = $1.split('?');
let realDependPath = cmlUtils.resolveSync(resourcePath, splitUrl[0]);
if(realDependPath && cmlUtils.isFile(realDependPath)) {
if(splitUrl[1]) {
realDependPath = realDependPath + '?' + splitUrl[1];
}
deps.push($1);
return `__cml${realDependPath}__lmc`;
} else {
return `url("${$1}")`;
}
})
}
})
}
})
return {
source: postcss([assetsPlugin]).process(source).css,
deps
}
}
================================================
FILE: packages/mvvm-style-loader/package.json
================================================
{
"name": "mvvm-style-loader",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"cover": "istanbul cover --report lcov _mocha -- -R spec --recursive",
"test": "mocha --recursive --reporter spec"
},
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"postcss": "7.0.14",
"chameleon-tool-utils": "1.0.8"
},
"devDependencies": {
"chai": "^4.2.0",
"eslint": "^5.9.0",
"istanbul": "^0.4.5",
"mocha": "^5.2.0"
}
}
================================================
FILE: packages/mvvm-style-loader/test/index.test.js
================================================
const _ = require('../index.js');
const path = require('path');
var expect = require('chai').expect;
describe('mvvm-style-loader', function() {
it('assets image url', function() {
let loaderContext = {
_module: {},
resourcePath: path.join(__dirname,'index.js')
}
let content = `
.class1 {
color: red;
background: url('./1.png');
}
`
let output = _.call(loaderContext, content);
expect(!!~output.indexOf('1.png')).to.be.equal(true);
expect(loaderContext._module._nodeType).to.be.equal('module');
expect(loaderContext._module._moduleType).to.be.equal('style');
expect(!!~loaderContext._module._cmlSource.indexOf(`__cml${path.join(__dirname,'1.png')}__lmc`)).to.be.equal(true);
})
it('assets inline image url', function() {
let loaderContext = {
_module: {},
resourcePath: path.join(__dirname,'index.js')
}
let content = `
.class1 {
color: red;
background: url('./1.png?__inline');
}
`
let output = _.call(loaderContext, content);
console.log(output)
console.log(loaderContext._module)
expect(!!~output.indexOf('1.png')).to.be.equal(true);
expect(loaderContext._module._nodeType).to.be.equal('module');
expect(loaderContext._module._moduleType).to.be.equal('style');
expect(!!~loaderContext._module._cmlSource.indexOf(`__cml${path.join(__dirname,'1.png?__inline')}__lmc`)).to.be.equal(true);
})
it('assets not file image url', function() {
let loaderContext = {
_module: {},
resourcePath: path.join(__dirname,'index.js')
}
let content = `
.class1 {
color: red;
background: url('./2.png');
}
`
let output = _.call(loaderContext, content);
console.log(output)
console.log(loaderContext._module)
expect(loaderContext._module._nodeType).to.be.equal('module');
expect(loaderContext._module._moduleType).to.be.equal('style');
expect(!!~loaderContext._module._cmlSource.indexOf(`background: url("./2.png")`)).to.be.equal(true);
})
})
================================================
FILE: packages/mvvm-template-parser/.eslintrc
================================================
{
// 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写
"globals": {
"cml": false
},
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion": 9,
"sourceType": "module", //设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
//想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn": true,
// impliedStric
"impliedStrict": true,
// 启用 JSX
"jsx": true,
"modules": true
}
},
//-----让eslint支持 JSX start
"plugins": [
],
"extends": [
"eslint:recommended"
],
//-----让eslint支持 JSX end
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
////////////////
// 可能的错误 //
////////////////
// 禁止条件表达式中出现赋值操作符
"no-cond-assign": 2,
// 禁用 console
"no-console": 0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition": 2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex": 2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1, "never"],
// 禁用 debugger
"no-debugger": 2,
// 禁止 function 定义中出现重名参数
"no-dupe-args": 2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁止空语句块
"no-empty": 2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class": 2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign": 2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast": 2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens": 0,
// 禁止不必要的分号
"no-extra-semi": 2,
// 禁止对 function 声明重新赋值
"no-func-assign": 2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2, "functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp": 2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace": 2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs": 2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls": 2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins": 0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces": 2,
// 禁用稀疏数组
"no-sparse-arrays": 2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline": 2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
"no-unreachable": 2,
// 要求使用 isNaN() 检查 NaN
"use-isnan": 2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc": 0,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof": 2,
//////////////
// 最佳实践 //
//////////////
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs": 2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return": 0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var": 0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2, 20],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return": 0,
// 强制所有控制语句使用一致的括号风格
"curly": [2, "all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case": 2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2, "property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {
"allowKeywords": false
}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [0, "allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in": 2,
// 禁用 alert、confirm 和 prompt
"no-alert": 0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller": 2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations": 2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex": 2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern": 2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null": 1,
// 禁用 eval()
"no-eval": 2,
// 禁止扩展原生类型
"no-extend-native": 2,
// 禁止不必要的 .bind() 调用
"no-extra-bind": 2,
// 禁用不必要的标签
"no-extra-label:": 0,
// 禁止 case 语句落空
"no-fallthrough": 2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal": 2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion": 0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals": 1,
// 禁止使用类似 eval() 的方法
"no-implied-eval": 2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this": 0,
// 禁用 __iterator__ 属性
"no-iterator": 2,
// 禁用标签语句
"no-labels": 2,
// 禁用不必要的嵌套块
"no-lone-blocks": 2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func": 1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
// 禁止使用多个空格
"no-multi-spaces": 2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str": 2,
// 禁止对原生对象赋值
"no-native-reassign": 2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
================================================
FILE: packages/mvvm-template-parser/.gitignore
================================================
# 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
# ///////////////////////////
================================================
FILE: packages/mvvm-template-parser/README.md
================================================
#### mvvm协议标准中处理cml文件template部分
================================================
FILE: packages/mvvm-template-parser/index.js
================================================
const {vueToCml, preDisappearAnnotation} = require('./lib/process-template.js');
const parser = require('mvvm-babel-parser');
const generator = require('mvvm-babel-generator/lib')
const types = require('@babel/types');
const traverse = require('@babel/traverse');
exports.parser = parser;
exports.generator = function(...args) {
let result = generator["default"].apply(this, args);
result.code = exports.postParseUnicode(result.code);
if (/;$/.test(result.code)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的;
result.code = result.code.slice(0, -1);
}
return result;
};
exports.types = types;
exports.traverse = traverse["default"];
exports.vueToCml = vueToCml;
exports.cmlparse = function(content) {
content = preDisappearAnnotation(content);
return parser.parse(content, {
plugins: ['jsx']
})
}
// 后置处理:用于处理 \u ,便于解析unicode 中文
exports.postParseUnicode = function(content) {
let reg = /\\u/g;
return unescape(content.replace(reg, '%u'));
}
================================================
FILE: packages/mvvm-template-parser/lib/process-template.js
================================================
const parser = require('mvvm-babel-parser');
const t = require('@babel/types');
const traverse = require('@babel/traverse')["default"];
const generate = require('mvvm-babel-generator/lib')["default"];
const _ = module.exports = {};
/* 将vue语法的模板转化为cml语法
主要是将
1 :id="value" => v-bind:id="value"
2 @click="handleClick" => c-bind:click="handleClick" 或者c-catch
*/
_.trim = function (value) {
return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};
_.vueToCml = function(source, options = {}) {
// 去掉模板中的注释
source = _.preDisappearAnnotation(source);
source = _.preParseTemplate(source);
source = _.compileTemplate(source, options);
// 后置处理:用于处理 \u ,便于解析unicode 中文
source = _.postParseUnicode(source);
if (/;$/.test(source)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的;
source = source.slice(0, -1);
}
return {
source,
usedBuildInTagMap: options.usedBuildInTagMap || {}
}
}
// 去掉html模板中的注释
_.preDisappearAnnotation = function (content) {
let annotionReg = //g;
return content.replace(annotionReg, function (match) {
return '';
})
}
// 解析属性上的 : 以及 @ v-on这样的语法;
_.preParseTemplate = function(source) {
let callbacks = {startCallback: _.startCallback};
let htmlArr = _.preParseHTMLtoArray(source, callbacks);
let newHtmlArr = [];
htmlArr.forEach((item) => {
if (item.type === 'tagContent') { // 标签内置直接push内容
newHtmlArr.push(item.content);
}
if (item.type === 'tagEnd') {
newHtmlArr.push(item.content);
}
if (item.type === 'tagStart') {
newHtmlArr.push(item.content)
}
});
return newHtmlArr.join('')
}
_.preParseHTMLtoArray = function(html, callbacks) {
let {startCallback} = callbacks;
// 需要考虑问题 单标签和双标签
let stack = [];
// id="value" id='value' class=red disabled
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// 标签的匹配,这些正则都不是g这种全局匹配,所以仅仅会匹配第一个遇到的标签;
// const startTag = new RegExp(`^<${qnameCapture}([\\s\\S])*(\\/?)>`);
// const startTag = /^<([a-zA-Z-:.]*)[^>]*?>/;
const startTagOpen = new RegExp(`^<${qnameCapture}`) // 匹配开始open
const startTagClose = /^\s*(\/?)>/ // 匹配开始关闭;单标签的关闭有两种情况,第一就是 > 第二个就是 />,可以通过捕获分组 / 来判断是单闭合标签还是双开标签的开始标签的闭合
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
let index = 0;
while (html) {
let textEnd = html.indexOf('<')
// 解析标签内容,包括开始标签以及结束标签
if (textEnd === 0) { // 以 < 开头的html
const startTagMatch = parseStartTag();
if (startTagMatch) {
stack.push(startTagMatch);
continue;
}
const endTagMatch = parseEndTag();
if (endTagMatch) {
stack.push(endTagMatch);
continue;
}
}
// 解析标签中间的内容
let text, rest, next
if (textEnd >= 0) {
rest = html.slice(textEnd)
while (
!endTag.test(rest) &&
!startTagOpen.test(rest)
) {
// < in plain text, be forgiving and treat it as text
next = rest.indexOf('<', 1)
if (next < 0) {break}
textEnd += next
rest = html.slice(textEnd)
}
let matchText = {
type: "tagContent"
};
text = html.substring(0, textEnd)
matchText.content = text;
matchText.isText = true;
stack.push(matchText);
advance(textEnd);
continue;
}
if (textEnd < 0) {
text = html;
html = '';
const matchText2 = {
type: 'tagContent',
content: text
}
stack.push(matchText2)
continue;
}
}
return stack;
function advance (n) {
index += n
html = html.substring(n)
}
function parseStartTag () {
// 开始标签也可能是一元标签 通过 isunary 字段进行区分
const start = html.match(startTagOpen)
if (start) {
const matchStart = {
type: 'tagStart',
tagName: start[1],
attrs: []
}
advance(start[0].length);
let end, attr
// 这里处理标签的属性值;
while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
advance(attr[0].length)
matchStart.attrs.push(attr)
}
if (end) {
matchStart.isunary = !!_.trim(end[1] || '');// 标记是否是一元标签
advance(end[0].length)
let attrString = startCallback(matchStart) || '';
let content ;
if (matchStart.isunary) {
content = `<${matchStart.tagName} ${attrString} />`
} else {
content = `<${matchStart.tagName} ${attrString} >`
}
matchStart.content = content;// 每个数组中这个值用于拼接;
return matchStart
}
}
}
function parseEndTag() {
const end = html.match(endTag);
if (end) {
const matchEnd = {
type: 'tagEnd',
tagName: end[1],
content: end[0]
}
advance(end[0].length)
return matchEnd;
}
}
}
_.startCallback = function(matchStart) {
let leftAttrsOnComponent = matchStart.attrs;// 遗留在组件上的属性,默认值是所有属性,
let attrString = (leftAttrsOnComponent || []).reduce((result, item) => {
if (item[1].indexOf(':') === 0) {
item[0] = _.preParseBindAttr(item[0]);// :id="value" ==> v-bind:id="value"
}
if (item[1].indexOf('@') === 0 || item[1].indexOf('v-on') === 0) {
item[0] = _.preParseVueEvent(item[0]);// @click="handleClick" v-on:click="handleClick" ==> c-bind:click="handleClick"
}
result = result + (item[0] || '');
return result;
}, '')
return attrString;
}
// 模板前置处理器
// 预处理:属性 :name="sth" ==> v-bind:name="sth",因为jsx识别不了 :name="sth"
_.preParseBindAttr = function (content) {
content = content.replace(/(\s+):([a-zA-Z_\-0-9]+?\s*=\s*["'][^"']*?["'])/ig, (m, $1, $2) => `${$1}v-bind:${$2}`);
return content;
}
/**
* 处理vue的事件
* ...
* ...
*
* ...
* @param {*} content
*/
_.preParseVueEvent = function (content) {
// v-on | @--> <-- 属性名 --><--=-->
let reg = /(?:v\-on:|@)([^\s"'<>\/=]+?)\s*=\s*/g
content = content.replace(reg, (m, $1) => {
if (typeof $1 === "string" && $1.endsWith('.stop')) {
$1 = $1.replace('.stop', '');
$1 = $1 === 'click' ? 'tap' : $1;
return `c-catch:${$1}=`;
} else {
$1 = $1 === 'click' ? 'tap' : $1;
return `c-bind:${$1}=`
}
});
return content;
}
_.compileTemplate = function(source, options) {
const ast = parser.parse(source, {
plugins: ['jsx']
});
traverse(ast, {
enter(path) {
// 所有的入口都以JSXElement为入口解析;
_.parseAllAttributes(path, options);
_.parseBuildTag(path, options);
}
});
return generate(ast).code;
}
_.isOriginTagOrNativeComp = function(tagName, options) {
let usedComponentInfo = (options.usingComponents || []).find((item) => item.tagName === tagName)
let isNative = usedComponentInfo && usedComponentInfo.isNative;
let isOrigin = (tagName && typeof tagName === 'string' && tagName.indexOf('origin-') === 0);
if (isOrigin || isNative) {
return true
}
return false;
}
/*
以标签为基础,解析attruibutes即可
1 v-bind:id="value" ==> id="{{value}}"
2 v-model="value" v-if="value" ==> c-if="{{value}}"
3 v-for需要特殊处理
4 :class class 需要特殊处理
*/
_.parseAllAttributes = function(path, options) {
let node = path.node;
if (t.isJSXElement(node)) {
let tagName = node.openingElement.name.name
if (_.isOriginTagOrNativeComp(tagName, options)) {
return // 原生标签和原生组件直接不解析
}
let attributes = node.openingElement.attributes;
let directives = ['v-if', 'v-else-if', 'v-else', 'v-model', 'v-show', 'v-text', 'v-for'];
let specialJSXNameSapce = ['c-bind', 'c-catch'];
let specialJSXName = ['class', 'key']
let newAttributes = [];
let bindKeyAttr = attributes.find((attr) => (t.isJSXNamespacedName(attr.name) && attr.name.name.name === 'key'));
let staticClassAttr = attributes.find((attr) => (t.isJSXIdentifier(attr.name) && attr.name.name === 'class'))
let dynamicClassAttr = attributes.find((attr) => (t.isJSXNamespacedName(attr.name) && attr.name.name.name === 'class'));
// 将class :class节点进行融合
if (staticClassAttr || dynamicClassAttr) {
let classNodeValue = '';
if (staticClassAttr) {
classNodeValue = `${classNodeValue} ${staticClassAttr.value.value}`
}
if (dynamicClassAttr) {
classNodeValue = `${classNodeValue} {{${dynamicClassAttr.value.value}}}`
}
if (classNodeValue) {
let classAttr = t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(classNodeValue));
newAttributes.push(classAttr);
}
}
attributes.forEach((attr) => {
// 处理简单的属性 id="value" ==> id="value" 不要处理class,因为cml语法仅支持单class需要特殊处理
if (t.isJSXIdentifier(attr.name) && attr.name.name !== 'class') {
if (!directives.includes(attr.name.name)) {
newAttributes.push(attr)
} else if (directives.includes(attr.name.name) && attr.name.name !== 'v-for') {
attr.value && (attr.value.value = `{{${attr.value.value}}}`);
attr.name.name = attr.name.name.replace('v-', 'c-')
newAttributes.push(attr);
} else if (attr.name.name === 'v-for') {
let value = attr.value && attr.value.value;
let {item, list, index} = _.analysisFor(value);
let cForAttr = t.jsxAttribute(t.jsxIdentifier('c-for'), t.stringLiteral(`{{${list}}}`));
newAttributes.push(cForAttr);
let cForItem = t.jsxAttribute(t.jsxIdentifier('c-for-item'), t.stringLiteral(`${item}`));
newAttributes.push(cForItem);
let cForIndex = t.jsxAttribute(t.jsxIdentifier('c-for-index'), t.stringLiteral(`${index}`));
newAttributes.push(cForIndex);
let bindKeyValue = bindKeyAttr && bindKeyAttr.value && bindKeyAttr.value.value;
if (bindKeyValue && typeof bindKeyValue === 'string') {
if (bindKeyValue === item) {
bindKeyValue = "*this";
} else {
let reg = new RegExp(`${item}\\.`, 'g');
bindKeyValue = bindKeyValue.replace(reg, '');
}
let keyAttr = t.jsxAttribute(t.jsxIdentifier('c-key'), t.stringLiteral(`${bindKeyValue}`));
newAttributes.push(keyAttr);
}
}
}
// 处理vue语法中的 v-bind:id="value" ==> id="{{value}}" 不包括 :class :key
if (t.isJSXNamespacedName(attr.name) && (attr.name.namespace.name === 'v-bind') && !specialJSXName.includes(attr.name.name.name)) {
let name = attr.name.name.name;
let value = attr.value.value;
let newAttr = t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(`{{${value}}}`));
newAttributes.push(newAttr)
}
// 将c-bind c-catch加入
if (t.isJSXNamespacedName(attr.name) && specialJSXNameSapce.includes(attr.name.namespace.name)) {
newAttributes.push(attr);
}
});
node.openingElement.attributes = newAttributes;
}
}
_.analysisFor = function (nodeValue) {
// v-for="item in items"
let reg1 = /\s*(.+?)\s+(?:in|of)\s+(.+)\s*/;
// v-for="(item, index) in items"
let reg2 = /\s*\(([^\,\s]+?)\s*\,\s*([^\,\s]+?)\s*\)\s*(?:in|of)\s+(.+)\s*/
let item, index, list;
let matches1 = nodeValue.match(reg1);
let matches2 = nodeValue.match(reg2);
if (matches2) {
item = matches2[1];
index = matches2[2];
list = matches2[3];
} else if (matches1) {
item = matches1[1];
index = 'index';
list = matches1[2];
}
return {
item,
index,
list
}
}
_.parseBuildTag = function (path, options) {
let node = path.node;
let buildInTagMap = options && options.buildInComponents;// {button:"cml-buildin-button"}
if (t.isJSXElement(node) && buildInTagMap) {
let currentTag = node.openingElement.name.name;
let targetTag = buildInTagMap[currentTag];
// 收集用了哪些内置组件 usedBuildInTagMap:{button:'cml-buildin-button',radio:'cml-buildin-radio'}
let usingComponents = (options.usingComponents || []).map(item => item.tagName)
// 兼容用户自己写了组件和内置组件重名
let isUserComponent = usingComponents.includes(currentTag);
if (isUserComponent) { // 如果是用户的内置组件,这里不做任何处理,直接返回
return;
} else {
if (targetTag && currentTag !== targetTag) {
node.openingElement.name.name = targetTag;
node.closingElement && (node.closingElement.name.name = targetTag);
(!options.usedBuildInTagMap) && (options.usedBuildInTagMap = {});
options.usedBuildInTagMap[currentTag] = targetTag;
}
}
}
}
// 后置处理:用于处理 \u ,便于解析unicode 中文
_.postParseUnicode = function(content) {
let reg = /\\u/g;
return unescape(content.replace(reg, '%u'));
}
================================================
FILE: packages/mvvm-template-parser/package.json
================================================
{
"name": "mvvm-template-parser",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"eslint": "eslint ./lib",
"lint": "eslint --ext .js lib --fix",
"test": "mocha --recursive --reporter spec",
"cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive"
},
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"@babel/traverse": "^7.3.4",
"@babel/types": "^7.3.4",
"mvvm-babel-generator": "1.0.8",
"mvvm-babel-parser": "1.0.8"
},
"devDependencies": {
"chai": "^4.2.0",
"coveralls": "^2.11.9",
"eslint": "^5.9.0",
"istanbul": "^0.4.5",
"mocha": "^5.2.0"
},
"gitHead": "e697386b2323bf287b18774ff482b2c7970f40d8"
}
================================================
FILE: packages/mvvm-template-parser/test/index.test.js
================================================
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 ')
});
})
================================================
FILE: packages/mvvm-template-parser/test/process-template.test.js
================================================
const expect = require('chai').expect;
const processTemplate = require('../lib/process-template.js');
let options = {
lang: 'cml',
buildInComponents: {button: "cml-buildin-button"},
usingComponents: [{
tagName: 'third-comp1',
refUrl: '/path/to/ref1',
filePath: 'path/to/real1',
isNative: true
}, {
tagName: 'thirdComp2',
refUrl: '/path/to/ref2',
filePath: 'path/to/real2',
isNative: false
}]
};
describe('process-template', function() {
describe('trim', function() {
it('test-trim', function() {
expect(processTemplate.trim(' value ')).to.equal('value');
});
});
describe('vueToCml', function() {
it('test-vueToCml-event', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal('');
});
});
describe('vueToCml', function() {
it('test-vueToCml-directive', function() {
let source = `ABc`
expect(processTemplate.vueToCml(source).source).to.equal('ABc');
});
});
describe('vueToCml', function() {
it('test-vueToCml-interation-haskey', function() {
let source = `{{item.id}}{{item[11]}}`
expect(processTemplate.vueToCml(source).source).to.equal('{{item.id}}{{item[11]}}');
});
});
describe('vueToCml', function() {
it('test-vueToCml-interation-key *this*', function() {
let source = `{{item.id}}{{item[11]}}`
expect(processTemplate.vueToCml(source).source).to.equal('{{item.id}}{{item[11]}}');
});
});
describe('vueToCml', function() {
it('test-vueToCml-interation-nokey', function() {
let source = `{{item.id}}{{item[11]}}`
expect(processTemplate.vueToCml(source).source).to.equal('{{item.id}}{{item[11]}}');
});
});
describe('vueToCml', function() {
it('test-vueToCml-class', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal(``);
});
});
describe('vueToCml', function() {
it('test-vueToCml-style', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal(``);
});
});
// 原生组件和 origin-tag标签
describe('vueToCml', function() {
it('test-vueToCml-origin-tag', function() {
let source = ``
expect(processTemplate.vueToCml(source, options).source).to.equal(``);
});
});
// 一元标签
describe('vueToCml', function() {
it('test-vueToCml-isunary', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal(``);
});
});
// 单属性
describe('vueToCml', function() {
it('test-vueToCml-singleAttr', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal(``);
});
});
// 汉字
describe('vueToCml', function() {
it('test-vueToCml-isunary', function() {
let source = ``
expect(processTemplate.vueToCml(source).source).to.equal(``);
});
});
describe('preDisappearAnnotation', function() {
it('test-preDisappearAnnotation', function() {
let source = ``
expect(processTemplate.preDisappearAnnotation(source)).to.equal(``);
});
});
describe('preParseHTMLtoArray', function() {
it('test-preParseHTMLtoArray', function() {
let source = ``
let callbacks = {startCallback: processTemplate.startCallback};
expect(processTemplate.preParseHTMLtoArray(source, callbacks)).to.be.an('array');
});
});
describe('startCallback', function() {
it('test-startCallback', function() {
let matchStart = {
attrs: [
[' :id="value"', ':id'],
[' @click="handleClick"', "@click"],
[' v-on:click="handleClick"', "v-on:click"]
]
}
expect(processTemplate.startCallback(matchStart)).to.be.equal(` v-bind:id="value" c-bind:tap="handleClick" c-bind:tap="handleClick"`);
});
});
describe('preParseBindAttr', function() {
it('test-preParseBindAttr', function() {
let source = ` :id="value"`
expect(processTemplate.preParseBindAttr(source)).to.be.equal(` v-bind:id="value"`);
});
});
describe('preParseVueEvent', function() {
it('test-preParseVueEvent-@', function() {
let source = `@click="handleClick"`
expect(processTemplate.preParseVueEvent(source)).to.be.equal(`c-bind:tap="handleClick"`);
});
});
describe('preParseVueEvent', function() {
it('test-preParseVueEvent-v-on', function() {
let source = `v-on:click="handleClick"`
expect(processTemplate.preParseVueEvent(source)).to.be.equal(`c-bind:tap="handleClick"`);
});
});
describe('isOriginTagOrNativeComp', function() {
it('test-isOriginTagOrNativeComp-isNative', function() {
expect(processTemplate.isOriginTagOrNativeComp('third-comp1', options)).to.be.ok;
});
});
describe('isOriginTagOrNativeComp', function() {
it('test-isOriginTagOrNativeComp-isOriginTag', function() {
expect(processTemplate.isOriginTagOrNativeComp('origin-input', options)).to.be.ok;
});
});
describe('isOriginTagOrNativeComp', function() {
it('test-isOriginTagOrNativeComp-is-not-originTag or native', function() {
expect(processTemplate.isOriginTagOrNativeComp('span', options)).to.be.not.ok;
});
});
describe('analysisFor', function() {
it('transform analysisFor ', function() {
expect(processTemplate.analysisFor(`(item,index) in items`)).to.includes.keys(`item`)
expect(processTemplate.analysisFor(`(item,index) in items`)).to.includes.keys(`index`)
expect(processTemplate.analysisFor(`(item,index) in items`)).to.includes.keys(`list`)
})
});
describe('analysisFor', function() {
it('transform analysisFor ', function() {
expect(processTemplate.analysisFor(`item in items`)).to.includes.keys(`item`)
expect(processTemplate.analysisFor(`item in items`)).to.includes.keys(`index`)
expect(processTemplate.analysisFor(`item in items`)).to.includes.keys(`list`)
})
});
})
================================================
FILE: packages/runtime-check/.eslintrc
================================================
/* eslint-enable */
{
"globals": {
"cml": false
},
"env": {
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"globalReturn": true,
"impliedStrict": true,
"jsx": true,
"modules": true
}
},
"plugins": [
],
"extends": [
"eslint:recommended"
],
"rules": {
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 2,
"no-control-regex": 2,
"comma-dangle": [1, "never"],
"no-debugger": 2,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty": 2,
"no-empty-character-class": 2,
"no-ex-assign": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-func-assign": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-negated-in-lhs": 2,
"no-obj-calls": 2,
"no-prototype-builtins": 0,
"no-regex-spaces": 2,
"no-sparse-arrays": 2,
"no-unexpected-multiline": 2,
"no-unreachable": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"accessor-pairs": 2,
"array-callback-return": 0,
"block-scoped-var": 0,
"complexity": [2, 30],
"consistent-return": 0,
"curly": [2, "all"],
"default-case": 2,
"dot-location": [2, "property"],
"dot-notation": [2, {
"allowKeywords": false
}],
"eqeqeq": [0, "allow-null"],
"guard-for-in": 2,
"no-alert": 0,
"no-caller": 2,
"no-case-declarations": 2,
"no-div-regex": 2,
"no-else-return": 0,
"no-empty-function": 2,
"no-empty-pattern": 2,
"no-eq-null": 1,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-label:": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implicit-coercion": 0,
"no-implicit-globals": 1,
"no-implied-eval": 2,
"no-invalid-this": 0,
"no-iterator": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-loop-func": 1,
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
/* eslint-enable */
================================================
FILE: packages/runtime-check/.gitignore
================================================
# Editor directories and files
.DS_Store
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# 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
# ///////////////////////////
================================================
FILE: packages/runtime-check/getDefines.js
================================================
const parser = require('@babel/parser');
const traverse = require('babel-traverse');
const parsePlugins = require('./parsePlugins.js');
/**
* 获取定义 两个loader共用代码
* 构建时的错误信息 直接throw Error
*
* @param {string} code 代码片段
* @return {Object} 接口及类型定义
*/
const getDefines = (code, filePath) => {
const showErrorMessage = function(content) {
throw new Error(`
文件位置:${filePath}
错误信息: ${content}`)
}
const ast = parser.parse(code, {
plugins: parsePlugins,
sourceType: 'module'
});
// 接口
const interfaces = {};
// 类型
const types = {};
// 类
const classes = {};
/**
* 获取函数input类型
*
* @param {Array} params 输入参数
* @return {Array} 输入参数类型
*/
const getInput = params => {
let input = [];
params.map(param => {
if (param.typeAnnotation.type == 'GenericTypeAnnotation') {
input.push(param.typeAnnotation.id.name);
} else if (param.typeAnnotation.type == 'NullableTypeAnnotation') {
// let nullableType = param.typeAnnotation.typeAnnotation.type.replace('TypeAnnotation','');
let nullableType = getNodeType(param.typeAnnotation.typeAnnotation);
input.push(`${nullableType}_cml_nullable_lmc_`)
} else if (param.typeAnnotation.type == 'NullLiteralTypeAnnotation') {
input.push('Null')
} else {
let dataType = param.typeAnnotation.type.replace('TypeAnnotation', '');
dataType === 'Void' && (dataType = 'Undefined');
input.push(dataType);
}
});
return input;
};
/**
* 获取节点type
*
* @param {Object} typeNode 节点
* @return {string} 类型
*/
const getNodeType = typeNode => {
if (typeNode.type == 'GenericTypeAnnotation') {
return typeNode.id.name;
} else if (typeNode.type == 'NullableTypeAnnotation') {
// let nullableType = typeNode.typeAnnotation.type.replace('TypeAnnotation','');
let nullableType = getNodeType(typeNode.typeAnnotation);
return `${nullableType}_cml_nullable_lmc_`;
} else if (typeNode.type == 'NullLiteralTypeAnnotation') {
return "Null";
} else {
let typeName = typeNode.type.replace('TypeAnnotation', '');
typeName === "Void" && (typeName = "Undefined")
// 没有测试到什么时候是Union类型
if (typeName == 'Union') {
return typeNode.types.map(node => {
const typeName = node.type.replace('TypeAnnotation', '');
if (typeName == 'Generic') {
return node.id.name;
}
return typeName;
});
}
return typeName;
}
};
/**
* 递归产生type
* @param {*} typeName
* @param {*} node
*/
function getTypeDefine(typeName, node) {
let result;
if (node.type === 'FunctionTypeAnnotation') {
result = {
input: getInput(node.params),
output: getNodeType(node.returnType)
};
types[typeName] = result;
} else if (node.type === 'ObjectTypeAnnotation') {
result = {};
node.properties.forEach(item => {
result[item.key.name] = getTypeDefine(`${typeName}__${item.key.name}`, item.value);
})
types[typeName] = result;
} else if (node.type === 'TupleTypeAnnotation') {
result = [];
// 数组只取第一个元素
let length = node.types.length;
if (length > 1) {
showErrorMessage(`type ${typeName}定义的数组类型元素类型只能为一种`)
} else if (length === 0) {
showErrorMessage(`type ${typeName}未定义数组元素类型`)
}
result.push(getTypeDefine(`${typeName}__item`, node.types[0]));
types[typeName] = result;
} else {
// 基本类型 获取type
typeName = getNodeType(node);
}
return typeName;
}
traverse["default"](ast, {
enter(path) {
if (path.node.type == 'TypeAlias') {
let tNode = path.node;
let tName = tNode.id.name;
getTypeDefine(tName, tNode.right);
} else if (path.node.type === 'InterfaceDeclaration') {
let iNode = path.node;
let iName = iNode.id.name;
let defines = {};
interfaces[iName] = defines;
if (iNode.body.type == 'ObjectTypeAnnotation') {
iNode.body.properties.forEach(item => {
if (item.method) {
defines[item.key.name] = {
input: getInput(item.value.params),
output: getNodeType(item.value.returnType)
};
} else {
defines[item.key.name] = getNodeType(item.value)
}
});
}
} else if (path.node.type === 'ClassDeclaration') {
classes[path.node.id.name] = path.node["implements"].map(item => item.id.name);
}
}
});
return {
ast: ast,
defines: {
types,
interfaces,
classes
}
};
};
module.exports = getDefines;
================================================
FILE: packages/runtime-check/index.js
================================================
const parsePlugins = require('./parsePlugins.js');
const getDefines = require('./getDefines.js');
module.exports = {
parsePlugins,
getDefines
}
================================================
FILE: packages/runtime-check/package.json
================================================
{
"name": "runtime-check",
"version": "1.0.8",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha --recursive --reporter spec",
"cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive",
"eslint:fix": "eslint index.js parsePlugins.js getDefines.js --fix",
"eslint": "eslint index.js parsePlugins.js getDefines.js"
},
"author": "Chameleon-Team",
"license": "Apache",
"dependencies": {
"@babel/parser": "7.1.0",
"babel-generator": "6.26.1",
"babel-traverse": "6.26.0"
},
"devDependencies": {
"chai": "^4.2.0",
"coveralls": "^2.11.9",
"eslint": "^5.9.0",
"gulp": "^3.9.1",
"gulp-uglify-es": "^1.0.4",
"istanbul": "^0.4.5",
"mocha": "^5.2.0"
},
"mail": "ChameleonCore@didiglobal.com",
"gitHead": "5ddcde4330774710f7646559446e008f7785ce00"
}
================================================
FILE: packages/runtime-check/parsePlugins.js
================================================
/**
* parse 代码时的ES语法扩展需要的插件
*/
module.exports = [
'flow',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'doExpressions',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'throwExpressions'
]
================================================
FILE: packages/runtime-check/test/index.test.js
================================================
const {expect} = require('chai');
const {getDefines} = require('../index.js');
const fs = require('fs');
const path = require('path');
let code = fs.readFileSync(path.resolve(__dirname,'./interface.test'),'utf-8');
let defines = getDefines(code,'path/to/errorfile');
let result = {
"types": {
"Scheme": {
"a": "String",
"b": "Boolean",
"c": "Number"
}
},
"interfaces": {
"EntryInterface": {
"retValueUndefind": {
"input": [],
"output": "Undefined"
},
"retValueNumber": {
"input": [],
"output": "Number"
},
"retValueString": {
"input": [],
"output": "String"
},
"retValueBoolean": {
"input": [],
"output": "Boolean"
},
"retValueObject": {
"input": [],
"output": "Object"
},
"retValueArray": {
"input": [],
"output": "Array"
},
"retValueRegExp": {
"input": [],
"output": "RegExp"
},
"retValueDate": {
"input": [],
"output": "Date"
},
"retValuePromise": {
"input": [],
"output": "Promise"
},
"retValueAsync": {
"input": [],
"output": "Promise"
},
"retValueFunction": {
"input": [],
"output": "Function"
},
"argsUndefined": {
"input": [
"Undefined"
],
"output": "Undefined"
},
"argsNumber": {
"input": [
"Number"
],
"output": "Undefined"
},
"argsString": {
"input": [
"String"
],
"output": "Undefined"
},
"argsBoolean": {
"input": [
"Boolean"
],
"output": "Undefined"
},
"argsObject": {
"input": [
"Object"
],
"output": "Undefined"
},
"argsArray": {
"input": [
"Array"
],
"output": "Undefined"
},
"argsRegexp": {
"input": [
"RegExp"
],
"output": "Undefined"
},
"argsDate": {
"input": [
"Date"
],
"output": "Undefined"
},
"argsPromsie": {
"input": [
"Promise"
],
"output": "Undefined"
},
"argsAsync": {
"input": [
"Promise"
],
"output": "Undefined"
},
"argsNullable": {
"input": [
"Number_cml_nullable_lmc_"
],
"output": "Undefined"
},
"argsMixins": {
"input": [
"Number",
"Object_cml_nullable_lmc_",
"String"
],
"output": "Undefined"
}
}
},
"classes": {
"Method": [
"EntryInterface"
]
}
}
describe('test runtime check',function(){
it('test the type of args and return value ',function(){
expect(defines.defines).to.be.deep.equal(result)
})
})
================================================
FILE: packages/runtime-check/test/interface.test
================================================
type Scheme = {
a: string,
b: bool,
c: number
}
interface EntryInterface {
retValueUndefind():Undefined;
retValueNumber():Number;
retValueString():String;
retValueBoolean():Boolean;
retValueObject():Object;
retValueArray():Array;
retValueRegExp():RegExp;
retValueDate():Date;
retValuePromise():Promise;
retValueAsync():Promise;
retValueFunction():Function;
argsUndefined(arg:Undefined):Undefined;
argsNumber(arg:Number):Undefined;
argsString(arg:String):Undefined;
argsBoolean(arg:Boolean):Undefined;
argsObject(arg:Object):Undefined;
argsArray(arg:Array):Undefined;
argsRegexp(arg:RegExp):Undefined;
argsDate(arg:Date):Undefined;
argsPromsie(arg:Promise):Undefined;
argsAsync(arg:Promise):Undefined;
argsNullable(arg:?Number):Undefined;
argsMixins(arg1:Number,arg2:?Object,arg3:String):Undefined;
}
class Method implements EntryInterface {
retValueUndefind(){
return undefined;
};
retValueNumber(){
return 8;
};
retValueString(){
return 'this is string'
};
retValueBoolean(){
return true;
};
retValueObject(){
return {};
};
retValueArray(){
return [1,2,3];
};
retValueRegExp(){
return /reg/g;
};
retValueDate(){
return new Date();
};
retValuePromise(){
return new Promise(() => {});
};
async retValueAsync(){
};
retValueFunction(){
return function(){}
};
argsUndefined(arg){};
argsNumber(arg){};
argsString(arg){};
argsBoolean(arg){};
argsObject(arg){};
argsArray(arg){};
argsRegexp(arg){};
argsDate(arg){};
argsPromsie(arg){};
argsAsync(arg){};
argsNullable(arg){};
argsMixins(arg1,arg2,arg3){};
}
export default new Method();
================================================
FILE: packages/url-loader/.babelrc
================================================
{
"presets": [
[
"env",
{
"useBuiltIns": true,
"targets": {
"node": "6.9.0"
},
"exclude": [
"transform-async-to-generator",
"transform-regenerator"
]
}
]
],
"plugins": [
[
"transform-object-rest-spread",
{
"useBuiltIns": true
}
]
],
"env": {
"test": {
"presets": [
"env"
],
"plugins": [
"transform-object-rest-spread"
]
}
}
}
================================================
FILE: packages/url-loader/.circleci/config.yml
================================================
unit_tests: &unit_tests
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: NPM Rebuild
command: npm install
- run:
name: Run unit tests.
command: npm run ci:test
canary_tests: &canary_tests
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: NPM Rebuild
command: npm install
- run:
name: Install Webpack Canary
command: npm i --no-save webpack@next
- run:
name: Run unit tests.
command: if [[ $(compver --name webpack --gte next --lt latest) < 1 ]] ; then printf "Next is older than Latest - Skipping Canary Suite"; else npm run ci:test ; fi
version: 2
jobs:
dependency_cache:
docker:
- image: webpackcontrib/circleci-node-base:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
node8-latest:
docker:
- image: webpackcontrib/circleci-node8:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: NPM Rebuild
command: npm install
- run:
name: Run unit tests.
command: npm run ci:coverage
- run:
name: Submit coverage data to codecov.
command: bash <(curl -s https://codecov.io/bash)
when: on_success
node6-latest:
docker:
- image: webpackcontrib/circleci-node6:latest
<<: *unit_tests
node10-latest:
docker:
- image: webpackcontrib/circleci-node10:latest
<<: *unit_tests
node8-canary:
docker:
- image: webpackcontrib/circleci-node8:latest
<<: *canary_tests
analysis:
docker:
- image: webpackcontrib/circleci-node-base:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: NPM Rebuild
command: npm install
- run:
name: Run linting.
command: npm run lint
- run:
name: Run NSP Security Check.
command: npm run security
- run:
name: Validate Commit Messages
command: npm run ci:lint:commits
publish:
docker:
- image: webpackcontrib/circleci-node-base:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: NPM Rebuild
command: npm install
# - run:
# name: Validate Commit Messages
# command: npm run release:validate
- run:
name: Publish to NPM
command: printf "noop running conventional-github-releaser"
version: 2.0
workflows:
version: 2
validate-publish:
jobs:
- dependency_cache
- node6-latest:
requires:
- dependency_cache
filters:
tags:
only: /.*/
- analysis:
requires:
- dependency_cache
filters:
tags:
only: /.*/
- node8-latest:
requires:
- analysis
- node6-latest
filters:
tags:
only: /.*/
- node10-latest:
requires:
- analysis
- node6-latest
filters:
tags:
only: /.*/
- node8-canary:
requires:
- analysis
- node6-latest
filters:
tags:
only: /.*/
- publish:
requires:
- node8-latest
- node8-canary
- node10-latest
filters:
branches:
only:
- master
================================================
FILE: packages/url-loader/.eslintignore
================================================
/node_modules
/dist
================================================
FILE: packages/url-loader/.eslintrc
================================================
{
// 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写
"globals": {
"cml": false
},
// 环境定义了预定义的全局变量。
"env": {
//环境定义了预定义的全局变量。更多在官网查看
"browser": true,
"node": true,
"commonjs": true,
"amd": true,
"es6": true,
"mocha": true
},
// JavaScript 语言选项
"parserOptions": {
// ECMAScript 版本
"ecmaVersion": 9,
"sourceType": "module", //设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
//想使用的额外的语言特性:
"ecmaFeatures": {
// 允许在全局作用域下使用 return 语句
"globalReturn": true,
// impliedStric
"impliedStrict": true,
// 启用 JSX
"jsx": true,
"modules": true
}
},
//-----让eslint支持 JSX start
"plugins": [
],
"extends": [
"eslint:recommended"
],
//-----让eslint支持 JSX end
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出),
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
*/
"rules": {
////////////////
// 可能的错误 //
////////////////
// 禁止条件表达式中出现赋值操作符
"no-cond-assign": 2,
// 禁用 console
"no-console": 0,
// 禁止在条件中使用常量表达式
// if (false) {
// doSomethingUnfinished();
// } //cuowu
"no-constant-condition": 2,
// 禁止在正则表达式中使用控制字符 :new RegExp("\x1f")
"no-control-regex": 2,
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
"comma-dangle": [1, "never"],
// 禁用 debugger
"no-debugger": 2,
// 禁止 function 定义中出现重名参数
"no-dupe-args": 2,
// 禁止对象字面量中出现重复的 key
"no-dupe-keys": 2,
// 禁止重复的 case 标签
"no-duplicate-case": 2,
// 禁止空语句块
"no-empty": 2,
// 禁止在正则表达式中使用空字符集 (/^abc[]/)
"no-empty-character-class": 2,
// 禁止对 catch 子句的参数重新赋值
"no-ex-assign": 2,
// 禁止不必要的布尔转换
"no-extra-boolean-cast": 2,
// 禁止不必要的括号 //(a * b) + c;//报错
"no-extra-parens": 0,
// 禁止不必要的分号
"no-extra-semi": 2,
// 禁止对 function 声明重新赋值
"no-func-assign": 2,
// 禁止在嵌套的块中出现 function 或 var 声明
"no-inner-declarations": [2, "functions"],
// 禁止 RegExp 构造函数中无效的正则表达式字符串
"no-invalid-regexp": 2,
// 禁止在字符串和注释之外不规则的空白
"no-irregular-whitespace": 2,
// 禁止在 in 表达式中出现否定的左操作数
"no-negated-in-lhs": 2,
// 禁止把全局对象 (Math 和 JSON) 作为函数调用 错误:var math = Math();
"no-obj-calls": 2,
// 禁止直接使用 Object.prototypes 的内置属性
"no-prototype-builtins": 0,
// 禁止正则表达式字面量中出现多个空格
"no-regex-spaces": 2,
// 禁用稀疏数组
"no-sparse-arrays": 2,
// 禁止出现令人困惑的多行表达式
"no-unexpected-multiline": 2,
// 禁止在return、throw、continue 和 break语句之后出现不可达代码
"no-unreachable": 2,
// 要求使用 isNaN() 检查 NaN
"use-isnan": 2,
// 强制使用有效的 JSDoc 注释
"valid-jsdoc": 0,
// 强制 typeof 表达式与有效的字符串进行比较
// typeof foo === "undefimed" 错误
"valid-typeof": 2,
//////////////
// 最佳实践 //
//////////////
// 定义对象的set存取器属性时,强制定义get
"accessor-pairs": 2,
// 强制数组方法的回调函数中有 return 语句
"array-callback-return": 0,
// 强制把变量的使用限制在其定义的作用域范围内
"block-scoped-var": 0,
// 限制圈复杂度,也就是类似if else能连续接多少个
"complexity": [2, 9],
// 要求 return 语句要么总是指定返回的值,要么不指定
"consistent-return": 0,
// 强制所有控制语句使用一致的括号风格
"curly": [2, "all"],
// switch 语句强制 default 分支,也可添加 // no default 注释取消此次警告
"default-case": 2,
// 强制object.key 中 . 的位置,参数:
// property,'.'号应与属性在同一行
// object, '.' 号应与对象名在同一行
"dot-location": [2, "property"],
// 强制使用.号取属性
// 参数: allowKeywords:true 使用保留字做属性名时,只能使用.方式取属性
// false 使用保留字做属性名时, 只能使用[]方式取属性 e.g [2, {"allowKeywords": false}]
// allowPattern: 当属性名匹配提供的正则表达式时,允许使用[]方式取值,否则只能用.号取值 e.g [2, {"allowPattern": "^[a-z]+(_[a-z]+)+$"}]
"dot-notation": [2, {
"allowKeywords": false
}],
// 使用 === 替代 == allow-null允许null和undefined==
"eqeqeq": [0, "allow-null"],
// 要求 for-in 循环中有一个 if 语句
"guard-for-in": 2,
// 禁用 alert、confirm 和 prompt
"no-alert": 0,
// 禁用 arguments.caller 或 arguments.callee
"no-caller": 2,
// 不允许在 case 子句中使用词法声明
"no-case-declarations": 2,
// 禁止除法操作符显式的出现在正则表达式开始的位置
"no-div-regex": 2,
// 禁止 if 语句中有 return 之后有 else
"no-else-return": 0,
// 禁止出现空函数.如果一个函数包含了一条注释,它将不会被认为有问题。
"no-empty-function": 2,
// 禁止使用空解构模式no-empty-pattern
"no-empty-pattern": 2,
// 禁止在没有类型检查操作符的情况下与 null 进行比较
"no-eq-null": 1,
// 禁用 eval()
"no-eval": 2,
// 禁止扩展原生类型
"no-extend-native": 2,
// 禁止不必要的 .bind() 调用
"no-extra-bind": 2,
// 禁用不必要的标签
"no-extra-label:": 0,
// 禁止 case 语句落空
"no-fallthrough": 2,
// 禁止数字字面量中使用前导和末尾小数点
"no-floating-decimal": 2,
// 禁止使用短符号进行类型转换(!!fOO)
"no-implicit-coercion": 0,
// 禁止在全局范围内使用 var 和命名的 function 声明
"no-implicit-globals": 1,
// 禁止使用类似 eval() 的方法
"no-implied-eval": 2,
// 禁止 this 关键字出现在类和类对象之外
"no-invalid-this": 0,
// 禁用 __iterator__ 属性
"no-iterator": 2,
// 禁用标签语句
"no-labels": 2,
// 禁用不必要的嵌套块
"no-lone-blocks": 2,
// 禁止在循环中出现 function 声明和表达式
"no-loop-func": 1,
// 禁用魔术数字(3.14什么的用常量代替)
"no-magic-numbers": [1, {
"ignore": [0, -1, 1]
}],
// 禁止使用多个空格
"no-multi-spaces": 2,
// 禁止使用多行字符串,在 JavaScript 中,可以在新行之前使用斜线创建多行字符串
"no-multi-str": 2,
// 禁止对原生对象赋值
"no-native-reassign": 2,
// 禁止在非赋值或条件语句中使用 new 操作符
"no-new": 2,
// 禁止对 Function 对象使用 new 操作符
"no-new-func": 0,
// 禁止对 String,Number 和 Boolean 使用 new 操作符
"no-new-wrappers": 2,
// 禁用八进制字面量
"no-octal": 2,
// 禁止在字符串中使用八进制转义序列
"no-octal-escape": 2,
// 不允许对 function 的参数进行重新赋值
"no-param-reassign": 0,
// 禁用 __proto__ 属性
"no-proto": 2,
// 禁止使用 var 多次声明同一变量
"no-redeclare": 2,
// 禁用指定的通过 require 加载的模块
"no-return-assign": 0,
// 禁止使用 javascript: url
"no-script-url": 0,
// 禁止自我赋值
"no-self-assign": 2,
// 禁止自身比较
"no-self-compare": 2,
// 禁用逗号操作符
"no-sequences": 2,
// 禁止抛出非异常字面量
"no-throw-literal": 2,
// 禁用一成不变的循环条件
"no-unmodified-loop-condition": 2,
// 禁止出现未使用过的表达式
"no-unused-expressions": 0,
// 禁用未使用过的标签
"no-unused-labels": 2,
// 禁止不必要的 .call() 和 .apply()
"no-useless-call": 2,
// 禁止不必要的字符串字面量或模板字面量的连接
"no-useless-concat": 0,
// 禁用不必要的转义字符
"no-useless-escape": 0,
// 禁用 void 操作符
"no-void": 0,
// 禁止在注释中使用特定的警告术语
"no-warning-comments": 0,
// 禁用 with 语句
"no-with": 2,
// 强制在parseInt()使用基数参数
"radix": 2,
// 要求所有的 var 声明出现在它们所在的作用域顶部
"vars-on-top": 0,
// 要求 IIFE 使用括号括起来
"wrap-iife": [2, "any"],
// 要求或禁止 “Yoda” 条件
"yoda": [2, "never"],
// 要求或禁止使用严格模式指令
"strict": 0,
//////////////
// 变量声明 //
//////////////
// 要求或禁止 var 声明中的初始化(初值)
"init-declarations": 0,
// 不允许 catch 子句的参数与外层作用域中的变量同名
"no-catch-shadow": 0,
// 禁止删除变量
"no-delete-var": 2,
// 不允许标签与变量同名
"no-label-var": 2,
// 禁用特定的全局变量
"no-restricted-globals": 0,
// 禁止 var 声明 与外层作用域的变量同名
"no-shadow": 0,
// 禁止覆盖受限制的标识符
"no-shadow-restricted-names": 2,
// 禁用未声明的变量,除非它们在 /*global */ 注释中被提到
"no-undef": 2,
// 禁止将变量初始化为 undefined
"no-undef-init": 2,
// 禁止将 undefined 作为标识符
"no-undefined": 0,
// 禁止出现未使用过的变量
"no-unused-vars": [2, {
"vars": "all",
"args": "none"
}],
// 不允许在变量定义之前使用它们
"no-use-before-define": 0,
//////////////////////////
// Node.js and CommonJS //
//////////////////////////
// require return statements after callbacks
"callback-return": 0,
// 要求 require() 出现在顶层模块作用域中
"global-require": 1,
// 要求回调函数中有容错处理
"handle-callback-err": [2, "^(err|error)$"],
// 禁止混合常规 var 声明和 require 调用
"no-mixed-requires": 0,
// 禁止调用 require 时使用 new 操作符
"no-new-require": 2,
// 禁止对 __dirname 和 __filename进行字符串连接
"no-path-concat": 0,
// 禁用 process.env
"no-process-env": 0,
// 禁用 process.exit()
"no-process-exit": 0,
// 禁用同步方法
"no-sync": 0,
//////////////
// 风格指南 //
//////////////
// 指定数组的元素之间要以空格隔开(, 后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
"array-bracket-spacing": [2, "never"],
// 禁止或强制在单行代码块中使用空格(禁用)
"block-spacing": [1, "never"],
//强制使用一致的缩进 第二个参数为 "tab" 时,会使用tab,
// if while function 后面的{必须与if在同一行,java风格。
"brace-style": [2, "1tbs", {
"allowSingleLine": true
}],
// 双峰驼命名格式
"camelcase": 2,
// 控制逗号前后的空格
"comma-spacing": [2, {
"before": false,
"after": true
}],
// 控制逗号在行尾出现还是在行首出现 (默认行尾)
// http://eslint.org/docs/rules/comma-style
"comma-style": [2, "last"],
//"SwitchCase" (默认:0) 强制 switch 语句中的 case 子句的缩进水平
// 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
"computed-property-spacing": [2, "never"],
// 用于指统一在回调函数中指向this的变量名,箭头函数中的this已经可以指向外层调用者,应该没卵用了
// e.g [0,"that"] 指定只能 var that = this. that不能指向其他任何值,this也不能赋值给that以外的其他值
"consistent-this": [1, "that"],
// 强制使用命名的 function 表达式
"func-names": 0,
// 文件末尾强制换行
"eol-last": 2,
"indent": [2, 2, {
"SwitchCase": 1
}],
// 强制在对象字面量的属性中键和值之间使用一致的间距
"key-spacing": [2, {
"beforeColon": false,
"afterColon": true
}],
// 强制使用一致的换行风格
"linebreak-style": [1, "unix"],
// 要求在注释周围有空行 ( 要求在块级注释之前有一空行)
"lines-around-comment": [1, {
"beforeBlockComment": true
}],
// 强制一致地使用函数声明或函数表达式,方法定义风格,参数:
// declaration: 强制使用方法声明的方式,function f(){} e.g [2, "declaration"]
// expression:强制使用方法表达式的方式,var f = function() {} e.g [2, "expression"]
// allowArrowFunctions: declaration风格中允许箭头函数。 e.g [2, "declaration", { "allowArrowFunctions": true }]
"func-style": 0,
// 强制回调函数最大嵌套深度 5层
"max-nested-callbacks": [1, 5],
// 禁止使用指定的标识符
"id-blacklist": 0,
// 强制标识符的最新和最大长度
"id-length": 0,
// 要求标识符匹配一个指定的正则表达式
"id-match": 0,
// 强制在 JSX 属性中一致地使用双引号或单引号
"jsx-quotes": 0,
// 强制在关键字前后使用一致的空格 (前后腰需要)
"keyword-spacing": 2,
// 强制一行的最大长度
"max-len": [1, 200],
// 强制最大行数
"max-lines": 0,
// 强制 function 定义中最多允许的参数数量
"max-params": [1, 7],
// 强制 function 块最多允许的的语句数量
"max-statements": [1, 200],
// 强制每一行中所允许的最大语句数量
"max-statements-per-line": 0,
// 要求构造函数首字母大写 (要求调用 new 操作符时有首字母大小的函数,允许调用首字母大写的函数时没有 new 操作符。)
"new-cap": [2, {
"newIsCap": true,
"capIsNew": false
}],
// 要求调用无参构造函数时有圆括号
"new-parens": 2,
// 要求或禁止 var 声明语句后有一行空行
"newline-after-var": 0,
// 禁止使用 Array 构造函数
"no-array-constructor": 2,
// 禁用按位运算符
"no-bitwise": 0,
// 要求 return 语句之前有一空行
"newline-before-return": 0,
// 要求方法链中每个调用都有一个换行符
"newline-per-chained-call": 1,
// 禁用 continue 语句
"no-continue": 0,
// 禁止在代码行后使用内联注释
"no-inline-comments": 0,
// 禁止 if 作为唯一的语句出现在 else 语句中
"no-lonely-if": 0,
// 禁止混合使用不同的操作符
"no-mixed-operators": 0,
// 不允许空格和 tab 混合缩进
"no-mixed-spaces-and-tabs": 2,
// 不允许多个空行
"no-multiple-empty-lines": [2, {
"max": 2
}],
// 不允许否定的表达式
"no-negated-condition": 0,
// 不允许使用嵌套的三元表达式
"no-nested-ternary": 0,
// 禁止使用 Object 的构造函数
"no-new-object": 2,
// 禁止使用一元操作符 ++ 和 --
"no-plusplus": 0,
// 禁止使用特定的语法
"no-restricted-syntax": 0,
// 禁止 function 标识符和括号之间出现空格
"no-spaced-func": 2,
// 不允许使用三元操作符
"no-ternary": 0,
// 禁用行尾空格
"no-trailing-spaces": 2,
// 禁止标识符中有悬空下划线_bar
"no-underscore-dangle": 0,
// 禁止可以在有更简单的可替代的表达式时使用三元操作符
"no-unneeded-ternary": 2,
// 禁止属性前有空白
"no-whitespace-before-property": 0,
// 强制花括号内换行符的一致性
"object-curly-newline": 0,
// 强制在花括号中使用一致的空格
"object-curly-spacing": 0,
// 强制将对象的属性放在不同的行上
"object-property-newline": 0,
// 强制函数中的变量要么一起声明要么分开声明
"one-var": [2, {
"initialized": "never"
}],
// 要求或禁止在 var 声明周围换行
"one-var-declaration-per-line": 0,
// 要求或禁止在可能的情况下要求使用简化的赋值操作符
"operator-assignment": 0,
// 强制操作符使用一致的换行符
"operator-linebreak": [2, "after", {
"overrides": {
"?": "before",
":": "before"
}
}],
// 要求或禁止块内填充
"padded-blocks": 0,
// 要求对象字面量属性名称用引号括起来
"quote-props": 0,
// 强制使用一致的反勾号、双引号或单引号
// "quotes": [2, "single","double", "avoid-escape"],
// 要求使用 JSDoc 注释
"require-jsdoc": 0,
// 要求或禁止使用分号而不是 ASI(这个才是控制行尾部分号的,)
"semi": [0, "always"],
// 强制分号之前和之后使用一致的空格
"semi-spacing": 0,
// 要求同一个声明块中的变量按顺序排列
"sort-vars": 0,
// 强制在块之前使用一致的空格
"space-before-blocks": [2, "always"],
// 强制在 function的左括号之前使用一致的空格
"space-before-function-paren": [0, "always"],
// 强制在圆括号内使用一致的空格
"space-in-parens": [2, "never"],
// 要求操作符周围有空格
"space-infix-ops": 2,
// 强制在一元操作符前后使用一致的空格
"space-unary-ops": [2, {
"words": true,
"nonwords": false
}],
// 强制在注释中 // 或 /* 使用一致的空格
"spaced-comment": [2, "always", {
"markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"]
}],
// 要求或禁止 Unicode BOM
"unicode-bom": 0,
// 要求正则表达式被括号括起来
"wrap-regex": 0,
//////////////
// ES6.相关 //
//////////////
// 要求箭头函数体使用大括号
"arrow-body-style": 2,
// 要求箭头函数的参数使用圆括号
"arrow-parens": 0,
"arrow-spacing": [2, {
"before": true,
"after": true
}],
// 强制在子类构造函数中用super()调用父类构造函数,TypeScrip的编译器也会提示
"constructor-super": 0,
// 强制 generator 函数中 * 号周围使用一致的空格
"generator-star-spacing": [2, {
"before": true,
"after": true
}],
// 禁止修改类声明的变量
"no-class-assign": 2,
// 不允许箭头功能,在那里他们可以混淆的比较
"no-confusing-arrow": 0,
// 禁止修改 const 声明的变量
"no-const-assign": 2,
// 禁止类成员中出现重复的名称
"no-dupe-class-members": 2,
// 不允许复制模块的进口
"no-duplicate-imports": 0,
// 禁止 Symbol 的构造函数
"no-new-symbol": 2,
// 允许指定模块加载时的进口
"no-restricted-imports": 0,
// 禁止在构造函数中,在调用 super() 之前使用 this 或 super
"no-this-before-super": 2,
// 禁止不必要的计算性能键对象的文字
"no-useless-computed-key": 0,
// 要求使用 let 或 const 而不是 var
"no-var": 0,
// 要求或禁止对象字面量中方法和属性使用简写语法
"object-shorthand": 0,
// 要求使用箭头函数作为回调
"prefer-arrow-callback": 0,
// 要求使用 const 声明那些声明后不再被修改的变量
"prefer-const": 0,
// 要求在合适的地方使用 Reflect 方法
"prefer-reflect": 0,
// 要求使用扩展运算符而非 .apply()
"prefer-spread": 0,
// 要求使用模板字面量而非字符串连接
"prefer-template": 0,
// Suggest using the rest parameters instead of arguments
"prefer-rest-params": 0,
// 要求generator 函数内有 yield
"require-yield": 0,
// enforce spacing between rest and spread operators and their expressions
"rest-spread-spacing": 0,
// 强制模块内的 import 排序
"sort-imports": 0,
// 要求或禁止模板字符串中的嵌入表达式周围空格的使用
"template-curly-spacing": 1,
// 强制在 yield* 表达式中 * 周围使用空格
"yield-star-spacing": 2
}
}
================================================
FILE: packages/url-loader/.gitattributes
================================================
package-lock.json -diff
* text=auto
bin/* eol=lf
================================================
FILE: packages/url-loader/.github/CODEOWNERS
================================================
# These are the default owners for everything in
# webpack-contrib
@webpack-contrib/org-maintainers
# Add repository specific users / groups
# below here for libs that are not maintained by the org.
================================================
FILE: packages/url-loader/.github/CONTRIBUTING.md
================================================
## Contributing in @webpack-contrib
We'd always love contributions to further improve the webpack / webpack-contrib ecosystem!
Here are the guidelines we'd like you to follow:
* [Questions and Problems](#question)
* [Issues and Bugs](#issue)
* [Feature Requests](#feature)
* [Pull Request Submission Guidelines](#submit-pr)
* [Commit Message Conventions](#commit)
### Got a Question or Problem?
Please submit support requests and questions to StackOverflow using the tag [[webpack]](http://stackoverflow.com/tags/webpack).
StackOverflow is better suited for this kind of support though you may also inquire in [Webpack Gitter](https://gitter.im/webpack/webpack).
The issue tracker is for bug reports and feature discussions.
### Found an Issue or Bug?
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we ask that you to provide a minimal reproduction scenario (github repo or failing test case). Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
- version of Webpack used
- version of the loader / plugin you are creating a bug report for
- the use-case that fails
A minimal reproduce scenario allows us to quickly confirm a bug (or point out config problems) as well as confirm that we are fixing the right problem.
We will be insisting on a minimal reproduce scenario in order to save maintainers time and ultimately be able to fix more bugs. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
### Feature Requests?
You can *request* a new feature by creating an issue on Github.
If you would like to *implement* a new feature, please submit an issue with a proposal for your work `first`, to be sure that particular makes sense for the project.
### Pull Request Submission Guidelines
Before you submit your Pull Request (PR) consider the following guidelines:
- Search Github for an open or closed PR that relates to your submission. You don't want to duplicate effort.
- Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
- Fill out our `Pull Request Template`. Your pull request will not be considered if it is ignored.
- Please sign the `Contributor License Agreement (CLA)` when a pull request is opened. We cannot accept your pull request without this. Make sure you sign with the primary email address associated with your local / github account.
### Webpack Contrib Commit Conventions
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```
():