Repository: didi/chameleon Branch: master Commit: 3531c7444cc2 Files: 1333 Total size: 4.4 MB Directory structure: gitextract_si943cgs/ ├── .eslintrc.js ├── .gitattributes ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── elevate/ │ └── cr.yml ├── lerna.json ├── package.json ├── packages/ │ ├── babel-plugin-chameleon-import/ │ │ ├── .editorconfig │ │ ├── .eslintignore │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── .travis.yml │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Plugin.js │ │ │ └── index.js │ │ └── test/ │ │ └── fixtures/ │ │ ├── array-expression/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── as-arguments/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── as-arguments-identifier/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── binary-expression/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── conditions/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── custom-name/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── custom-style-path/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── custom-style-path-ignore/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── execute-direct/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── execute-member/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── export-import/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── expression-statement/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── file-name/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── import-alias/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── import-css/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── keep-named-import/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── material-ui/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── member-expression/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── modules-false/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── multiple-libraries/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── multiple-libraries-hilojs/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── multiple-words/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── new-expression/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── object-shorthand/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── property/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── react-element/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── react-toolbox/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── return/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── specifier-alias/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── use-multiple-times/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ ├── variable-declarator/ │ │ │ ├── actual.js │ │ │ └── expected.js │ │ └── variable-scope/ │ │ ├── actual.js │ │ └── expected.js │ ├── chameleon-css-loader/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── handler/ │ │ │ └── lines.js │ │ ├── index.js │ │ ├── package.json │ │ ├── parser/ │ │ │ ├── media.js │ │ │ ├── miniapp.js │ │ │ ├── web.js │ │ │ └── weex.js │ │ ├── postcss/ │ │ │ ├── add-alipay-class.js │ │ │ ├── cpx.js │ │ │ ├── weex-plus.js │ │ │ └── weex.js │ │ ├── proxy/ │ │ │ ├── proxyMiniapp.js │ │ │ └── proxyWeb.js │ │ ├── test/ │ │ │ ├── parser-test/ │ │ │ │ ├── media.test.js │ │ │ │ ├── miniapp.test.js │ │ │ │ ├── web.test.js │ │ │ │ └── weex.test.js │ │ │ ├── postcss-test/ │ │ │ │ └── weex-plus.test.js │ │ │ ├── proxy-test/ │ │ │ │ ├── proxyMiniapp.test.js │ │ │ │ └── proxyWeb.test.js │ │ │ ├── transform/ │ │ │ │ └── weex.test.js │ │ │ └── utils.test.js │ │ ├── transform/ │ │ │ ├── color.js │ │ │ └── weex.js │ │ └── utils.js │ ├── chameleon-dev-proxy/ │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json │ ├── chameleon-errors-webpack-plugin/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── index.js │ │ ├── package.json │ │ └── src/ │ │ ├── core/ │ │ │ ├── extractWebpackError.js │ │ │ ├── formatErrors.js │ │ │ └── transformErrors.js │ │ ├── formatters/ │ │ │ ├── defaultError.js │ │ │ ├── eslintError.js │ │ │ └── moduleNotFound.js │ │ ├── friendly-errors-plugin.js │ │ ├── output.js │ │ ├── transformers/ │ │ │ ├── babelSyntax.js │ │ │ ├── esLintError.js │ │ │ └── moduleNotFound.js │ │ └── utils/ │ │ ├── colors.js │ │ └── index.js │ ├── chameleon-linter/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── bin/ │ │ │ └── cli.js │ │ ├── checkers/ │ │ │ ├── index.js │ │ │ ├── json.js │ │ │ ├── script.js │ │ │ ├── style.js │ │ │ └── template/ │ │ │ ├── index.js │ │ │ └── lib/ │ │ │ ├── js-ast-parser.js │ │ │ ├── json-ast-parser.js │ │ │ └── template-ast-parser/ │ │ │ ├── classes/ │ │ │ │ ├── customized-node.js │ │ │ │ ├── rule-loader.js │ │ │ │ └── suspicious-node.js │ │ │ ├── index.js │ │ │ ├── lib/ │ │ │ │ ├── ast-tree-traversal.js │ │ │ │ ├── customized-node-dispatcher.js │ │ │ │ └── suspicious-node-dispatcher.js │ │ │ ├── options.js │ │ │ ├── rules/ │ │ │ │ ├── component/ │ │ │ │ │ ├── attr.js │ │ │ │ │ ├── cml-method-node.js │ │ │ │ │ ├── cml-prop-node.js │ │ │ │ │ ├── cml.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── vue-method-node.js │ │ │ │ │ ├── vue-prop-node.js │ │ │ │ │ └── vue.js │ │ │ │ └── template/ │ │ │ │ ├── cml.js │ │ │ │ ├── index.js │ │ │ │ ├── method-node.js │ │ │ │ ├── mustache-node.js │ │ │ │ ├── vue-directive-node.js │ │ │ │ └── vue.js │ │ │ └── tools/ │ │ │ ├── dash-to-camelcase.js │ │ │ ├── flat-array.js │ │ │ ├── get-offset-pos-from-text.js │ │ │ ├── get-props-from-object-expression.js │ │ │ ├── index.js │ │ │ ├── is-origin-component.js │ │ │ ├── is-suspicious-textnode.js │ │ │ ├── loop-scope-handler.js │ │ │ ├── parse-single-expression.js │ │ │ └── rule-filter-match.js │ │ ├── classes/ │ │ │ └── message.js │ │ ├── config/ │ │ │ ├── .cmllintrc │ │ │ ├── built-in-components/ │ │ │ │ └── index.js │ │ │ ├── common-events.json │ │ │ ├── fakeComps/ │ │ │ │ └── index.js │ │ │ ├── globalVars.js │ │ │ ├── index.js │ │ │ ├── parser-config.js │ │ │ ├── tag-embed-rules.json │ │ │ └── white-list/ │ │ │ ├── cml-white-list.js │ │ │ ├── index.js │ │ │ ├── vue-white-list.js │ │ │ ├── web-white-list.js │ │ │ ├── weex-white-list.js │ │ │ └── wx-white-list.js │ │ ├── docs/ │ │ │ ├── cml-file-specification.md │ │ │ ├── components/ │ │ │ │ ├── buildin/ │ │ │ │ │ ├── button.md │ │ │ │ │ ├── checkbox.md │ │ │ │ │ ├── input.md │ │ │ │ │ ├── list.md │ │ │ │ │ ├── page.md │ │ │ │ │ ├── radio.md │ │ │ │ │ ├── richtext.md │ │ │ │ │ ├── scroller.md │ │ │ │ │ ├── switch.md │ │ │ │ │ ├── textarea.md │ │ │ │ │ └── video.md │ │ │ │ └── chameleon-ui/ │ │ │ │ ├── c-actionsheet.md │ │ │ │ ├── c-aside.md │ │ │ │ ├── c-checkbox-group.md │ │ │ │ ├── c-col.md │ │ │ │ ├── c-container.md │ │ │ │ ├── c-dialog.md │ │ │ │ ├── c-foot.md │ │ │ │ ├── c-form-item.md │ │ │ │ ├── c-form.md │ │ │ │ ├── c-head.md │ │ │ │ ├── c-main.md │ │ │ │ ├── c-picker-item.md │ │ │ │ ├── c-picker-panel.md │ │ │ │ ├── c-picker.md │ │ │ │ ├── c-popup.md │ │ │ │ ├── c-radio-group.md │ │ │ │ ├── c-refresh.md │ │ │ │ ├── c-row.md │ │ │ │ ├── c-tab-pane-item.md │ │ │ │ ├── c-tab-pane.md │ │ │ │ ├── c-tabs-item.md │ │ │ │ ├── c-tabs.md │ │ │ │ ├── c-tip.md │ │ │ │ └── c-toast.md │ │ │ ├── directory-specification.md │ │ │ ├── interface-specification.md │ │ │ └── templates/ │ │ │ ├── template-functionalities.md │ │ │ ├── template-internal.md │ │ │ └── template.md │ │ ├── file-spec.js │ │ ├── file-structure.js │ │ ├── index.js │ │ ├── linters/ │ │ │ ├── index.js │ │ │ ├── json.js │ │ │ ├── script.js │ │ │ ├── style.js │ │ │ └── template.js │ │ ├── package.json │ │ ├── test/ │ │ │ ├── checker/ │ │ │ │ └── cml/ │ │ │ │ ├── json/ │ │ │ │ │ ├── nonstandard.cml │ │ │ │ │ ├── nonstandard.interface │ │ │ │ │ ├── nonstandard.weex.cml │ │ │ │ │ ├── nonstandard.wx.cml │ │ │ │ │ └── standard.cml │ │ │ │ └── script/ │ │ │ │ ├── global-variable/ │ │ │ │ │ ├── standard.interface │ │ │ │ │ ├── standard.web.cml │ │ │ │ │ ├── standard.weex.cml │ │ │ │ │ └── standard.wx.cml │ │ │ │ ├── include/ │ │ │ │ │ ├── include-interface-fail.interface │ │ │ │ │ ├── include-src-cml-fail.interface │ │ │ │ │ ├── include-src-cml-mis-prop-fail.interface │ │ │ │ │ ├── include-src-js-class-name-fail.interface │ │ │ │ │ ├── include-src-js-fail.interface │ │ │ │ │ ├── include-src-js-mis-prop-fail.interface │ │ │ │ │ ├── origin-comp-interface.interface │ │ │ │ │ ├── origin-interface.interface │ │ │ │ │ └── src/ │ │ │ │ │ ├── someplatform-mis-class.js │ │ │ │ │ ├── someplatform-mis-prop.cml │ │ │ │ │ └── someplatform-mis-prop.js │ │ │ │ ├── interfaces/ │ │ │ │ │ └── prop-not-defined.interface │ │ │ │ ├── no-global-variable/ │ │ │ │ │ ├── standard.interface │ │ │ │ │ ├── standard.web.cml │ │ │ │ │ ├── standard.weex.cml │ │ │ │ │ └── standard.wx.cml │ │ │ │ ├── nointerface/ │ │ │ │ │ └── nonstandard.wx.cml │ │ │ │ ├── nonstandard.cml │ │ │ │ ├── properties-methods/ │ │ │ │ │ ├── event-not-defined.interface │ │ │ │ │ ├── event-not-defined.web.cml │ │ │ │ │ ├── property-not-defined.interface │ │ │ │ │ └── property-not-defined.wx.cml │ │ │ │ ├── standard/ │ │ │ │ │ ├── standard.interface │ │ │ │ │ ├── standard.web.cml │ │ │ │ │ ├── standard.weex.cml │ │ │ │ │ └── standard.wx.cml │ │ │ │ └── standard.cml │ │ │ ├── cml.test.js │ │ │ ├── config.test.js │ │ │ ├── core/ │ │ │ │ └── standard/ │ │ │ │ ├── chameleon.config.js │ │ │ │ └── src/ │ │ │ │ ├── app/ │ │ │ │ │ └── app.cml │ │ │ │ ├── router.config.json │ │ │ │ └── store/ │ │ │ │ └── index.js │ │ │ ├── core.test.js │ │ │ ├── interface/ │ │ │ │ └── common.interface │ │ │ ├── interface.test.js │ │ │ ├── linter/ │ │ │ │ └── cml/ │ │ │ │ ├── cml/ │ │ │ │ │ ├── nointerface/ │ │ │ │ │ │ └── standard.web.cml │ │ │ │ │ ├── nonstandard.cml │ │ │ │ │ ├── standard/ │ │ │ │ │ │ ├── standard.interface │ │ │ │ │ │ ├── standard.web.cml │ │ │ │ │ │ ├── standard.weex.cml │ │ │ │ │ │ └── standard.wx.cml │ │ │ │ │ ├── standard.cml │ │ │ │ │ └── syntaxError/ │ │ │ │ │ ├── standard.interface │ │ │ │ │ ├── standard.web.cml │ │ │ │ │ ├── standard.weex.cml │ │ │ │ │ └── standard.wx.cml │ │ │ │ ├── json/ │ │ │ │ │ ├── .cmllintrc │ │ │ │ │ ├── no-bracket.cml │ │ │ │ │ ├── no-comma.cml │ │ │ │ │ ├── no-quotes.cml │ │ │ │ │ └── standard.cml │ │ │ │ ├── script/ │ │ │ │ │ ├── nonstandard-arrow.cml │ │ │ │ │ ├── nonstandard.cml │ │ │ │ │ └── standard.cml │ │ │ │ └── style/ │ │ │ │ ├── .cmllintrc │ │ │ │ ├── nest.cml │ │ │ │ ├── no-bracket.cml │ │ │ │ ├── no-semicolon.cml │ │ │ │ ├── no-standard-important.cml │ │ │ │ ├── no-standard.stylus.cml │ │ │ │ ├── standard.cml │ │ │ │ └── standard.stylus.cml │ │ │ ├── lintrc/ │ │ │ │ ├── nonstandard/ │ │ │ │ │ └── .cmllintrc │ │ │ │ └── standard/ │ │ │ │ └── .cmllintrc │ │ │ ├── template/ │ │ │ │ ├── checker/ │ │ │ │ │ ├── template-lib-class.test.js │ │ │ │ │ ├── template-lib-export.test.js │ │ │ │ │ ├── template-lib-js.test.js │ │ │ │ │ ├── template-lib-json.test.js │ │ │ │ │ ├── template-lib-template.test.js │ │ │ │ │ └── template.test.js │ │ │ │ ├── docs/ │ │ │ │ │ ├── check/ │ │ │ │ │ │ ├── fail/ │ │ │ │ │ │ │ ├── index-props-events-cml.cml │ │ │ │ │ │ │ ├── index-props-events-vue.cml │ │ │ │ │ │ │ ├── index-props-methods-polymorphic.cml │ │ │ │ │ │ │ ├── index-props-methods-single.cml │ │ │ │ │ │ │ ├── index-vars-events.cml │ │ │ │ │ │ │ ├── index-vars-methods-cml.cml │ │ │ │ │ │ │ └── index-vars-methods-vue.cml │ │ │ │ │ │ └── success/ │ │ │ │ │ │ ├── index-lib-check-class.cml │ │ │ │ │ │ ├── index-lib-check-export.cml │ │ │ │ │ │ ├── index-lib-check-implements.cml │ │ │ │ │ │ ├── index-lib-check-plugin.cml │ │ │ │ │ │ ├── index-lib-check-vue-class.cml │ │ │ │ │ │ ├── index-lib-js-class.cml │ │ │ │ │ │ ├── index-lib-js-export.cml │ │ │ │ │ │ ├── index-lib-json-polymorphic.cml │ │ │ │ │ │ ├── index-lib-json-single.cml │ │ │ │ │ │ ├── index-lib-template-cml.cml │ │ │ │ │ │ └── index-lib-template-vue.cml │ │ │ │ │ └── components/ │ │ │ │ │ ├── c-checkbox/ │ │ │ │ │ │ └── c-checkbox.cml │ │ │ │ │ ├── radio/ │ │ │ │ │ │ └── radio.cml │ │ │ │ │ ├── scroller/ │ │ │ │ │ │ ├── scroller.interface │ │ │ │ │ │ ├── scroller.web.cml │ │ │ │ │ │ ├── scroller.weex.cml │ │ │ │ │ │ └── scroller.wx.cml │ │ │ │ │ ├── show-component/ │ │ │ │ │ │ ├── scroller.interface │ │ │ │ │ │ ├── scroller.web.cml │ │ │ │ │ │ ├── scroller.weex.cml │ │ │ │ │ │ └── scroller.wx.cml │ │ │ │ │ └── single/ │ │ │ │ │ └── single.cml │ │ │ │ └── linter/ │ │ │ │ ├── cases/ │ │ │ │ │ ├── fail/ │ │ │ │ │ │ ├── built-in-embed-rule-excludes-error.js │ │ │ │ │ │ ├── built-in-embed-rule-includes-error.js │ │ │ │ │ │ ├── built-in-props-error.js │ │ │ │ │ │ ├── built-in-props-lang-vue-error.js │ │ │ │ │ │ ├── directive-cml-error.js │ │ │ │ │ │ ├── directive-vue-error.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── lang-error.js │ │ │ │ │ │ ├── origin-component-cml-directive-forbiden-error.js │ │ │ │ │ │ ├── origin-component-skip-rules-error.js │ │ │ │ │ │ ├── platform-specific-tags-error.js │ │ │ │ │ │ ├── template-tag-error.js │ │ │ │ │ │ └── usingcomponent-ref-error.js │ │ │ │ │ └── pass/ │ │ │ │ │ ├── built-in-embed-rule-excludes-pass.js │ │ │ │ │ ├── built-in-embed-rule-includes-pass.js │ │ │ │ │ ├── built-in-props-lang-vue-pass.js │ │ │ │ │ ├── built-in-props-pass.js │ │ │ │ │ ├── component-is-pass.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── lang-pass.js │ │ │ │ │ ├── origin-component-skip-rules-pass.js │ │ │ │ │ ├── platform-specific-tags-pass.js │ │ │ │ │ ├── tag-close.js │ │ │ │ │ └── usingcomponent-ref-pass.js │ │ │ │ └── template.test.js │ │ │ └── utils.test.js │ │ └── utils.js │ ├── chameleon-loader/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── .scripts/ │ │ │ └── version.js │ │ ├── LICENSE │ │ ├── gulpfile.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── cml-compile/ │ │ │ │ ├── handle-platform-css.js │ │ │ │ ├── json-handle.js │ │ │ │ ├── runtime/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── common/ │ │ │ │ │ │ └── util.js │ │ │ │ │ ├── component.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── page.js │ │ │ │ │ ├── web/ │ │ │ │ │ │ ├── app.js │ │ │ │ │ │ ├── component.js │ │ │ │ │ │ ├── page.js │ │ │ │ │ │ └── util.js │ │ │ │ │ ├── weex/ │ │ │ │ │ │ └── util.js │ │ │ │ │ └── wx/ │ │ │ │ │ └── util.js │ │ │ │ └── wxml-selector.js │ │ │ ├── helpers.js │ │ │ ├── index.js │ │ │ ├── interface-check/ │ │ │ │ ├── check.js │ │ │ │ └── getScriptCode.js │ │ │ ├── load-icon.js │ │ │ ├── loader.js │ │ │ ├── loaderMethods.js │ │ │ ├── miniapp-script.js │ │ │ ├── parser.js │ │ │ ├── runtime/ │ │ │ │ ├── check.js │ │ │ │ └── component-normalizer.js │ │ │ ├── selector.js │ │ │ └── utils/ │ │ │ ├── normalize.js │ │ │ ├── prehandle.js │ │ │ └── try-require.js │ │ └── test/ │ │ └── src/ │ │ ├── cml-compile.test/ │ │ │ └── runtime/ │ │ │ ├── all-properties.js │ │ │ ├── common-utils.test.js │ │ │ ├── web-utils.test.js │ │ │ ├── weex-utils.test.js │ │ │ └── wx-utils.test.js │ │ ├── load-icon.test.js │ │ ├── loader.test.js │ │ ├── loaderMethods.test.js │ │ ├── parser.cml │ │ ├── parser.test.js │ │ ├── project/ │ │ │ └── src/ │ │ │ ├── components/ │ │ │ │ └── coma.cml │ │ │ └── pages/ │ │ │ └── pagea.cml │ │ └── utils.test/ │ │ ├── normalize.test.js │ │ ├── prehandle.test.js │ │ └── try-require.test.js │ ├── chameleon-miniapp-target/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── gulpfile.js │ │ ├── package.json │ │ └── src/ │ │ ├── JsonpChunkTemplatePlugin.js │ │ ├── JsonpHotUpdateChunkTemplatePlugin.js │ │ ├── JsonpMainTemplatePlugin.js │ │ ├── JsonpTemplatePlugin.js │ │ └── index.js │ ├── chameleon-mixins/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── alipay-mixins.js │ │ ├── common.js │ │ ├── miniapp-utils/ │ │ │ └── px2cpx.js │ │ ├── package.json │ │ ├── test/ │ │ │ ├── common.test.js │ │ │ ├── utils.test.js │ │ │ ├── web-mixins.test.js │ │ │ ├── weex-mixins.test.js │ │ │ └── wx-mixins.test.js │ │ ├── utils.js │ │ ├── web-mixins.js │ │ ├── web-utils/ │ │ │ └── px2cpx.js │ │ ├── weex-mixins.js │ │ ├── wx-alipay-common-mixins.js │ │ └── wx-mixins.js │ ├── chameleon-template-parse/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── gulpfile.js │ │ ├── index.html │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── common/ │ │ │ │ ├── cml-map.js │ │ │ │ ├── process-template.js │ │ │ │ └── utils.js │ │ │ ├── compile-template-cml.js │ │ │ ├── compile-template-vue.js │ │ │ ├── index.js │ │ │ └── parser/ │ │ │ ├── index.js │ │ │ ├── parse-animation-tag.js │ │ │ ├── parse-attribute.js │ │ │ ├── parse-class.js │ │ │ ├── parse-condition.js │ │ │ ├── parse-directive.js │ │ │ ├── parse-event.js │ │ │ ├── parse-interation.js │ │ │ ├── parse-ref.js │ │ │ ├── parse-style.js │ │ │ └── parse-vue2wx.js │ │ ├── test/ │ │ │ ├── common/ │ │ │ │ ├── process-template.test.js │ │ │ │ └── utils.test.js │ │ │ ├── index.js │ │ │ └── parser/ │ │ │ ├── cml.test.js │ │ │ ├── index.cml.test.js │ │ │ ├── index.vue.test.js │ │ │ └── vue.test.js │ │ └── webpack.config.js │ ├── chameleon-templates/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── component/ │ │ │ ├── component/ │ │ │ │ └── index.cml │ │ │ ├── interface-component/ │ │ │ │ ├── index.alipay.cml │ │ │ │ ├── index.baidu.cml │ │ │ │ ├── index.interface │ │ │ │ ├── index.qq.cml │ │ │ │ ├── index.tt.cml │ │ │ │ ├── index.web.cml │ │ │ │ ├── index.weex.cml │ │ │ │ └── index.wx.cml │ │ │ └── interface-js/ │ │ │ └── index.interface │ │ ├── index.js │ │ ├── package.json │ │ ├── page/ │ │ │ └── index.cml │ │ ├── project/ │ │ │ ├── .gitignore │ │ │ ├── chameleon.config.js │ │ │ ├── mock/ │ │ │ │ ├── api/ │ │ │ │ │ └── index.js │ │ │ │ └── template/ │ │ │ │ └── index.php │ │ │ ├── package.json │ │ │ └── src/ │ │ │ ├── app/ │ │ │ │ └── app.cml │ │ │ ├── components/ │ │ │ │ └── demo-com/ │ │ │ │ └── demo-com.cml │ │ │ ├── pages/ │ │ │ │ └── index/ │ │ │ │ └── index.cml │ │ │ ├── router.config.json │ │ │ └── store/ │ │ │ ├── actions.js │ │ │ ├── getters.js │ │ │ ├── index.js │ │ │ ├── mutations.js │ │ │ └── state.js │ │ ├── server/ │ │ │ ├── fisdata/ │ │ │ │ ├── FISData.class.php │ │ │ │ ├── Manager.class.php │ │ │ │ ├── README.md │ │ │ │ ├── TestData.class.php │ │ │ │ ├── bookmark.js │ │ │ │ ├── index.tpl │ │ │ │ ├── libs/ │ │ │ │ │ ├── JsonPro/ │ │ │ │ │ │ ├── CommonHelper.php │ │ │ │ │ │ ├── Demo.php │ │ │ │ │ │ ├── adocParser.php │ │ │ │ │ │ ├── genArray.php │ │ │ │ │ │ ├── genCase.php │ │ │ │ │ │ ├── genConf.php │ │ │ │ │ │ ├── genFile.php │ │ │ │ │ │ ├── genInterface.php │ │ │ │ │ │ ├── genLog.php │ │ │ │ │ │ └── genValue.php │ │ │ │ │ └── Util.class.php │ │ │ │ ├── package.json │ │ │ │ └── plugin/ │ │ │ │ ├── ChameleonData.class.php │ │ │ │ ├── FISADOCData.class.php │ │ │ │ ├── FISJSONData.class.php │ │ │ │ ├── FISPHPData.class.php │ │ │ │ ├── gen_plugins.php.sh │ │ │ │ └── plugins.php │ │ │ ├── index.php │ │ │ ├── rewrite/ │ │ │ │ ├── README.md │ │ │ │ └── Rewrite.php │ │ │ ├── smarty/ │ │ │ │ ├── Smarty.class.php │ │ │ │ ├── SmartyBC.class.php │ │ │ │ ├── debug.tpl │ │ │ │ ├── plugins/ │ │ │ │ │ ├── block.textformat.php │ │ │ │ │ ├── function.counter.php │ │ │ │ │ ├── function.cycle.php │ │ │ │ │ ├── function.fetch.php │ │ │ │ │ ├── function.html_checkboxes.php │ │ │ │ │ ├── function.html_image.php │ │ │ │ │ ├── function.html_options.php │ │ │ │ │ ├── function.html_radios.php │ │ │ │ │ ├── function.html_select_date.php │ │ │ │ │ ├── function.html_select_time.php │ │ │ │ │ ├── function.html_table.php │ │ │ │ │ ├── function.mailto.php │ │ │ │ │ ├── function.math.php │ │ │ │ │ ├── modifier.capitalize.php │ │ │ │ │ ├── modifier.date_format.php │ │ │ │ │ ├── modifier.debug_print_var.php │ │ │ │ │ ├── modifier.escape.php │ │ │ │ │ ├── modifier.regex_replace.php │ │ │ │ │ ├── modifier.replace.php │ │ │ │ │ ├── modifier.spacify.php │ │ │ │ │ ├── modifier.truncate.php │ │ │ │ │ ├── modifiercompiler.cat.php │ │ │ │ │ ├── modifiercompiler.count_characters.php │ │ │ │ │ ├── modifiercompiler.count_paragraphs.php │ │ │ │ │ ├── modifiercompiler.count_sentences.php │ │ │ │ │ ├── modifiercompiler.count_words.php │ │ │ │ │ ├── modifiercompiler.default.php │ │ │ │ │ ├── modifiercompiler.escape.php │ │ │ │ │ ├── modifiercompiler.from_charset.php │ │ │ │ │ ├── modifiercompiler.indent.php │ │ │ │ │ ├── modifiercompiler.lower.php │ │ │ │ │ ├── modifiercompiler.noprint.php │ │ │ │ │ ├── modifiercompiler.string_format.php │ │ │ │ │ ├── modifiercompiler.strip.php │ │ │ │ │ ├── modifiercompiler.strip_tags.php │ │ │ │ │ ├── modifiercompiler.to_charset.php │ │ │ │ │ ├── modifiercompiler.unescape.php │ │ │ │ │ ├── modifiercompiler.upper.php │ │ │ │ │ ├── modifiercompiler.wordwrap.php │ │ │ │ │ ├── outputfilter.trimwhitespace.php │ │ │ │ │ ├── shared.escape_special_chars.php │ │ │ │ │ ├── shared.literal_compiler_param.php │ │ │ │ │ ├── shared.make_timestamp.php │ │ │ │ │ ├── shared.mb_str_replace.php │ │ │ │ │ ├── shared.mb_unicode.php │ │ │ │ │ ├── shared.mb_wordwrap.php │ │ │ │ │ └── variablefilter.htmlspecialchars.php │ │ │ │ └── sysplugins/ │ │ │ │ ├── smarty_cacheresource.php │ │ │ │ ├── smarty_cacheresource_custom.php │ │ │ │ ├── smarty_cacheresource_keyvaluestore.php │ │ │ │ ├── smarty_config_source.php │ │ │ │ ├── smarty_internal_cacheresource_file.php │ │ │ │ ├── smarty_internal_compile_append.php │ │ │ │ ├── smarty_internal_compile_assign.php │ │ │ │ ├── smarty_internal_compile_block.php │ │ │ │ ├── smarty_internal_compile_break.php │ │ │ │ ├── smarty_internal_compile_call.php │ │ │ │ ├── smarty_internal_compile_capture.php │ │ │ │ ├── smarty_internal_compile_config_load.php │ │ │ │ ├── smarty_internal_compile_continue.php │ │ │ │ ├── smarty_internal_compile_debug.php │ │ │ │ ├── smarty_internal_compile_eval.php │ │ │ │ ├── smarty_internal_compile_extends.php │ │ │ │ ├── smarty_internal_compile_for.php │ │ │ │ ├── smarty_internal_compile_foreach.php │ │ │ │ ├── smarty_internal_compile_function.php │ │ │ │ ├── smarty_internal_compile_if.php │ │ │ │ ├── smarty_internal_compile_include.php │ │ │ │ ├── smarty_internal_compile_include_php.php │ │ │ │ ├── smarty_internal_compile_insert.php │ │ │ │ ├── smarty_internal_compile_ldelim.php │ │ │ │ ├── smarty_internal_compile_nocache.php │ │ │ │ ├── smarty_internal_compile_private_block_plugin.php │ │ │ │ ├── smarty_internal_compile_private_function_plugin.php │ │ │ │ ├── smarty_internal_compile_private_modifier.php │ │ │ │ ├── smarty_internal_compile_private_object_block_function.php │ │ │ │ ├── smarty_internal_compile_private_object_function.php │ │ │ │ ├── smarty_internal_compile_private_print_expression.php │ │ │ │ ├── smarty_internal_compile_private_registered_block.php │ │ │ │ ├── smarty_internal_compile_private_registered_function.php │ │ │ │ ├── smarty_internal_compile_private_special_variable.php │ │ │ │ ├── smarty_internal_compile_rdelim.php │ │ │ │ ├── smarty_internal_compile_section.php │ │ │ │ ├── smarty_internal_compile_setfilter.php │ │ │ │ ├── smarty_internal_compile_while.php │ │ │ │ ├── smarty_internal_compilebase.php │ │ │ │ ├── smarty_internal_config.php │ │ │ │ ├── smarty_internal_config_file_compiler.php │ │ │ │ ├── smarty_internal_configfilelexer.php │ │ │ │ ├── smarty_internal_configfileparser.php │ │ │ │ ├── smarty_internal_data.php │ │ │ │ ├── smarty_internal_debug.php │ │ │ │ ├── smarty_internal_filter_handler.php │ │ │ │ ├── smarty_internal_function_call_handler.php │ │ │ │ ├── smarty_internal_get_include_path.php │ │ │ │ ├── smarty_internal_nocache_insert.php │ │ │ │ ├── smarty_internal_parsetree.php │ │ │ │ ├── smarty_internal_resource_eval.php │ │ │ │ ├── smarty_internal_resource_extends.php │ │ │ │ ├── smarty_internal_resource_file.php │ │ │ │ ├── smarty_internal_resource_php.php │ │ │ │ ├── smarty_internal_resource_registered.php │ │ │ │ ├── smarty_internal_resource_stream.php │ │ │ │ ├── smarty_internal_resource_string.php │ │ │ │ ├── smarty_internal_smartytemplatecompiler.php │ │ │ │ ├── smarty_internal_template.php │ │ │ │ ├── smarty_internal_templatebase.php │ │ │ │ ├── smarty_internal_templatecompilerbase.php │ │ │ │ ├── smarty_internal_templatelexer.php │ │ │ │ ├── smarty_internal_templateparser.php │ │ │ │ ├── smarty_internal_utility.php │ │ │ │ ├── smarty_internal_write_file.php │ │ │ │ ├── smarty_resource.php │ │ │ │ ├── smarty_resource_custom.php │ │ │ │ ├── smarty_resource_recompiled.php │ │ │ │ ├── smarty_resource_uncompiled.php │ │ │ │ └── smarty_security.php │ │ │ └── smarty.conf │ │ └── todo-demo/ │ │ ├── .gitignore │ │ ├── chameleon.config.js │ │ ├── mock/ │ │ │ ├── api/ │ │ │ │ └── index.js │ │ │ └── template/ │ │ │ └── index.php │ │ ├── package.json │ │ └── src/ │ │ ├── app/ │ │ │ └── app.cml │ │ ├── components/ │ │ │ ├── c-checkbox/ │ │ │ │ └── c-checkbox.cml │ │ │ └── c-todoitem/ │ │ │ └── c-todoitem.cml │ │ ├── pages/ │ │ │ └── index/ │ │ │ └── index.cml │ │ ├── router.config.json │ │ └── store/ │ │ ├── action-types.js │ │ ├── actions.js │ │ ├── getter-types.js │ │ ├── getters.js │ │ ├── index/ │ │ │ ├── action-types.js │ │ │ ├── actions.js │ │ │ ├── getter-types.js │ │ │ ├── getters.js │ │ │ ├── mutation-types.js │ │ │ ├── mutations.js │ │ │ └── state.js │ │ ├── index.js │ │ ├── mutation-types.js │ │ ├── mutations.js │ │ ├── state.js │ │ └── utils.js │ ├── chameleon-tool/ │ │ ├── .babelrc │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .istanbul.yml │ │ ├── .npmignore │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── chameleon.js │ │ ├── commanders/ │ │ │ ├── alipay/ │ │ │ │ └── index.js │ │ │ ├── baidu/ │ │ │ │ └── index.js │ │ │ ├── build/ │ │ │ │ └── index.js │ │ │ ├── dev/ │ │ │ │ └── index.js │ │ │ ├── ext/ │ │ │ │ └── index.js │ │ │ ├── extPlatform.js │ │ │ ├── info/ │ │ │ │ └── index.js │ │ │ ├── init/ │ │ │ │ ├── index.js │ │ │ │ └── platform.json │ │ │ ├── qq/ │ │ │ │ └── index.js │ │ │ ├── server/ │ │ │ │ └── index.js │ │ │ ├── tt/ │ │ │ │ └── index.js │ │ │ ├── utils.js │ │ │ ├── web/ │ │ │ │ ├── apiMiddleware.js │ │ │ │ ├── dev-client.js │ │ │ │ ├── dev-server.js │ │ │ │ ├── dynamicApiMiddleware.js │ │ │ │ ├── index.js │ │ │ │ ├── liveload-dev-client.js │ │ │ │ ├── php_cgi_middleware.js │ │ │ │ ├── responseTime.js │ │ │ │ └── web-socket.js │ │ │ ├── weex/ │ │ │ │ ├── index.js │ │ │ │ └── socket-server.js │ │ │ └── wx/ │ │ │ └── index.js │ │ ├── configs/ │ │ │ ├── cml-loader.conf.js │ │ │ ├── component_export/ │ │ │ │ ├── dependencies.js │ │ │ │ ├── export-loader.js │ │ │ │ ├── exportPlugin.js │ │ │ │ ├── getMiniAppExportConfig.js │ │ │ │ ├── getWebExportConfig.js │ │ │ │ ├── getWebExportConfig_new.js │ │ │ │ ├── getWeexExportConfig.js │ │ │ │ ├── getWeexExportConfig_new.js │ │ │ │ ├── postcssPlugin.js │ │ │ │ ├── utils.js │ │ │ │ ├── webpack.web.config.js │ │ │ │ └── webpack.weex.config.js │ │ │ ├── config.js │ │ │ ├── default/ │ │ │ │ ├── app.cml │ │ │ │ ├── entry.js │ │ │ │ ├── html_entry.html │ │ │ │ ├── miniappPolyfill.js │ │ │ │ ├── rem.js │ │ │ │ ├── router.js │ │ │ │ ├── smarty_entry.html │ │ │ │ ├── weexPolyfill.js │ │ │ │ └── weex_liveload_entry.js │ │ │ ├── entryLoader.js │ │ │ ├── getCommonConfig.js │ │ │ ├── getMiniAppBuildConfig.js │ │ │ ├── getMiniAppCommonConfig.js │ │ │ ├── getMiniAppDevConfig.js │ │ │ ├── getWebBuildConfig.js │ │ │ ├── getWebCommonConfig.js │ │ │ ├── getWebDevConfig.js │ │ │ ├── getWebExportConfig.js │ │ │ ├── getWeexBuildConfig.js │ │ │ ├── getWeexCommonConfig.js │ │ │ ├── getWeexDevConfig.js │ │ │ ├── index.js │ │ │ ├── mvvm/ │ │ │ │ ├── getExtendConfig.js │ │ │ │ └── originSourceLoader.js │ │ │ ├── plugins/ │ │ │ │ ├── CopyNpmPLugin.js │ │ │ │ ├── miniAppBaseCssAdd.js │ │ │ │ └── miniAppSubPkg.js │ │ │ ├── postcss/ │ │ │ │ ├── alipay/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── baidu/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── export/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── extend/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── qq/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── tt/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── web/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ ├── weex/ │ │ │ │ │ └── .postcssrc.js │ │ │ │ └── wx/ │ │ │ │ └── .postcssrc.js │ │ │ ├── preview-assets/ │ │ │ │ └── qrcode.js │ │ │ ├── preview.html │ │ │ ├── routerLoader.js │ │ │ ├── utils.js │ │ │ ├── web_empty.html │ │ │ ├── web_global.css │ │ │ ├── web_global.js │ │ │ └── weex_liveload/ │ │ │ ├── WeexWebSocket.js │ │ │ └── liveLoad.js │ │ ├── lib/ │ │ │ ├── cli.js │ │ │ ├── config.js │ │ │ ├── index.js │ │ │ ├── log.js │ │ │ └── utils.js │ │ ├── npm-shrinkwrap.json │ │ ├── package.json │ │ └── test/ │ │ ├── commander/ │ │ │ ├── index.cml │ │ │ ├── index.interface │ │ │ └── init.test.js │ │ ├── configs/ │ │ │ └── utils.js │ │ └── lib/ │ │ ├── cli.test.js │ │ ├── config.test.js │ │ ├── index.test.js │ │ └── log.test.js │ ├── chameleon-tool-utils/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .istanbul.yml │ │ ├── .npmignore │ │ ├── .travis.yml │ │ ├── gulpfile.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.js │ │ │ ├── lib/ │ │ │ │ └── splitParts.js │ │ │ └── log.js │ │ └── test/ │ │ ├── index.test.js │ │ ├── log.test.js │ │ └── testlib/ │ │ ├── alipay/ │ │ │ ├── alipay.axml │ │ │ └── alipay.json │ │ ├── baidu/ │ │ │ ├── baidu.json │ │ │ └── baidu.swan │ │ ├── cli/ │ │ │ └── config.js │ │ ├── demo-project/ │ │ │ ├── .gitignore │ │ │ ├── chameleon.config.js │ │ │ ├── coma/ │ │ │ │ └── coma.interface │ │ │ ├── comb/ │ │ │ │ └── comb.cml │ │ │ ├── index.cml │ │ │ ├── package.json │ │ │ └── src/ │ │ │ ├── app/ │ │ │ │ └── app.cml │ │ │ ├── components/ │ │ │ │ ├── com1/ │ │ │ │ │ └── com1.cml │ │ │ │ ├── com2/ │ │ │ │ │ ├── com2.interface │ │ │ │ │ ├── com2.web.cml │ │ │ │ │ └── com2.wx.cml │ │ │ │ └── com3/ │ │ │ │ └── com3.interface │ │ │ ├── pages/ │ │ │ │ ├── pagea/ │ │ │ │ │ └── pagea.cml │ │ │ │ └── pageb/ │ │ │ │ └── pageb.cml │ │ │ └── router.config.json │ │ ├── index.cml │ │ ├── index.interface │ │ └── wx/ │ │ ├── wx.json │ │ └── wx.wxml │ ├── chameleon-vue-precompiler/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── lib/ │ │ │ ├── index.js │ │ │ └── precompiler/ │ │ │ ├── components/ │ │ │ │ ├── div.js │ │ │ │ ├── index.js │ │ │ │ └── span.js │ │ │ ├── config.js │ │ │ ├── hooks/ │ │ │ │ ├── events.js │ │ │ │ ├── index.js │ │ │ │ ├── style-binding.js │ │ │ │ └── style.js │ │ │ ├── index.js │ │ │ ├── node/ │ │ │ │ └── tag.js │ │ │ └── util/ │ │ │ ├── ast.js │ │ │ └── index.js │ │ └── package.json │ ├── chameleon-webpack-plugin/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── chunkhash.js │ │ │ ├── moduleId.js │ │ │ └── utils.js │ │ ├── package.json │ │ └── test/ │ │ └── utils.test.js │ ├── chameleon-weex-vue-loader/ │ │ ├── .gitignore │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── gen-id.js │ │ │ ├── loader.js │ │ │ ├── normalize.js │ │ │ ├── parser.js │ │ │ ├── script-loader.js │ │ │ ├── selector.js │ │ │ ├── style-loader.js │ │ │ ├── style-rewriter.js │ │ │ ├── template-compiler.js │ │ │ └── template-loader.js │ │ └── package.json │ ├── cml-component-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── bin/ │ │ │ └── cli.js │ │ ├── config/ │ │ │ └── parse-config.js │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── entrance-flat.js │ │ │ └── readme-builder.js │ │ └── test/ │ │ ├── docs/ │ │ │ ├── built-in-components/ │ │ │ │ ├── input/ │ │ │ │ │ ├── input.interface │ │ │ │ │ ├── input.web.cml │ │ │ │ │ ├── input.weex.cml │ │ │ │ │ └── input.wx.cml │ │ │ │ └── layout/ │ │ │ │ ├── aside/ │ │ │ │ │ └── aside.cml │ │ │ │ ├── col/ │ │ │ │ │ └── col.cml │ │ │ │ ├── container/ │ │ │ │ │ └── container.cml │ │ │ │ └── foot/ │ │ │ │ └── foot.cml │ │ │ ├── export-default.cml │ │ │ └── index.interface │ │ └── test.js │ ├── cml-extract-css-webpack-plugin/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── dist/ │ │ │ ├── cjs.js │ │ │ ├── helper.js │ │ │ ├── index.js │ │ │ ├── lib/ │ │ │ │ ├── ExtractTextPluginCompilation.js │ │ │ │ ├── ExtractedModule.js │ │ │ │ ├── OrderUndefinedError.js │ │ │ │ └── helpers.js │ │ │ └── loader.js │ │ ├── package.json │ │ ├── schema/ │ │ │ ├── loader.json │ │ │ └── plugin.json │ │ └── test/ │ │ └── index.js │ ├── cml-htmllinter/ │ │ ├── AUTHORS │ │ ├── HISTORY │ │ ├── LICENSE │ │ ├── docs/ │ │ │ └── gen_options.js │ │ ├── gulpfile.js │ │ ├── lib/ │ │ │ ├── config.js │ │ │ ├── hook/ │ │ │ │ ├── index.js │ │ │ │ ├── skip-empty-text.js │ │ │ │ ├── skip-normal-tag.js │ │ │ │ └── skip_origin_tag.js │ │ │ ├── index.js │ │ │ ├── inline_config.js │ │ │ ├── issue.js │ │ │ ├── knife/ │ │ │ │ ├── README.md │ │ │ │ ├── apply_rules.js │ │ │ │ ├── attr_parse.js │ │ │ │ ├── boolean_attrs.js │ │ │ │ ├── index.js │ │ │ │ ├── is_cml_directives.js │ │ │ │ ├── is_common_attrs.js │ │ │ │ ├── is_common_events.js │ │ │ │ ├── is_labeable.js │ │ │ │ ├── is_origin_tag.js │ │ │ │ ├── is_void_element.js │ │ │ │ ├── lang_tag.js │ │ │ │ ├── match_filter.js │ │ │ │ ├── relative_line_col.js │ │ │ │ ├── shred.js │ │ │ │ └── tag_utils.js │ │ │ ├── linter.js │ │ │ ├── messages.js │ │ │ ├── parser/ │ │ │ │ ├── dom_builder.js │ │ │ │ └── index.js │ │ │ ├── presets/ │ │ │ │ ├── accessibility.js │ │ │ │ ├── default.js │ │ │ │ ├── global.js │ │ │ │ ├── index.js │ │ │ │ └── validate.js │ │ │ ├── process_option.js │ │ │ ├── rules/ │ │ │ │ ├── attr-bans.js │ │ │ │ ├── attr-name-style.js │ │ │ │ ├── attr-new-line.js │ │ │ │ ├── attr-no-dup.js │ │ │ │ ├── attr-no-unsafe-char.js │ │ │ │ ├── attr-order.js │ │ │ │ ├── attr-quote-style.js │ │ │ │ ├── attr-req-value.js │ │ │ │ ├── attr-validate.js │ │ │ │ ├── attr.js │ │ │ │ ├── class-no-dup.js │ │ │ │ ├── class-style.js │ │ │ │ ├── class.js │ │ │ │ ├── component-attr-check.js │ │ │ │ ├── directive-cml-valid-value.js │ │ │ │ ├── directive-cml.js │ │ │ │ ├── directive-name-forbiden.js │ │ │ │ ├── doctype-first.js │ │ │ │ ├── doctype-html5.js │ │ │ │ ├── dom.js │ │ │ │ ├── fig-req-figcaption.js │ │ │ │ ├── focusable-tabindex-style.js │ │ │ │ ├── free-options.js │ │ │ │ ├── head-req-title.js │ │ │ │ ├── head-valid-content-model.js │ │ │ │ ├── href-style.js │ │ │ │ ├── html-valid-content-model.js │ │ │ │ ├── id-class-no-ad.js │ │ │ │ ├── id-no-dup.js │ │ │ │ ├── id-style.js │ │ │ │ ├── img-req-alt.js │ │ │ │ ├── img-req-src.js │ │ │ │ ├── indent-style.js │ │ │ │ ├── index.js │ │ │ │ ├── input-radio-req-name.js │ │ │ │ ├── input-req-label.js │ │ │ │ ├── label-req-for.js │ │ │ │ ├── lang.js │ │ │ │ ├── line-end-style.js │ │ │ │ ├── line-max-len.js │ │ │ │ ├── line-no-trailing-whitespace.js │ │ │ │ ├── line.js │ │ │ │ ├── link-req-noopener.js │ │ │ │ ├── origin-tag-forbidden-directive.js │ │ │ │ ├── origin-tag.js │ │ │ │ ├── spec-char-escape.js │ │ │ │ ├── table-req-caption.js │ │ │ │ ├── table-req-header.js │ │ │ │ ├── tag-bans.js │ │ │ │ ├── tag-close.js │ │ │ │ ├── tag-embed.js │ │ │ │ ├── tag-name-lowercase.js │ │ │ │ ├── tag-req-attr.js │ │ │ │ ├── tag-white-list.js │ │ │ │ ├── tag.js │ │ │ │ ├── template-lang.js │ │ │ │ ├── text-forbid-raw.js │ │ │ │ ├── text.js │ │ │ │ ├── title-max-len.js │ │ │ │ ├── title-no-dup.js │ │ │ │ └── title.js │ │ │ └── tools/ │ │ │ ├── index.js │ │ │ └── is_origin_component.js │ │ ├── package.json │ │ ├── repl.js │ │ └── test/ │ │ ├── .eslintrc │ │ ├── fixtures/ │ │ │ ├── .gitkeep │ │ │ ├── const_rule.js │ │ │ └── sanity.html │ │ ├── functional/ │ │ │ ├── attr-bans.js │ │ │ ├── attr-name-style.js │ │ │ ├── attr-new-line.js │ │ │ ├── attr-no-dup.js │ │ │ ├── attr-no-unsafe-char.js │ │ │ ├── attr-order.js │ │ │ ├── attr-quote-style.js │ │ │ ├── attr-req-value.js │ │ │ ├── attr-validate.js │ │ │ ├── class-no-dup.js │ │ │ ├── class-style.js │ │ │ ├── component-attr-check.js │ │ │ ├── doctype-first.js │ │ │ ├── doctype-html5.js │ │ │ ├── fig-req-figcaption.js │ │ │ ├── focusable-tabindex-style.js │ │ │ ├── head-req-title.js │ │ │ ├── head-valid-content-model.js │ │ │ ├── href-style.js │ │ │ ├── html-valid-content-model.js │ │ │ ├── id-class-no-ad.js │ │ │ ├── id-no-dup.js │ │ │ ├── id-style.js │ │ │ ├── img-req-alt.js │ │ │ ├── img-req-src.js │ │ │ ├── indent-delta.js │ │ │ ├── indent-style.js │ │ │ ├── input-radio-req-name.js │ │ │ ├── input-req-label.js │ │ │ ├── label-req-for.js │ │ │ ├── lang.js │ │ │ ├── line-end-style.js │ │ │ ├── line-max-len.js │ │ │ ├── line-no-trailing-whitespace.js │ │ │ ├── link-req-noopener.js │ │ │ ├── raw-ignore-regex.js │ │ │ ├── runner.test.js │ │ │ ├── spec-char-escape.js │ │ │ ├── table-req-caption.js │ │ │ ├── table-req-header.js │ │ │ ├── tag-bans.js │ │ │ ├── tag-name-lowercase.js │ │ │ ├── tag-req-attr.js │ │ │ ├── title-max-len.js │ │ │ └── title-no-dup.js │ │ └── unit/ │ │ ├── htmllint.js │ │ ├── inline-config-html/ │ │ │ └── inline-all.html │ │ ├── knife.apply_rules.js │ │ ├── knife.is_labeable.js │ │ ├── knife.relative_line_col.js │ │ ├── linter.js │ │ ├── messages.js │ │ ├── parser.dom_builder.js │ │ ├── parser.js │ │ ├── raw-ignore-regex.js │ │ ├── rules.doctype-first.js │ │ ├── rules.focusable-tabindex-style.js │ │ ├── rules.js │ │ └── runner.test.js │ ├── cml-interface-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── config/ │ │ │ └── babel-parser-config.js │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ast-tree-parser.js │ │ │ └── file-reader.js │ │ └── test/ │ │ ├── docs/ │ │ │ ├── include-interface.interface │ │ │ └── index.interface │ │ └── test.js │ ├── cml-js-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── config/ │ │ │ └── babel-parser-config.js │ │ ├── index.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── file-reader.js │ │ │ └── visitors/ │ │ │ ├── containerPathVisitor.js │ │ │ ├── exportPathVisitor.js │ │ │ └── index.js │ │ └── test/ │ │ ├── docs/ │ │ │ ├── export-class.cml │ │ │ └── export-default.cml │ │ └── test.js │ ├── cml-vue-loader/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── helpers.js │ │ │ ├── loader.js │ │ │ ├── parser.js │ │ │ ├── runtime/ │ │ │ │ └── component-normalizer.js │ │ │ ├── selector.js │ │ │ ├── style-compiler/ │ │ │ │ ├── index.js │ │ │ │ ├── load-postcss-config.js │ │ │ │ └── plugins/ │ │ │ │ ├── scope-id.js │ │ │ │ └── trim.js │ │ │ ├── template-compiler/ │ │ │ │ ├── index.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── transform-require.js │ │ │ │ │ └── transform-srcset.js │ │ │ │ ├── preprocessor.js │ │ │ │ └── url-to-require.js │ │ │ └── utils/ │ │ │ ├── normalize.js │ │ │ ├── options-cache.js │ │ │ └── try-require.js │ │ └── package.json │ ├── easy-chameleon/ │ │ ├── .babelrc │ │ ├── .gitignore │ │ ├── bin/ │ │ │ └── index.js │ │ ├── config/ │ │ │ ├── getWebpackConfig.js │ │ │ └── utils.js │ │ ├── entry/ │ │ │ ├── web_global.css │ │ │ └── web_global.js │ │ ├── index.js │ │ ├── npm-shrinkwrap.json │ │ ├── package.json │ │ └── postcss/ │ │ ├── web/ │ │ │ └── .postcssrc.js │ │ ├── weex/ │ │ │ └── .postcssrc.js │ │ └── wx/ │ │ └── .postcssrc.js │ ├── interface-loader/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.js │ │ │ └── lib/ │ │ │ └── check.js │ │ └── test/ │ │ └── lib.test/ │ │ ├── check.js │ │ ├── check.test.js │ │ ├── generator.test.js │ │ └── interface.test │ ├── mvvm-babel-generator/ │ │ ├── LICENSE │ │ ├── lib/ │ │ │ ├── buffer.js │ │ │ ├── generators/ │ │ │ │ ├── base.js │ │ │ │ ├── classes.js │ │ │ │ ├── expressions.js │ │ │ │ ├── flow.js │ │ │ │ ├── index.js │ │ │ │ ├── jsx.js │ │ │ │ ├── methods.js │ │ │ │ ├── modules.js │ │ │ │ ├── statements.js │ │ │ │ ├── template-literals.js │ │ │ │ ├── types.js │ │ │ │ └── typescript.js │ │ │ ├── index.js │ │ │ ├── node/ │ │ │ │ ├── index.js │ │ │ │ ├── parentheses.js │ │ │ │ └── whitespace.js │ │ │ ├── printer.js │ │ │ └── source-map.js │ │ └── package.json │ ├── mvvm-babel-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .istanbul.yml │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── lib/ │ │ │ └── index.js │ │ ├── package.json │ │ └── test/ │ │ ├── brace.js │ │ └── testjsx.tpl │ ├── mvvm-cml-loader/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── helper.js │ │ ├── index.js │ │ ├── package.json │ │ └── selector.js │ ├── mvvm-file-loader/ │ │ ├── README.md │ │ ├── dist/ │ │ │ ├── cjs.js │ │ │ ├── index.js │ │ │ └── options.json │ │ └── package.json │ ├── mvvm-interface-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── check.js │ │ │ ├── getInterfaceCode.js │ │ │ ├── getInterfaceCode_old.js │ │ │ ├── getMethodCode.js │ │ │ ├── getMethodCode_old.js │ │ │ └── resolveRequire.js │ │ ├── package.json │ │ ├── runtime/ │ │ │ ├── checkWrapper.js │ │ │ └── copyProto.js │ │ └── test/ │ │ ├── check.test.js │ │ ├── checkWrapper.test.js │ │ ├── copyProto.test.js │ │ ├── getInterfaceCode.test.js │ │ ├── getMethodCode.test.js │ │ └── lib/ │ │ └── components/ │ │ ├── coma/ │ │ │ ├── coma.alipay.cml │ │ │ ├── coma.baidu.cml │ │ │ ├── coma.interface │ │ │ ├── coma.web.cml │ │ │ ├── coma.weex.cml │ │ │ └── coma.wx.cml │ │ ├── comb/ │ │ │ ├── comb.baidu.cml │ │ │ └── comb.interface │ │ ├── demo-com/ │ │ │ └── demo-com.cml │ │ ├── first/ │ │ │ ├── first.interface │ │ │ └── test.js │ │ ├── methodinclude.interface │ │ ├── methodsrcerror.interface │ │ ├── partsrcerror.interface │ │ ├── ph-com/ │ │ │ ├── ph-com.alipay.cml │ │ │ ├── ph-com.baidu.cml │ │ │ ├── ph-com.interface │ │ │ ├── ph-com.web.cml │ │ │ ├── ph-com.weex.cml │ │ │ └── ph-com.wx.cml │ │ ├── second/ │ │ │ └── second.interface │ │ ├── third/ │ │ │ ├── double.interface │ │ │ ├── includea.interface │ │ │ ├── includeb.interface │ │ │ ├── multi.interface │ │ │ ├── not.interface │ │ │ └── third.interface │ │ ├── third.interface │ │ ├── thirdinterface.js │ │ └── thirdmethod.js │ ├── mvvm-miniapp-loader/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── helper.js │ │ ├── index.js │ │ ├── package.json │ │ └── selector.js │ ├── mvvm-pack/ │ │ ├── .eslintrc │ │ ├── cmlNode.js │ │ ├── compiler.js │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── amd.js │ │ │ ├── amdbootstrap.global.js │ │ │ ├── amdbootstrap.module.js │ │ │ ├── amdwrapper.js │ │ │ └── handleScript.js │ │ ├── log.js │ │ ├── minimize/ │ │ │ ├── uglifycss.js │ │ │ └── uglifyjs.js │ │ ├── mvvmGraphPlugin.js │ │ ├── package.json │ │ └── test/ │ │ ├── cmlNode.test.js │ │ ├── demo-project/ │ │ │ ├── .gitignore │ │ │ ├── chameleon.config.js │ │ │ ├── coma/ │ │ │ │ └── coma.interface │ │ │ ├── comb/ │ │ │ │ └── comb.cml │ │ │ ├── index.cml │ │ │ ├── package.json │ │ │ └── src/ │ │ │ ├── app/ │ │ │ │ └── app.cml │ │ │ ├── components/ │ │ │ │ ├── coma/ │ │ │ │ │ └── coma.cml │ │ │ │ ├── comb/ │ │ │ │ │ ├── comb.interface │ │ │ │ │ ├── comb.web.cml │ │ │ │ │ └── comb.wx.cml │ │ │ │ └── comc/ │ │ │ │ └── comc.interface │ │ │ ├── pages/ │ │ │ │ ├── pagea/ │ │ │ │ │ └── pagea.cml │ │ │ │ └── pageb/ │ │ │ │ └── pageb.cml │ │ │ └── router.config.json │ │ ├── demoPlugin.js │ │ ├── lib/ │ │ │ ├── amd.test.js │ │ │ ├── amdbootstrap.global.test.js │ │ │ ├── amdbootstrap.module.test.js │ │ │ ├── amdwrapper.test.js │ │ │ ├── handleScript.node.js │ │ │ └── handleScript.test.js │ │ ├── log.test.js │ │ ├── mvvmGraphPlugin.test.js │ │ ├── test.wxml │ │ ├── uglifycss.test.js │ │ └── uglifyjs.test.js │ ├── mvvm-style-loader/ │ │ ├── index.js │ │ ├── lib.js │ │ ├── package.json │ │ └── test/ │ │ └── index.test.js │ ├── mvvm-template-parser/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.js │ │ ├── lib/ │ │ │ └── process-template.js │ │ ├── package.json │ │ └── test/ │ │ ├── index.test.js │ │ └── process-template.test.js │ ├── runtime-check/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── getDefines.js │ │ ├── index.js │ │ ├── package.json │ │ ├── parsePlugins.js │ │ └── test/ │ │ ├── index.test.js │ │ └── interface.test │ ├── url-loader/ │ │ ├── .babelrc │ │ ├── .circleci/ │ │ │ └── config.yml │ │ ├── .eslintignore │ │ ├── .eslintrc │ │ ├── .gitattributes │ │ ├── .github/ │ │ │ ├── CODEOWNERS │ │ │ ├── CONTRIBUTING.md │ │ │ ├── ISSUE_TEMPLATE.md │ │ │ └── PULL_REQUEST_TEMPLATE.md │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── codecov.yml │ │ ├── commitlint.config.js │ │ ├── package.json │ │ └── src/ │ │ ├── cjs.js │ │ ├── index.js │ │ ├── options.json │ │ └── utils/ │ │ └── normalizeFallback.js │ ├── webpack-check-plugin/ │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── check.js │ │ │ └── tokensMap.js │ │ ├── package.json │ │ └── test/ │ │ └── check.test.js │ └── webpack-liveload-middleware/ │ ├── .eslintrc │ ├── .gitignore │ ├── .npmignore │ ├── LICENSE │ ├── client.js │ ├── helpers.js │ ├── middleware.js │ ├── package.json │ └── test/ │ └── helpers.test.js └── test/ └── test.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.js ================================================ module.exports = { root:true, env: { browser: true, es6: true, }, extends: [ 'standard', ], globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly' }, parserOptions: { ecmaVersion: 2018, parser:'babel-eslint' }, rules: { 'semi':[2,'always'] } } ================================================ FILE: .gitattributes ================================================ *.php linguist-language=JavaScript *.interface linguist-language=JavaScript *.cml linguist-language=JavaScript ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: '' --- **bug描述** 描述出现的问题 **复现bug的步骤** 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **问题截图** **编译环境信息** - chameleon-tool 版本:[e.g.0.1.0] - nodejs版本、npm版本:[node v8.11.4、npm 5.6.0] - 电脑操作系统: [e.g. macOS High Sierra 10.13.6] **运行环境信息** - 端版本: [e.g. 微信小程序 7.0.3、weex 2.0、手机Safari浏览器 、PC Chrome浏览器 1.30] - 手机机型: [e.g. iPhone6 12.0] ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .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 /node_modules /packages/*/node_modules 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 coverage .nyc_output # /////////////////////////// ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "8.10.0" before_script: - lerna bootstrap --ignore-scripts - cd packages/easy-chameleon && rm -rf node_modules && npm i && cd ../../ - cd packages/chameleon-tool && rm -rf node_modules && npm i && cd ../../ script: - npm run coverage after_script: - cat coverage/lcov.info | node_modules/.bin/codecov ================================================ FILE: CHANGELOG.md ================================================ ## [1.0.8] * 支持data-class 配置 chameleon.config.js 文件中配置 ```javascript wx:{ //alipay/baidu/... dev:{ hasMiniAppCustomDataClass:true } build:{ hasMiniAppCustomDataClass:true } } ``` * 内置支持循环依赖检测 ```javascript optimize: { circularDependency: true// 默认引入自动检测循环引用的插件 } ``` ## [1.0.8-alpha.1] * 路由配置文件处理 * HtmlWebpackPlugin配置适配 * express本地服务配置 * 预览页面跳转以及二维码适配 * hash页面适配多路由: 需要修改preview页面跳转 * history页面适配多路由:需要修改本地express服务 ## [1.0.8-alpha.0] * 支持chameleon-api-miniapp按需加载 * 支持组件动态加载 * 支持路由懒加载 * 支持终端构建进度 processBar 可配置,默认关闭 * 修复config.json生成的weex端信息缺失 * 支持压缩模式下console信息可配置是否删除 * chameleon-linter修复useingComponents中引入动态组件校验 ## [1.0.6] * web端动态加载支持自定义chunk名称 * 修复小程序端原生tabbar图标配置在windows上的Bug ## [1.0.6-alpha.6] * web端 dev模式下如何支持跨域 * 构建过程警告信息支持配置是否输出,可以排查重复npm包的安装 * 压缩构建过程删除console * windows下 subProject 配置npmName的路径适配修复 * 修复项目内安装cli通过npm脚本构建全局变量校验问题 ## [1.0.6-alpha.5] * 增加运行环境获取命令 cml info * 提供构建进程和面板,优化开发体验 * 更新初始化项目依赖版本 * 支持weex多bundle构建自定义name ## [1.0.6-alpha.3] * 初始化项目paltform默认值['web','weex','wx']三端 * 支持weex多bundle构建 ## [1.0.6-alpha.2] * 支付宝小程序编译组件包裹层id上移 * web、weex端router.config.json的编译方式增加name字段进行区分 ## [1.0.6-alpha.1] * 升级less编译器 * 支持原生组件样式引入对应平台样式文件不带后缀 * 支持路由多平台配置 ## [1.0.5] * 发布支持头条和小程序增强相关能力的正式版本 * 预览页面改版,支持预览页面搜索能力 ## [1.0.5-alpha.2] * 全局配置支持小程序云开发 * 支持小程序插件使用和插件开发 * 支持微信小程序sitemap配置 * 命令行内置支持头条小程序 * 支持头条小程序端语法校验 ## [1.0.5-alpha.1] - 支持全局样式 - 修复config.json缺少qq和扩展新端信息 - 优化全局配置,支持性能优化配置项 - 配置不引入基础样式,小程序端不生成对应样式文件 - 修复语法校验中stylus、component 校验逻辑报错 ## [1.0.4] - 支持mock文件更新实时重新编译 - 支持分包页面依赖的js更新,打包结果重新编译 - 优化dev模式内存占用 - 修复分包模式下组件js抽离的时候重复对页面js抽离的操作 ## [1.0.4-alpha.2] - 扩展新端增加内置环境变量 NODE_ENV - 支持模板多态语法 ## [1.0.4-alpha.1] - 解决分包内组件js分包不彻底,优化包体积40%左右 - 支持原生小程序组件的导入也是函数式的 - 支持公用样式以文件的形式 @import ## [1.0.3] - 发布1.0.3-alpha.0的正式版 ## [1.0.3-alpha.0] - Bugfixes - 区分web端click和tap事件,由原来的click和tap统一处理成tap,改为tap和click区别对待,如果要在pc端生效click事件,那么必须绑定click而非tap - 修复windows 上 app.json 中分包页面没有删除 - 修复windows 上初始化项目无法运行 - Features - 扩展新端命令支持集成到chameleon-tool中 ## [1.0.2] - 修改project tododemo的package.lock.json ## [1.0.0] ### Bugfixes - 修复alipay baidu qq 端引用原生组件的问题 ## [0.4.1] ### Bugfixes - 修复内联事件对象 $event的匹配问题 - 修复chameleon.config.js内部配置公用miniappConfig一个对象导致的对象合并不准确问题 ## [0.4.1-alpha.1] ### Bugfixes - 修复window下分包加载的bug - 修复babel-loader无法在回调插件中重新配置的bug ## [0.4.1-alpha.0] ### Bug Fixes - 修复 config.json文件中不生成extra - 修复 -h 指令提示 -n 的情况 ## [0.4.0] ### Features - 灰度完成发正式版 ## [0.4.0-alpha.2] ### Features - 合并0.3.3 和 mvvm+ 版本的代码 ## [0.4.0-mvvm.16] ### Bug Fixes - 修复扩展新端运行时代码插入 ## [0.4.0-mvvm.15] ### Bug Fixes - 修复扩展新端代码中无法使用async函数 ## [0.4.0-mvvm.13] ### Bug Fixes - 修复扩展新端用户编译插件报错导致阻塞watch - 修复扩展新端中不支持变量注入的bug ## [0.4.0-mvvm.12] ### Features - 支持给扩展新端自定义loader传默认options ### Bug Fixes - 修复多路由报错的bug ## [0.4.0-mvvm.11] ### Features - 增加生成config.json文件的钩子 - 打包文件增量覆盖 - 新端插件中可以有默认配置 - 静态资源可以添加生成路径前缀 ## [0.4.0-mvvm.10] ### Features - 完善mvvm-pack单元测试 - 扩展新端compiler对象新增获取路由方法 - 扩展新端cml节点的extra字段添加引用组件 ## [0.4.0-mvvm.9] ### Features - 支持script类型节点 originSource字段获取节点babel前代码 - linter支持扩展多态协议的校验 ## [0.4.0-mvvm.8] ### Bug Fixes - 修复扩展新端在window上构建报错 ## [0.4.0-mvvm.7] ### Features - 支持扩展新端支持文件指纹与js和css压缩 - 对齐百度和微信小程序能力,支持 支付宝 web weex 在自定义组件上使用 tap touchstart touchend touchcancel原生事件直接触发 ## [0.4.0-mvvm.6] ### Features - 支持扩展新端对小程序原生组件支持 ## [0.4.0-mvvm.1-0.4.0-mvvm.3] ### Features - 增加 扩展新端功能 - 增加 web端组件导出支持externals参数进行运行时分离 - 增加 支持小程序配置原生tabbar ## [0.3.3] ### Features: - 组件导出依赖分离 - 可通过命令行配置 preview 预览页是否打开 - 合并qq小程序分支 - web端原生origin click事件不处理成tap - 修改全局变量校验失效问题,同时增加 qq 全局变量名单。 - 模板变量校验添加 LogicalExpression 处理逻辑表达式里的变量 - 支持以组件、页面、子项目粒度配置是否注入默认样式 ### Bug Fixes - 修改cover-view报未定义组件问题件 - 修复引入zepto库后tap事件触发两次问题 ## [0.3.3-alpha.6] ### Features - 组件导出依赖分离 - 可通过命令行配置 preview 预览页是否打开 ## [0.3.3-alpha.5] ### Features - 合并qq小程序分支 - web端原生origin click事件不处理成tap ## [0.3.3-alpha.4] ### Features - 修改全局变量校验失效问题,同时增加 qq 全局变量名单。 - 模板变量校验添加 LogicalExpression 处理逻辑表达式里的变量 ### Bug Fixes - 修改cover-view报未定义组件问题件 - 修复引入zepto库后tap事件触发两次问题 ## [0.3.3-alpha.3] ### Features - 支持web weex可配置是否包裹组件 ## [0.3.3-alpha.qq.1] ### Features * 支持qq小程序 旧项目支持qq小程序 需要修改的地方 - 1 升级以下几个npm包版本 "chameleon-api": "0.5.0-alpha.6", "chameleon-bridge": "0.2.0-alpha.5", "chameleon-runtime": "0.2.2-alpha.qq", "chameleon-store": "0.1.0-alpha.qq", "chameleon-ui-builtin": "0.2.11-alpha.qq", "cml-ui": "0.2.0-alpha.qq" - 2 chameleon.config.js中`platforms`字段添加`qq` - ## [0.3.3-alpha.2] ### Bug Fixes * 修复 weex dev模式liveload失效 老项目如果修复,还需要升级项目中两个npm包如下: - "chameleon-api": "0.4.17", - "chameleon-bridge": "0.1.10", * weex babelPolyfill为true时 将添加整个@babel/polyfill修改为只添加几个polyfill方法miniappPolyfill.js ## [0.3.3-alpha.1] - 支持小程序分包加载 - 修复windows 路径带有数字无法运行 - 升级webpack-bundle-analyzer 解决安全漏洞警告 ## [0.3.3-alpha.0] - web/weex样式一致性加强 - 基础样式设置支持不导入 - 修复 vue 语法下 v-for bug - 支持在cml组件上绑定原生事件,注意:需要升级chameleon-ui-builtin到0.2.10-alpha.4版本才支持 - 修复weex内联事件传汉字编译过慢; - 支持组件上绑定多个内联事件传参的情况 ## [0.3.2] ### Bug Fixes * 修复 web端模板错误 ## [0.3.1] 有bug ## [0.3.0] 0.3.0-alpha.9 灰度为正式版本 ## [0.3.0-alpha.9](https://github.com/didi/chameleon/compare/v0.3.0-alpha.8...v0.3.0-alpha.9) ### Bug Fixes * 修复 cml build 命令 不配置web端也会进行web端构建的bug ### 0.3.0-alpha.8 灰度完成发布0.3.0版本 ## [0.3.0-alpha.8](https://github.com/didi/chameleon/compare/v0.3.0-alpha.7...v0.3.0-alpha.8) ### Bug Fixes * 修复 全局安装chameleon-tool时的npm warn ## [0.3.0-alpha.7](https://github.com/didi/chameleon/compare/v0.3.0-alpha.6...v0.3.0-alpha.7) ### Bug Fixes * 修复 build 模式autoprefixer被删除 * 修复 chameleon.config.js 中的base配置优先级bug ### Features * 增加了cmss.enableAutoPrefix 参数控制是否添加css的autoprefix ## [0.3.0-alpha.6](https://github.com/didi/chameleon/compare/v0.3.0-alpha.5...v0.3.0-alpha.6) ### Bug Fixes * 修复 cml wx build执行后报错 ## [0.3.0-alpha.5](https://github.com/didi/chameleon/compare/v0.3.0-alpha.4...v0.3.0-alpha.5) ### Bug Fixes * 修复 cml weex build生成config.json 格式不正确 ## [0.3.0-alpha.4](https://github.com/didi/chameleon/compare/v0.3.0-alpha.3...v0.3.0-alpha.4) ### Bug Fixes * 回退模板的chameleon-ui-builtin@0.2.1 到chameleon-ui-builtin@0.2.0, 因为image组件不兼容样式设置 ## [0.3.0-alpha.3](https://github.com/didi/chameleon/compare/v0.3.0-alpha.2...v0.3.0-alpha.3) ### Features * 升级初始化项目运行时依赖 * weex config.json中增加md5字段 ### Bug Fixes * 组件间css优先级问题 ## [0.3.0-alpha.2](https://github.com/didi/chameleon/compare/v0.3.0-alpha.1...v0.3.0-alpha.2) ### Features * 支持微信wxs 支付宝sjs 百度.filter.js的文件引用 [issues/67](https://github.com/didi/chameleon/issues/67) ## [0.3.0-alpha.1](https://github.com/didi/chameleon/compare/v0.2.0...v0.3.0-alpha.1) ### Bug Fixes * preview页面iframe未撑开修复 * weex的 build模式jsbundle中存在本地路径 ### Features * 小程序的图片地址本地图片改网络图片 * 解决父级目录babel-loader问题 * build模式config.json的生成 * web端weex端 多态组件支持js格式的vue组件 * 小程序和weex也添加babelPolyfill的选项 * 校验不支持Promise类型定义 * 默认添加/components 别名 * 校验添加生命周期函数白名单 ## [0.2.0](https://github.com/didi/chameleon/compare/v0.2.0-alpha.1...v0.2.0) ### Bug Fixes - 默认开启全局变量校验,升级初始化项目中依赖符合全局变量校验 - linter校验支持component is的校验 ## [0.2.0-alpha.1](https://github.com/didi/chameleon/compare/v0.2.0-alpha.0...v0.2.0-alpha.1) ### Bug Fixes - 修复 js中import css文件导致构建停滞的bug ## [0.2.0-alpha.0](https://github.com/didi/chameleon/compare/v0.1.0-alpha.4...v0.2.0-alpha.0) ### Features - 优化api多域名mock方式 - 组件间css优先级修复 - chameleon.config.js支持base配置 - cml和vue的语法支持事件冒泡 - vue语法下扩展了 v-on:click.stop="handleClick" 的形式来阻止冒泡 - wx端用户自定义组件不添加 cml-base class - component is支持事件绑定以及component is上的属性解析 - 支持cml子项目放入node_module中引入 - 支持拷贝node_modules中的小程序子项目 ### Bug Fixes - 修复 组件间css优先级 使父组件可覆盖子组件样式 ## [0.1.1](https://github.com/didi/chameleon/compare/v0.1.0-alpha.4...v0.1.1) ### Features - init component命令提示文案 - init 多态组件中的json部分优化 ## [0.1.0-alpha.4](https://github.com/didi/chameleon/compare/v0.1.0-alpha.3...v0.1.0-alpha.4) ### Features - 终端提示英文化 - 样式隔离,模板包裹,特殊属性被包裹层继承 - 事件处理优化 - 动画和轮播图的模板解析支持 - chameleon-template-parse 单测完善到 90% 以上 增加对模板解析时候语法的校验 - mock多域名请求优化 ### Bug Fixes - 修复component is v-model c-model v-show c-show ## [0.1.0-alpha.3](https://github.com/didi/chameleon/compare/v0.1.0-alpha.2...v0.1.0-alpha.3) ### Bug Fixes - **chameleon-tool** 修复 alpha版本cli兼容0.1.1版本的chameleon-api ([0deaa8d](https://github.com/didi/chameleon/commit/0deaa8df11f605fc08c1b71850379500ea3f38cc)) ## [0.1.0-alpha.2](https://github.com/didi/chameleon/compare/v0.1.0-alpha.1...v0.1.0-alpha.2) ### Bug Fixes - **chameleon-template-parse** 修复 c-show bug ([4c2c750](https://github.com/didi/chameleon/commit/4c2c7507f2aa906f0580ed59d056e91be7269a93)) - **chameleon-template-parse** 修复 component is bug ([4c2c750](https://github.com/didi/chameleon/commit/4c2c7507f2aa906f0580ed59d056e91be7269a93)) - **chameleon-loader** 修复 window上数字目录编译报错 ([f1b236d](https://github.com/didi/chameleon/commit/f1b236dfe602daf9dd476b9c6e33e980e3640dbc)) - **chameleon-mixins** 修复 百度小程序中动画bug ([ca41f54](https://github.com/didi/chameleon/commit/ca41f5460bc0098ce8b401e4b0fc2baad0ffc254)) ## [0.1.0-alpha.1](https://github.com/didi/chameleon/compare/v0.0.12...v0.1.0-alpha.1) ### Features - 支持百度小程序和支付宝小程序 - 支持mock多域名请求 ## [0.0.16](https://github.com/didi/chameleon/compare/v0.0.15...v0.0.16) ### Bug Fixes * 修复 小程序组件导出 样式文件压缩 * 回退 v0.3.0-alpha.0中引入微信预览模式白屏的问题 ## [0.0.13](https://github.com/didi/chameleon/compare/b2aa4b...6dc5ff9#diff-b21d2ccb648a84e2a7348250c471cc2aL32) ### Bug Fixes - **chameleon-templates** 修复默认初始化项目中的微信app.json默认配置([dc58180](https://github.com/didi/chameleon/commit/dc58180827327bbd966398c57602822992238c1f)) ## [0.0.12](https://github.com/didi/chameleon/compare/v0.0.11...v0.0.12) ### Bug Fixes - **chameleon-css-loader** 修复低版本浏览器todo demo白屏问题,fix [#3](https://github.com/didi/chameleon/issues/3) ([d565a29](https://github.com/didi/chameleon/commit/d565a292ccef56de5c283cce2debeaca5ee7d722)) - **chameleon-css-loader** 修复不处理第一个样式多态的问题([d565a29](https://github.com/didi/chameleon/commit/d565a292ccef56de5c283cce2debeaca5ee7d722)) - **chameleon-loader** 修复cml-ui中组件事件不代理的问题([21e0709](https://github.com/didi/chameleon/commit/21e0709353a2635f9055a79009b9d992dfb68f78)) - **chameleon-templates** 修复todo-demo图片损坏问题([de5b42d](https://github.com/didi/chameleon/commit/de5b42da50e5b7315ce1ad33b82c2e6ed94fe04a)) - **chameleon-templates** 升级初始化项目依赖版本,fix [#2](https://github.com/didi/chameleon/issues/2) ([75ba521](https://github.com/didi/chameleon/commit/75ba52111634f218a404ca85fe57e448f8ed880a)) ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright (C) 2017 Beijing Didi Infinity Technology and Development Co.,Ltd. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ Chameleon [![Build Status](https://www.travis-ci.org/didi/chameleon.svg?branch=master)](https://www.travis-ci.org/didi/chameleon) [![license](https://img.shields.io/npm/l/chameleon-tool.svg?registry_uri=https%3A%2F%2Fregistry.npmjs.com&style=popout-square)](https://www.npmjs.com/package/chameleon-tool) [![version](https://img.shields.io/npm/v/chameleon-tool.svg?style=flat)](https://www.npmjs.com/package/chameleon-tool) **Chameleon**/kəˈmiːlɪən/,简写`CML`,中文名`卡梅龙`;中文意思`变色龙`,意味着就像变色龙一样能适应不同环境的跨端整体解决方案。 [English Introduction](https://github.com/didi/chameleon/blob/gh-pages/doc/quick_start/readme-en.md) | 中文介绍 ## 文档 [新官网文档](http://cml.didi.cn/) [CML.JS.org](https://cml.js.org) - [安装](https://CML.JS.org/doc/quick_start/quick_start.html) - [快速上手](https://CML.JS.org/doc/quick_start/quick_start.html) - [API 文档](https://CML.JS.org/doc/api/api.html) - [组件文档](https://CML.JS.org/doc/component/component.html) - [资源汇总 💰awesome-cml](https://github.com/chameleon-team/awesome-cml):依赖库、demo、完整应用示例、学习资源 - [后续规划](https://github.com/didi/chameleon/wiki/%E5%90%8E%E6%9C%9F%E8%A7%84%E5%88%92) - [五分钟上手视频教程](https://mp.weixin.qq.com/s/3NY_pbqDVnbQSYQG_D2qiA) - [青桔单车 chameleon 跨平台实践分享](https://mp.weixin.qq.com/s/N8PpxRHHtlIHVlemQ1Gepg) - [谁在使用?](https://github.com/didi/chameleon/issues/131) ## 仓库更新说明 本仓库仅包含编译时代码,全部开源代码参见:https://github.com/chameleon-team master为稳定版本,除了紧急 bug 修复,每份代码提交都有很严格的发布流程规范,会先在分支经历一段时间灰度期,确认稳定可用后才会合并到 master, [进行中的项目分支介绍](https://github.com/didi/chameleon/wiki/%E8%BF%9B%E8%A1%8C%E4%B8%AD%E7%9A%84%E9%A1%B9%E7%9B%AE) ## CML 即 多端 支持平台:**web、微信小程序、支付宝小程序、百度小程序、[android(weex)](https://github.com/chameleon-team/chameleon-sdk-android)、[ios(weex)](https://github.com/chameleon-team/chameleon-sdk-ios)、qq 小程序、[字节跳动小程序](https://cml.js.org/doc/example/tt_miniapp.html)、[快应用](https://cml.js.org/doc/example/quickapp_miniapp.html)、持续更新中** **一端所见即多端所见**——多端高度一致,无需关注各端文档。 > 基于多态协议不影响各端差异化灵活性 | web | 微信小程序 | native-weex | 百度小程序 | 支付宝小程序 | | :----------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------: | | | | | | | ## 背景 研发同学在端内既追求 h5 的灵活性,也要追求性能趋近于原生。 面对入口扩张,App 客户端、微信小程序、支付宝小程序、百度小程序、Android 厂商联盟快应用、其他类小程序,单一功能在各平台都要重复实现,开发和维护成本成倍增加。迫切需要维护一套代码可以构建多入口的解决方案,滴滴跨端解决方案 Chameleon 终于发布。真正专注于让一套代码运行多端。 ## 设计理念 软件架构设计里面最基础的概念“拆分”和“合并”,拆分的意义是“分而治之”,将复杂问题拆分成单一问题解决,比如后端业务系统的”微服务化“设计;“合并”的意义是将同样的业务需求抽象收敛到一块,达成高效率高质量的目的,例如后端业务系统中的“中台服务”设计。 而 Chameleon 属于后者,通过定义统一的语言框架+[统一多态协议](http://cml.didi.cn/docs/poly.html),从多端(对应多个独立服务)业务中抽离出自成体系、连续性强、可维护强的“前端中台服务”。 ### 跨端目标 虽然不同各端环境千变万化,但万变不离其宗的是 MVVM 架构思想,**Chameleon 目标是让 MVVM 跨端环境大统一**。 ![跨端目标](https://cml.js.org/doc/assets/mvvm4.png) ## 开发语言 代码示例 ```html ``` 从事过网页编程的人知道,网页编程采用的是 HTML + CSS + JS 这样的组合,同样道理,chameleon 中采用的是 CML + CMSS + JS。 [JS](https://CML.JS.org/doc/logic/logic.html)语法用于处理页面的逻辑层,与普通网页编程相比,本项目目标定义标准 MVVM 框架,拥有完整的生命周期,watch,computed,数据双向绑定等优秀的特性,能够快速提高开发速度、降低维护成本。 [CML](https://CML.JS.org/doc/view/cml.html)(Chameleon Markup Language)用于描述页面的结构,我们知道 HTML 是有一套标准的语义化标签,例如文本是`` 按钮是` , document.getElementById('react-container')); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/custom-style-path/expected.js ================================================ "use strict"; require("antd/lib/button/style/2x"); var _button = _interopRequireDefault(require("antd/lib/button")); var _react = _interopRequireDefault(require("react")); var _reactDom = _interopRequireDefault(require("react-dom")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById('react-container')); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/custom-style-path-ignore/actual.js ================================================ import React from "react"; import ReactDom from "react-dom"; import { Animation, Button } from "antd"; ReactDOM.render( , document.getElementById("react-container") ); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/custom-style-path-ignore/expected.js ================================================ "use strict"; var _animation = _interopRequireDefault(require("antd/lib/animation")); require("antd/lib/button/style/2x"); var _button = _interopRequireDefault(require("antd/lib/button")); var _react = _interopRequireDefault(require("react")); var _reactDom = _interopRequireDefault(require("react-dom")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement(_animation.default, null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById("react-container")); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/execute-direct/actual.js ================================================ import { message } from 'antd'; message('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/execute-direct/expected.js ================================================ "use strict"; var _message2 = _interopRequireDefault(require("antd/lib/message")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _message2.default)('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/execute-member/actual.js ================================================ import { message } from 'antd'; message.success('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/execute-member/expected.js ================================================ "use strict"; var _message2 = _interopRequireDefault(require("antd/lib/message")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _message2.default.success('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/export-import/actual.js ================================================ import { DatePicker } from 'antd'; export default DatePicker; ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/export-import/expected.js ================================================ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _datePicker = _interopRequireDefault(require("antd/lib/date-picker")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _default = _datePicker.default; exports.default = _default; ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/expression-statement/actual.js ================================================ import {Toast} from 'antd-mobile'; window.Toast = Toast; Toast.success('test'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/expression-statement/expected.js ================================================ "use strict"; var _antdMobile = require("antd-mobile"); window.Toast = _antdMobile.Toast; _antdMobile.Toast.success('test'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/file-name/actual.js ================================================ import { Select } from 'antd-mobile-fake-2.0'; if (Select) {} console.log(Select); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/file-name/expected.js ================================================ "use strict"; var _index = _interopRequireDefault(require("antd-mobile-fake-2.0/lib/select/index.native")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } if (_index.default) {} console.log(_index.default); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/import-alias/actual.js ================================================ import { Select as AntdSelect } from 'antd'; if (AntdSelect) { console.log('foo'); } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/import-alias/expected.js ================================================ "use strict"; var _select = _interopRequireDefault(require("antd/lib/select")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } if (_select.default) { console.log('foo'); } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/import-css/actual.js ================================================ import { message } from 'antd'; import { Button } from 'antd'; message('xxx'); ReactDOM.render(
); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/import-css/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); require("antd/lib/button/style"); var _button = _interopRequireDefault(require("antd/lib/button")); require("antd/lib/message/style"); var _message2 = _interopRequireDefault(require("antd/lib/message")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _message2.default)('xxx'); ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx"))); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/keep-named-import/actual.js ================================================ import { start, end } from 'stream'; start(); end(); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/keep-named-import/expected.js ================================================ "use strict"; var _end2 = require("stream/lib/end"); var _start2 = require("stream/lib/start"); (0, _start2.start)(); (0, _end2.end)(); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/material-ui/actual.js ================================================ import { Toolbar } from 'material-ui'; Toolbar('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/material-ui/expected.js ================================================ "use strict"; var _Toolbar2 = _interopRequireDefault(require("material-ui/Toolbar")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _Toolbar2.default)('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/member-expression/actual.js ================================================ import antd from 'antd'; ReactDOM.render(
Button
); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/member-expression/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, { type: "primary" }, "Button"))); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/modules-false/actual.js ================================================ import React from 'react'; import ReactDom from 'react-dom'; import { Button } from 'antd'; ReactDOM.render(
, document.getElementById('react-container')); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/modules-false/expected.js ================================================ "use strict"; require("antd/lib/button/style"); var _button = _interopRequireDefault(require("antd/lib/button")); var _react = _interopRequireDefault(require("react")); var _reactDom = _interopRequireDefault(require("react-dom")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx")), document.getElementById('react-container')); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-libraries/actual.js ================================================ import { Select } from 'antd'; import { Select as SelectMobile } from 'antd-mobile'; if (Select) {} if (SelectMobile) {} ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-libraries/expected.js ================================================ "use strict"; var _select = _interopRequireDefault(require("antd-mobile/lib/select")); var _select2 = _interopRequireDefault(require("antd/lib/select")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } if (_select2.default) {} if (_select.default) {} ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-libraries-hilojs/actual.js ================================================ import { Select } from 'antd'; import { Abc, Class } from 'hilojs'; if (Select) {} if (Class && Abc) {} ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-libraries-hilojs/expected.js ================================================ "use strict"; var _abc = _interopRequireDefault(require("hilojs/abc")); var _class = _interopRequireDefault(require("hilojs/core/class")); var _select = _interopRequireDefault(require("antd/lib/select")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } if (_select.default) {} if (_class.default && _abc.default) {} ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-words/actual.js ================================================ import { InputNumber } from 'antd'; ; ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/multiple-words/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _inputNumber = _interopRequireDefault(require("antd/lib/input-number")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _react.default.createElement(_inputNumber.default, null); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/new-expression/actual.js ================================================ import { Button } from 'antd'; new Button(); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/new-expression/expected.js ================================================ "use strict"; var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } new _button.default(); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/object-shorthand/actual.js ================================================ import { message } from 'antd'; message('xxx'); function App() { const message = 'xxx'; console.log({ message }); } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/object-shorthand/expected.js ================================================ "use strict"; var _message2 = _interopRequireDefault(require("antd/lib/message")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _message2.default)('xxx'); function App() { var message = 'xxx'; console.log({ message: message }); } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/property/actual.js ================================================ import { Button } from 'antd'; ReactDOM.render(
); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/property/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement("div", { component: _button.default })); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/react-element/actual.js ================================================ import { Button } from 'antd'; ReactDOM.render(
); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/react-element/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx"))); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/react-toolbox/actual.js ================================================ import { AppBar } from 'react-toolbox'; AppBar('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/react-toolbox/expected.js ================================================ "use strict"; var _app_bar = _interopRequireDefault(require("react-toolbox/lib/app_bar")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _app_bar.default)('xxx'); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/return/actual.js ================================================ import { toast } from 'antd'; function a() { return toast; } function b(toast) { return toast; } function c() { var toast = 'toast'; return toast; } function d() { var toast = 'toast'; return function () { return toast; }; } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/return/expected.js ================================================ "use strict"; var _toast2 = _interopRequireDefault(require("antd/lib/toast")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function a() { return _toast2.default; } function b(toast) { return toast; } function c() { var toast = 'toast'; return toast; } function d() { var toast = 'toast'; return function () { return toast; }; } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/specifier-alias/actual.js ================================================ import { Button as Button1 } from 'antd'; const foo = Button1.foo ReactDOM.render(
xxxx
); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/specifier-alias/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var foo = _button.default.foo; ReactDOM.render(_react.default.createElement("div", null, _react.default.createElement(_button.default, null, "xxxx"))); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/use-multiple-times/actual.js ================================================ import a from 'a'; import { Button } from 'antd-mobile'; a(Button); a(Button); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/use-multiple-times/expected.js ================================================ "use strict"; var _button = _interopRequireDefault(require("antd-mobile/lib/button")); var _a = _interopRequireDefault(require("a")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _a.default)(_button.default); (0, _a.default)(_button.default); ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/variable-declarator/actual.js ================================================ import { Button } from 'antd'; const a = Button; ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/variable-declarator/expected.js ================================================ "use strict"; var _button = _interopRequireDefault(require("antd/lib/button")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var a = _button.default; ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/variable-scope/actual.js ================================================ import { message } from 'antd'; message('xxx'); function App() { const message = 'xxx'; return
{message}
; } ================================================ FILE: packages/babel-plugin-chameleon-import/test/fixtures/variable-scope/expected.js ================================================ "use strict"; var _react = _interopRequireDefault(require("react")); var _message2 = _interopRequireDefault(require("antd/lib/message")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } (0, _message2.default)('xxx'); function App() { var message = 'xxx'; return _react.default.createElement("div", null, message); } ================================================ FILE: packages/chameleon-css-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": 6, "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/chameleon-css-loader/.gitignore ================================================ ================================================ FILE: packages/chameleon-css-loader/handler/lines.js ================================================ // 静态编译和运行时 web和小程序端对lines属性特殊处理 module.exports = function(linesNumber) { // 作为一个属性注意最后不能添加分号 return `display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: ${linesNumber}; overflow: hidden` } ================================================ FILE: packages/chameleon-css-loader/index.js ================================================ const loaderUtils = require('loader-utils') const webParse = require('./parser/web'); const miniParse = require('./parser/miniapp'); const weexParse = require('./parser/weex'); const mediaParse = require('./parser/media'); module.exports = function(source, options) { options = options || loaderUtils.getOptions(this); options.filePath = this.resourcePath; // 处理差异化的media代码块功能 if (options.media) { return mediaParse(source, options.cmlType); } if (source) { let result; switch (options.platform) { case 'web': result = webParse(source, options); break; case 'miniapp': result = miniParse(source, options); break; case 'weex': result = weexParse(source, options); break; default: break; } return result; } return source; } ================================================ FILE: packages/chameleon-css-loader/package.json ================================================ { "name": "chameleon-css-loader", "version": "1.0.8", "description": "chameleon样式处理", "main": "index.js", "scripts": { "test": "mocha --recursive --reporter spec", "cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive", "eslint": "eslint ./proxy ./transform ./parser utils.js index.js", "eslint:fix": "eslint utils.js --ext .js parser --ext .js proxy --ext .js transform --fix" }, "keywords": [ "css" ], "author": "Chameleon-Team", "license": "Apache", "dependencies": { "autoprefixer": "^9.3.1", "hash-sum": "^1.0.2", "loader-utils": "^1.1.0", "postcss": "^7.0.6", "postcss-plugin-px2rem": "^0.7.0", "rework": "^1.0.1" }, "devDependencies": { "chai": "^4.2.0", "chameleon-tool-utils": "1.0.8", "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/chameleon-css-loader/parser/media.js ================================================ module.exports = function(source = '', targetType) { let reg = /@media\s*cml-type\s*\(([\w\s,]*)\)\s*/g; if (!reg.test(source)) { return source; } reg.lastIndex = 0; while (true) { // eslint-disable-line let result = reg.exec(source); if (!result) {break;} let cmlTypes = result[1] || ''; cmlTypes = cmlTypes.split(',').map(item => item.trim()); let isSave = ~cmlTypes.indexOf(targetType); let startIndex = result.index; // @media的开始 let currentIndex = source.indexOf('{', startIndex); // 从第一个@media开始 let signStartIndex = currentIndex; // 第一个{的位置 if (currentIndex == -1) { throw new Error("@media cml-type format err"); } let signStack = []; signStack.push(0); while (signStack.length > 0) { let index1 = source.indexOf('{', currentIndex + 1); let index2 = source.indexOf('}', currentIndex + 1); let index; // 都有的话 index为最前面的 if (index1 !== -1 && index2 !== -1) { index = Math.min(index1, index2); } else { index = Math.max(index1, index2); } if (index === -1) { throw new Error("@media cml-type format err"); } let sign = source[index]; currentIndex = index; if (sign === '{') { signStack.push(signStack.length); } else if (sign === '}') { signStack.pop(); } } // 操作source if (isSave) { // 保存的@media var sourceArray = Array.from(source); sourceArray.splice(startIndex, currentIndex - startIndex + 1, source.slice(signStartIndex + 1, currentIndex)); source = sourceArray.join(''); } else { // 删除的 source = source.slice(0, startIndex) + source.slice(currentIndex + 1); } reg.lastIndex = 0; } return source; } ================================================ FILE: packages/chameleon-css-loader/parser/miniapp.js ================================================ const cpx = require('../postcss/cpx.js'); const weexPlus = require('../postcss/weex-plus'); const addAlipayClassPlugin = require('../postcss/add-alipay-class') const postcss = require('postcss'); const cmlUtils = require('chameleon-tool-utils'); module.exports = function(source, options = {}) { options.cpxType = 'rpx'; let {cmlType, filePath} = options; let globalCssPath = [`chameleon-runtime/src/platform/${cmlType}/style/index.css`, `chameleon-runtime/src/platform/${cmlType}/style/page.css`]; let globalStyleConfig = cml.config.get().globalStyleConfig; if (globalStyleConfig && globalStyleConfig.globalCssPath && cmlUtils.isFile(globalStyleConfig.globalCssPath)) { globalCssPath.push(globalStyleConfig.globalCssPath) } let isGlobalCss = globalCssPath.some((item) => filePath.includes(item)) if (cmlType === 'alipay' && !isGlobalCss) { // 对于全局样式不能经过 addAlipayClassPlugin 这个插件进行样式唯一性的处理;比如这样的 .cml-57b5135a.scroller-wrap return postcss([cpx(options), weexPlus(), addAlipayClassPlugin(options)]).process(source).css; } else { return postcss([cpx(options), weexPlus()]).process(source).css; } } ================================================ FILE: packages/chameleon-css-loader/parser/web.js ================================================ const cpx = require('../postcss/cpx.js'); const postcss = require('postcss'); const px2rem = require('postcss-plugin-px2rem'); const weexPlus = require('../postcss/weex-plus'); module.exports = function(source, options = {}) { if (options.rem === true) { return postcss([px2rem(options.remOptions), weexPlus()]).process(source).css; } else { options.cpxType = 'scale'; return postcss([cpx(options), weexPlus()]).process(source).css; } } ================================================ FILE: packages/chameleon-css-loader/parser/weex.js ================================================ const weex = require('../postcss/weex.js'); module.exports = function(source, options = {}) { return weex(source, options); } ================================================ FILE: packages/chameleon-css-loader/postcss/add-alipay-class.js ================================================ const postcss = require('postcss'); const hash = require('hash-sum'); module.exports = postcss.plugin('add-alipay-class-name', function(options) { let { filePath} = options; let randomClassName = hash(filePath); options.alipayClassName = `.cml-${randomClassName}`; return function(css, result) { function getSelector (value) { let temp = ''; for (let i = 0, len = value.length; i < len; i++) { if (value[i] === '.') { temp += `${options.alipayClassName}.` } else { temp += value[i]; } } return temp; } css.walkRules(rule => { if (rule.selector.indexOf('.') === 0) { let newSelector = getSelector(rule.selector); rule.selector = newSelector; } }); }; }) ================================================ FILE: packages/chameleon-css-loader/postcss/cpx.js ================================================ const postcss = require('postcss'); // 非rem的cpx处理,rem的利用postcss-plugin-px2rem插件即可实现 module.exports = postcss.plugin('postcss-plugin-cpx', function (options) { // parseType 参数决定cpx的转换 let { unitPrecision = 5, scale = 1, cpxType = 'scale'} = options; const pxRegex = /(\d*\.?\d+)cpx/gi; const pxReplace = function(m, $1, $2) { switch (cpxType) { case 'scale': return toFixed(parseFloat($1, 10) * scale, unitPrecision) + 'px'; case 'px': return $1 + "px"; case 'rpx': return $1 + "rpx"; default: return m; } } function toFixed(number, precision) { var multiplier = Math.pow(10, precision + 1); var wholeNumber = Math.floor(number * multiplier); return Math.round(wholeNumber / 10) * 10 / multiplier; } return function (css) { css.walkDecls(function (decl, i) { if (~decl.value.indexOf('cpx')) { let value = decl.value.replace(pxRegex, pxReplace); decl.value = value; } }); css.walkAtRules('media', function (rule) { if (!rule.params.indexOf('cpx')) { rule.params = rule.params.replace(pxRegex, pxReplace); } }); }; }); ================================================ FILE: packages/chameleon-css-loader/postcss/weex-plus.js ================================================ // 为web和小程序提供编译weex为标准的特殊样式 const postcss = require('postcss'); module.exports = postcss.plugin('postcss-weex-plus', function(options) { return (root, result) => { root.walkDecls((decl, i) => { if (decl.prop === 'lines') { const decllist = [ postcss.decl({prop: 'overflow', value: 'hidden'}), postcss.decl({prop: 'text-overflow', value: 'ellipsis'}), postcss.decl({prop: 'display', value: '-webkit-box'}), postcss.decl({prop: '-webkit-line-clamp', value: decl.value}), postcss.decl({prop: '-webkit-box-orient', value: 'vertical'}) ]; decl.parent.append(...decllist) } // decl.parent.removeChild(decl); }) } }) ================================================ FILE: packages/chameleon-css-loader/postcss/weex.js ================================================ const rework = require('rework'); const weexCssSupport = require('../transform/weex'); /** * 处理css源码 * * @param {string} source 源码 * @return {string} 处理后的数据 */ module.exports = source => { let result = rework(source).use(function (ast) { ast.rules.forEach(rule => { if (!rule.declarations) { return; } let allDeclarations = []; rule.declarations.forEach(declaration => { delete declaration.position; // 注释部分不做处理 if (declaration.type !== 'comment') { let declarations = weexCssSupport.convert(declaration); allDeclarations = allDeclarations.concat(declarations); } }); rule.declarations = allDeclarations; }); }) .toString(); return result; }; ================================================ FILE: packages/chameleon-css-loader/proxy/proxyMiniapp.js ================================================ const utils = require('../utils'); const lines = require('../handler/lines'); // 运行时的cpx2rpx不能使用postcss处理,因为$cmlStyle方法用到了该方法,在运行时使用postcss 会出现Cannot find module "fs"的错误 module.exports = function(content) { content = utils.disappearCssComment(content); return parse(content); function parse (style) { return style .split(';') .filter(declaration => !!declaration.trim()) .map(declaration => { let {key, value} = utils.getStyleKeyValue(declaration); return { property: key, value }; }) .map(declaration => { if (declaration.property === 'lines') { return lines(declaration.value); } declaration.value = handle(declaration.value); return declaration.property + ':' + declaration.value; }) .join(';') } function handle(content) { if (content && content.replace) { content = content.replace(/(\d*\.?\d+)cpx/ig, function (m, $1) { return $1 + 'rpx' }) } return content } } ================================================ FILE: packages/chameleon-css-loader/proxy/proxyWeb.js ================================================ /** 提供编译时样式处理的方法 */ // 运行时不能使用postcss 体积过大 const lines = require('../handler/lines'); const utils = require('../utils'); module.exports = function(content, options) { if (typeof options === 'string') { options = JSON.parse(utils.singlequot2doublequot(options)) } if (typeof content !== 'string') { throw new Error(`expected the value of style is string but get ${typeof content}`); } content = utils.disappearCssComment(content); content = utils.uniqueStyle(content); return parse(content); function parse (style) { return style .split(';') .filter(declaration => !!declaration.trim()) .map(declaration => { let {key, value} = utils.getStyleKeyValue(declaration); return { property: key, value }; }) .map(declaration => { if (declaration.property === 'lines') { return lines(declaration.value); } declaration.value = handle(declaration.value, options); return declaration.property + ':' + declaration.value; }) .join(';') } function handle(content, options) { const pxRegex = /(\d*\.?\d+)cpx/gi; let unitPrecision = 5; function toFixed(number, precision) { var multiplier = Math.pow(10, precision + 1); var wholeNumber = Math.floor(number * multiplier); return Math.round(wholeNumber / 10) * 10 / multiplier; } if (options.rem === true) { let base = options.remOptions.rootValue.cpx; return content.replace(pxRegex, function(m, $1) { return toFixed(parseFloat($1, 10) / base, unitPrecision) + 'rem'; }) } else { let scale = options.scale; return content.replace(pxRegex, function(m, $1) { return toFixed(parseFloat($1, 10) * scale, unitPrecision) + 'px'; }) } } } ================================================ FILE: packages/chameleon-css-loader/test/parser-test/media.test.js ================================================ let content = ` @media cml-type(wx) { body { color: red; } } @media cml-type(weex) { body { color: blue; } } @media cml-type(web) { body { color: green; } } @media cml-type(web,wx) { body { color: orange; } } .border-scale(@color, @border-radius: 2px, @border-width: 1px 1px 1px 1px) { &:after { content: " "; position: absolute; top: 0; left: 0; width: 200%; height: 200%; border: 1px solid @color; border-width: @border-width; border-radius: 2 * @border-radius; transform-origin: 0% 0%; -webkit-transform-origin: 0% 0%; -webkit-transform: scale(0.5, 0.5); transform: scale(0.5, 0.5); pointer-events: none; -webkit-box-sizing: border-box; box-sizing: border-box; } } ` const expect = require('chai').expect; let mediaParse = require('../../parser/media.js'); describe('parse/media.js', function() { it('cmltype wx', function() { let result = mediaParse(content, 'wx'); console.log(result) expect(/red/.test(result)).to.be.ok; expect(/blue/.test(result)).to.equals(false); }) }) ================================================ FILE: packages/chameleon-css-loader/test/parser-test/miniapp.test.js ================================================ const parseCss = require('../../parser/miniapp.js'); const expect = require('chai').expect; let source = `body {width:100cpx;font-size:26px;/* height:200px; */}`; let alipayCSS = `.cls1{color:red} .cls2{width:100cpx;height:200cpx;} .cls3:{font-size:30cpx;}` describe('parse/miniapp', function() { it('parse cssstyle px to rpx but leave comment in style alone', function() { let result = parseCss(source,{ cmlType:'alipay', filePath:"chameleon-runtime/src/platform/alipay/style/index.css" }); expect(/100rpx/.test(result)).to.be.ok; }) it('parse cssstyle px to rpx but leave comment in style alone', function() { let result = parseCss(alipayCSS,{ cmlType:'alipay', filePath:"/src/page/index.cml" }); expect(/100rpx/.test(result)).to.be.ok; }) it('parse cssstyle px to rpx but leave comment in style alone', function() { let result = parseCss(source,{ cmlType:'wx', filePath:"chameleon-runtime/src/platform/alipay/style/index.css" }); expect(/100rpx/.test(result)).to.be.ok; }) }) ================================================ FILE: packages/chameleon-css-loader/test/parser-test/web.test.js ================================================ const parseCss = require('../../parser/web.js'); const expect = require('chai').expect; let source = `body {width:75cpx; height: 1px}`; let source2 = `width:75cpx; height: 1px;`; describe('parse/web', function() { it('web-cssStyle:if options.rem to be truthy ,parse px to rem,else multiply the px value by options.scale', function() { let options1 = { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: {cpx: 75} } } let options2 = { rem: false, scale: 0.5, remOptions: { // base on 750px standard. rootValue: {cpx: 75} } } let result1 = parseCss(source, options1); let result2 = parseCss(source, options2); expect(result1).to.equal('body {width:1rem; height: 1px}'); expect(result2).to.equal('body {width:37.5px; height: 1px}') let result3 = parseCss(source2, options1); expect(result3).to.equal('width:1rem; height: 1px;'); let result4 = parseCss(source2, options2); expect(result4).to.equal('width:37.5px; height: 1px;') }) }) ================================================ FILE: packages/chameleon-css-loader/test/parser-test/weex.test.js ================================================ const parseCss = require('../../parser/weex.js'); const expect = require('chai').expect; let source = `body {width:75cpx;border:1cpx solid red;}`; describe('parse/weex', function() { it('if options.rem to be truthy ,parse px to rem,else multiply the px value by options.scale', function() { let result = parseCss(source);// body {width:1rem;} console.log(result); expect(/border\-style/.test(result)).to.be.ok; expect(/border\-width/.test(result)).to.be.ok; expect(/border\-color/.test(result)).to.be.ok; expect(/75px/.test(result)).to.be.ok; }) }) describe('parse/weex', function() { let css = ` .a { border: solid 1px red; } .b { border: dotted 0 rgb(255,255, 255); } .c { border-left: solid 1px #ccc; } `; it('border', function() { let result = parseCss(css);// body {width:1rem;} console.log(result); expect(result).to.equal(`.a {\n border-style: solid;\n border-width: 1px;\n border-color: #ff0000;\n}\n\n.b {\n border-style: dotted;\n border-width: 0;\n border-color: rgb(255,255,255);\n}\n\n.c {\n border-left-style: solid;\n border-left-width: 1px;\n border-left-color: #ccc;\n}`); }) }) describe('parse/weex', function() { let css = ` .a { margin-left: 10px; } .b { margin: 10px; } .c { margin: 10px 20px; } .d { margin: 10px 20px 10px; } .e { margin: 10px 20px 5px 15px; } .f { border-style:solid; border-width:10cpx; border-color:red; flex-flow:row; } `; it('margin', function() { let result = parseCss(css);// body {width:1rem;} expect(result).to.equal('.a {\n margin-left: 10px;\n}\n\n.b {\n margin-top: 10px;\n margin-right: 10px;\n margin-bottom: 10px;\n margin-left: 10px;\n}\n\n.c {\n margin-top: 10px;\n margin-right: 20px;\n margin-bottom: 10px;\n margin-left: 20px;\n}\n\n.d {\n margin-top: 10px;\n margin-right: 20px;\n margin-bottom: 10px;\n margin-left: 20px;\n}\n\n.e {\n margin-top: 10px;\n margin-right: 20px;\n margin-bottom: 5px;\n margin-left: 15px;\n}\n\n.f {\n border-top-style: solid;\n border-right-style: solid;\n border-bottom-style: solid;\n border-left-style: solid;\n border-top-width: 10px;\n border-right-width: 10px;\n border-bottom-width: 10px;\n border-left-width: 10px;\n border-top-color: red;\n border-right-color: red;\n border-bottom-color: red;\n border-left-color: red;\n flex-direction: row;\n}'); }) }) describe('parse/weex', function() { let css = ` .a { padding-left: 10px; } .b { padding: 10px; } `; it('padding', function() { let result = parseCss(css);// body {width:1rem;} console.log(result); expect(result).to.equal(`.a {\n padding-left: 10px;\n}\n\n.b {\n padding-top: 10px;\n padding-right: 10px;\n padding-bottom: 10px;\n padding-left: 10px;\n}`); }) }) describe('parse/weex', function() { let css = ` .a { background: #ccc url(img.png) no-repeat scroll center center / 50% content-box border-box; } .b { background: url(img.png) #ccc; } `; it('background', function() { let result = parseCss(css);// body {width:1rem;} console.log(result); expect(result).to.equal(`.a {\n background-color: #ccc;\n background-position: center center;\n background-size: 50%;\n background-repeat: no-repeat;\n background-origin: content-box;\n background-clip: border-box;\n background-attachment: scroll;\n background-image: url(img.png);\n}\n\n.b {\n background-color: #ccc;\n background-image: url(img.png);\n}`); }) }) ================================================ FILE: packages/chameleon-css-loader/test/postcss-test/weex-plus.test.js ================================================ var postcss = require('postcss'); var weexPlus = require('../../postcss/weex-plus'); var content = ` .class1 { lines: 1; } ` const expect = require('chai').expect; describe('postcss/weex-plus', function() { it('convert lines', function() { let ret = postcss(weexPlus()).process(content).css; console.log(ret) expect(ret).to.equal(`\n.class1 {\n lines: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n}\n`); }) }) ================================================ FILE: packages/chameleon-css-loader/test/proxy-test/proxyMiniapp.test.js ================================================ const parseCss = require('../../proxy/proxyMiniapp.js'); const expect = require('chai').expect; let source = `width:75cpx;font-size:26px;/* height:200px; */`; describe('proxy/miniapp', function() { it('parse miniapp cssvalue cpx to rpx', function() { let result = parseCss(source); expect(/75rpx/.test(result)).to.be.ok; }) it('lines', function() { let result = parseCss('lines:1;'); expect(result).to.equal('display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden'); }) }) ================================================ FILE: packages/chameleon-css-loader/test/proxy-test/proxyWeb.test.js ================================================ const parseCss = require('../../proxy/proxyWeb.js'); const expect = require('chai').expect; let source = `width:75cpx;font-size:26px;/* height:200px; */`; describe('parse/web', function() { it('web-cssvalue : if options.rem to be truthy ,parse cpx to rem,else multiply the cpx value by options.scale', function() { let options1 = { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: {cpx: 75}, // to leave 1px alone. minPixelValue: 1.01 } } let options2 = { rem: false, scale: 0.5, remOptions: { // base on 750px standard. rootValue: {cpx: 75}, // to leave 1px alone. minPixelValue: 1.01 } } let result1 = parseCss(source, options1);// body {width:1rem;} let result2 = parseCss(source, options2);// body {width:37.5px;} expect(/1rem/.test(result1)).to.be.ok; expect(/37.5px/.test(result2)).to.be.ok; }) it('lines', function() { let result = parseCss('lines:1;'); expect(result).to.equal('display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; overflow: hidden'); }) }) ================================================ FILE: packages/chameleon-css-loader/test/transform/weex.test.js ================================================ const parseCss = require('../../transform/weex.js'); const expect = require('chai').expect; let source = `width:75cpx;border:1cpx solid red;`; describe('parse/weex', function() { it('if options.rem to be truthy ,parse px to rem,else multiply the px value by options.scale', function() { let result = parseCss.parse(source);// body {width:1rem;} console.log(result); expect(/75px/.test(result)).to.be.ok; expect(/1px/.test(result)).to.be.ok; }) }) ================================================ FILE: packages/chameleon-css-loader/test/utils.test.js ================================================ const utils = require('../utils.js'); const expect = require('chai').expect; describe('utils', function() { it('transform singlequot to doublequot', function() { expect(utils.singlequot2doublequot(`'`)).to.be.equal(`"`); }); it('unique cssStyleValue ', function() { expect(utils.uniqueStyle(`width:300px;width:300px`)).to.be.equal(`width:300px`) }); it('disappearCssComment', function() { expect(utils.disappearCssComment(`/*width:300px;*/`)).to.be.empty; }) }) ================================================ FILE: packages/chameleon-css-loader/transform/color.js ================================================ const COLOR_MAP = { aliceblue: '#f0f8ff', antiquewhite: '#faebd7', aqua: '#00ffff', aquamarine: '#7fffd4', azure: '#f0ffff', beige: '#f5f5dc', bisque: '#ffe4c4', black: '#000000', blanchedalmond: '#ffebcd', blue: '#0000ff', blueviolet: '#8a2be2', brown: '#a52a2a', burlywood: '#deb887', cadetblue: '#5f9ea0', chartreuse: '#7fff00', chocolate: '#d2691e', coral: '#ff7f50', cornflowerblue: '#6495ed', cornsilk: '#fff8dc', crimson: '#dc143c', cyan: '#00ffff', darkblue: '#00008b', darkcyan: '#008b8b', darkgoldenrod: '#b8860b', darkgray: '#a9a9a9', darkgrey: '#a9a9a9', darkgreen: '#006400', darkkhaki: '#bdb76b', darkmagenta: '#8b008b', darkolivegreen: '#556b2f', darkorange: '#ff8c00', darkorchid: '#9932cc', darkred: '#8b0000', darksalmon: '#e9967a', darkseagreen: '#8fbc8f', darkslateblue: '#483d8b', darkslategray: '#2f4f4f', darkslategrey: '#2f4f4f', darkturquoise: '#00ced1', darkviolet: '#9400d3', deeppink: '#ff1493', deepskyblue: '#00bfff', dimgray: '#696969', dimgrey: '#696969', dodgerblue: '#1e90ff', firebrick: '#b22222', floralwhite: '#fffaf0', forestgreen: '#228b22', fuchsia: '#ff00ff', gainsboro: '#dcdcdc', ghostwhite: '#f8f8ff', gold: '#ffd700', goldenrod: '#daa520', gray: '#808080', grey: '#808080', green: '#008000', greenyellow: '#adff2f', honeydew: '#f0fff0', hotpink: '#ff69b4', indianred: '#cd5c5c', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', lavenderblush: '#fff0f5', lawngreen: '#7cfc00', lemonchiffon: '#fffacd', lightblue: '#add8e6', lightcoral: '#f08080', lightcyan: '#e0ffff', lightgoldenrodyellow: '#fafad2', lightgray: '#d3d3d3', lightgrey: '#d3d3d3', lightgreen: '#90ee90', lightpink: '#ffb6c1', lightsalmon: '#ffa07a', lightseagreen: '#20b2aa', lightskyblue: '#87cefa', lightslateblue: '#8470ff', lightslategray: '#778899', lightslategrey: '#778899', lightsteelblue: '#b0c4de', lightyellow: '#ffffe0', lime: '#00ff00', limegreen: '#32cd32', linen: '#faf0e6', magenta: '#ff00ff', maroon: '#800000', mediumaquamarine: '#66cdaa', mediumblue: '#0000cd', mediumorchid: '#ba55d3', mediumpurple: '#9370db', mediumseagreen: '#3cb371', mediumslateblue: '#7b68ee', mediumspringgreen: '#00fa9a', mediumturquoise: '#48d1cc', mediumvioletred: '#c71585', midnightblue: '#191970', mintcream: '#f5fffa', mistyrose: '#ffe4e1', moccasin: '#ffe4b5', navajowhite: '#ffdead', navy: '#000080', oldlace: '#fdf5e6', olive: '#808000', olivedrab: '#6b8e23', orange: '#ffa500', orangered: '#ff4500', orchid: '#da70d6', palegoldenrod: '#eee8aa', palegreen: '#98fb98', paleturquoise: '#afeeee', palevioletred: '#db7093', papayawhip: '#ffefd5', peachpuff: '#ffdab9', peru: '#cd853f', pink: '#ffc0cb', plum: '#dda0dd', powderblue: '#b0e0e6', purple: '#800080', rebeccapurple: '#663399', red: '#ff0000', rosybrown: '#bc8f8f', royalblue: '#4169e1', saddlebrown: '#8b4513', salmon: '#fa8072', sandybrown: '#f4a460', seagreen: '#2e8b57', seashell: '#fff5ee', sienna: '#a0522d', silver: '#c0c0c0', skyblue: '#87ceeb', slateblue: '#6a5acd', slategray: '#708090', slategrey: '#708090', snow: '#fffafa', springgreen: '#00ff7f', steelblue: '#4682b4', tan: '#d2b48c', teal: '#008080', thistle: '#d8bfd8', tomato: '#ff6347', transparent: '#000000', turquoise: '#40e0d0', violet: '#ee82ee', violetred: '#d02090', wheat: '#f5deb3', white: '#ffffff', whitesmoke: '#f5f5f5', yellow: '#ffff00', yellowgreen: '#9acd32' }; module.exports = function (color) { return COLOR_MAP[color] || color; }; ================================================ FILE: packages/chameleon-css-loader/transform/weex.js ================================================ const color = require('./color'); const utils = require('../utils.js'); /** * 处理映射表 * * @type {Object} */ const HANDLE_MAP = { directions: ['top', 'right', 'bottom', 'left'], angles: ['top-left', 'top-right', 'bottom-right', 'bottom-left'], borderAttributes: ['style', 'width', 'color'], backgroundAttributes: ['color', 'position', 'size', 'repeat', 'origin', 'clip', 'attachment', 'image'], flexAttributes: ['direction', 'wrap'] }; /** * 获取声明列表 * * @param {string} tpl 声明模板 * @param {string} value 声明值 * @param {string} type 声明类型 * @return {Array} 声明列表 */ let getDeclarations = (tpl, value, type) => { let declarations = []; tpl.replace(/\${(.*)}/g, (match, varible) => { declarations = HANDLE_MAP[varible + 's'].map(item => { let val = value; if (type == 'margin' || type == 'padding') { val = getBoxValues(value)[item]; } else if (varible == 'borderAttribute') { val = getBorderValues(value)[item]; } else if (varible == 'backgroundAttribute') { val = getBackgroundValues(value)[item]; } else if (varible == 'flexAttribute') { val = getFlexValues(value)[item]; } else if (varible == 'angle') { val = getBorderRadiusValues(value)[item]; } else { val = value; } if (val) { return { type: 'declaration', property: tpl.replace(/\${.*}/g, item), value: val }; } else { return null; } }).filter(declaration => declaration); }); return declarations; } let getValueSegs = value => { let vals = value.replace(/\s*,\s*/g, ',').split(/\s+/); return vals.map(color); }; /** * 获取边框样式值 * * @param {string} value 边框样式值 * @return {Object} 边框样式表 */ let getBorderValues = value => { let result = {}; let vals = getValueSegs(value); vals.forEach(val => { if (/^(solid|dashed|dotted|none)/g.test(val)) { result.style = val; } else if (/^(0(px)?|[0-9]+px)/g.test(val)) { result.width = val; } else if (/^(#[0-9a-fA-F]*|rgba?\(.*?\))/g.test(val)) { result.color = val; } }); return result; }; let getFlexValues = value => { let result = {}; let vals = getValueSegs(value); if (vals.length == 2) { result = { direction: 'inherit', wrap: 'inherit' }; } let directions = ['row', 'row-reverse', 'column', 'column-reverse', 'initial', 'initial']; let wraps = ['nowrap', 'wrap', 'wrap-reverse', 'initial', 'initial']; vals.forEach(function (val, index) { if (index == 0 && (directions.indexOf(val) > -1)) { result.direction = val; } else if (index == 1 && (wraps.indexOf(val) > -1)) { result.wrap = val; } }); return result; }; /** * 获取box样式值 * * @param {string} value box样式值 * @return {Object} box样式表 */ let getBoxValues = value => { let vals = getValueSegs(value); if (vals.length == 1) { return { top: vals[0], right: vals[0], bottom: vals[0], left: vals[0] }; } else if (vals.length == 2) { return { top: vals[0], right: vals[1], bottom: vals[0], left: vals[1] }; } else if (vals.length == 3) { return { top: vals[0], right: vals[1], bottom: vals[2], left: vals[1] }; } else if (vals.length == 4) { return { top: vals[0], right: vals[1], bottom: vals[2], left: vals[3] }; } else { return { top: 0, right: 0, bottom: 0, left: 0 }; } }; let getBorderRadiusValues = value => { let results = []; let vals = value.split('/'); vals.forEach(val => { let vals = getValueSegs(value); let result; if (vals.length == 1) { result = { 'top-left': vals[0], 'top-right': vals[0], 'bottom-right': vals[0], 'bottom-left': vals[0] }; } else if (vals.length == 2) { result = { 'top-left': vals[0], 'top-right': vals[1], 'bottom-right': vals[0], 'bottom-left': vals[1] }; } else if (vals.length == 3) { result = { 'top-left': vals[0], 'top-right': vals[1], 'bottom-right': vals[2], 'bottom-left': vals[1] }; } else if (vals.length == 4) { result = { 'top-left': vals[0], 'top-right': vals[1], 'bottom-right': vals[2], 'bottom-left': vals[3] }; } results.push(result); }) let result = {}; results.forEach(current => { ['top-left', 'top-right', 'bottom-right', 'bottom-left'].forEach(key => { if (!result[key]) { result[key] = current[key]; } else { result[key] += (' ' + current[key]); } }); }) return result; }; /** * 获取background样式值 * * @param {string} value 背景样式值 * @return {Object} 背景样式表 */ let getBackgroundValues = value => { let result = {}; let vals = getValueSegs(value); let positionEnd = false; /* eslint-disable */ vals.forEach(val => { if (/#[0-9a-fA-F]*|rgba?\(.*?\)/g.test(val)) { result.color = val; } else if (/^url\(.*\)/g.test(val)) { result.image = val; } else if (/^(repeat|repeat-x|repeat-y|no-repeat)/g.test(val)) { result.repeat = val; } else if (/^(padding\-box|border\-box|content\-box)/g.test(val) && !result.origin) { result.origin = val; } else if (/^(padding\-box|border\-box|content\-box)/g.test(val)) { result.clip = val; } else if (/^(top|center|bottom|left|right|[0-9.]+(px|%))/g.test(val) && !positionEnd) { result.position = !result.position ? val : result.position + ' ' + val; } else if (/\//g.test(val)) { positionEnd = true; } else if (/^(top|center|bottom|left|right|[0-9.]+(px|%))/g.test(val)) { result.size = !result.size ? val : result.size + ' ' + val; } else if (/^(scroll|fixed)/g.test(val)) { result.attachment = val; } }); /* eslint-disable */ return result; }; let parse = function (style) { return style .split(';') .filter(declaration => !!declaration.trim()) .map(declaration => { let {key, value} = utils.getStyleKeyValue(declaration); return { property: key, value }; }) .map(declaration => convert(declaration) .map(declaration => declaration.property + ': ' + declaration.value) .join(';')) .join(';'); }; let convert = function (declaration) { let declarations = []; let {property, value} = declaration; value = value.replace(/(\d*\.?\d+)cpx/gi, function(m, $1, $2) { return $1 + "px"; }) switch (property) { case 'margin': declarations = getDeclarations('margin-${direction}', value, property); break; case 'padding': declarations = getDeclarations('padding-${direction}', value, property); break; case 'background': declarations = getDeclarations('background-${backgroundAttribute}', value, property); break; case 'border': case 'border-top': case 'border-right': case 'border-bottom': case 'border-left': declarations = getDeclarations(property + '-${borderAttribute}', value, property); break; case 'border-style': declarations = getDeclarations('border-${direction}-style', value, property); break; case 'border-width': declarations = getDeclarations('border-${direction}-width', value); break; case 'border-color': declarations = getDeclarations('border-${direction}-color', value); break; case 'border-radius': declarations = getDeclarations('border-${angle}-radius', value, property); break; case 'flex-flow': declarations = getDeclarations('flex-${flexAttribute}', value, property); break; default: declarations = [{ type: 'declaration', property: property, value: getValueSegs(value).join(' ') }]; break; } return declarations; } module.exports = { convert: convert, parse: parse }; ================================================ FILE: packages/chameleon-css-loader/utils.js ================================================ const _ = {}; module.exports = _; // 将字符串中的 单引号变成 双引号; _.singlequot2doublequot = function (value) { return value.replace(/['']/g, '"'); } // 用于将css样式值中的重复样式去掉 _.uniqueStyle = function(content) { const uniqueStyleKeyValue = {}; const splitStyleKeyValue = content.split(';').filter(item => !!item.trim()); splitStyleKeyValue.forEach((declaration) => { let {key, value} = _.getStyleKeyValue(declaration); if (!key || !value) { throw new Error('please check if the style that you write is correct') } uniqueStyleKeyValue[key] = value; }); let result = []; Object.keys(uniqueStyleKeyValue).forEach(key => { result.push(`${key}:${uniqueStyleKeyValue[key]}`) }) return result.join(';'); } // 用于删除css样式的注释; /*width:100px;*/ ==> '' _.disappearCssComment = function(content) { let commentReg = /\/\*[\s\S]*?\*\//g; return content.replace(commentReg, function (match) { return ''; }) } _.getStyleKeyValue = function(declaration) { let colonIndex = declaration.indexOf(':'); let key = declaration.slice(0, colonIndex).trim(); let value = declaration.slice(colonIndex + 1).trim(); return { key, value } } ================================================ FILE: packages/chameleon-dev-proxy/.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/chameleon-dev-proxy/index.js ================================================ 'use strict'; require('babel-polyfill'); var tlsUtils = require('node-mitmproxy/lib/tls/tlsUtils'); var qrcode = require('qrcode-terminal'); var http = require('http'); var config = require('node-mitmproxy/lib/common/config'); var colors = require('colors'); var createRequestHandler = require('node-mitmproxy/lib/mitmproxy/createRequestHandler'); var createConnectHandler = require('node-mitmproxy/lib/mitmproxy/createConnectHandler'); var createFakeServerCenter = require('node-mitmproxy/lib/mitmproxy/createFakeServerCenter'); var createUpgradeHandler = require('node-mitmproxy/lib/mitmproxy/createUpgradeHandler'); module.exports = { createProxy: function createProxy(_ref) { var _ref$port = _ref.port; var port = _ref$port === undefined ? config.defaultPort : _ref$port; var caCertPath = _ref.caCertPath; var caKeyPath = _ref.caKeyPath; var sslConnectInterceptor = _ref.sslConnectInterceptor; var requestInterceptor = _ref.requestInterceptor; var responseInterceptor = _ref.responseInterceptor; var _ref$getCertSocketTim = _ref.getCertSocketTimeout; var getCertSocketTimeout = _ref$getCertSocketTim === undefined ? 1 * 1000 : _ref$getCertSocketTim; var _ref$middlewares = _ref.middlewares; var middlewares = _ref$middlewares === undefined ? [] : _ref$middlewares; var externalProxy = _ref.externalProxy; // Don't reject unauthorized process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; //if (!caCertPath && !caKeyPath) { var rs = this.createCA(caCertPath); caCertPath = rs.caCertPath; caKeyPath = rs.caKeyPath; if (rs.create) { console.log(colors.cyan('CA Cert saved in: ' + caCertPath)); console.log(colors.cyan('CA private key saved in: ' + caKeyPath)); } //} port = ~~port; var requestHandler = createRequestHandler(requestInterceptor, responseInterceptor, middlewares, externalProxy); var upgradeHandler = createUpgradeHandler(); var fakeServersCenter = createFakeServerCenter({ caCertPath: caCertPath, caKeyPath: caKeyPath, requestHandler: requestHandler, upgradeHandler: upgradeHandler, getCertSocketTimeout: getCertSocketTimeout }); var connectHandler = createConnectHandler(sslConnectInterceptor, fakeServersCenter); var server = new http.Server(); server.listen(port, function () { let crlUrl = _ref.devServer + config.caCertFileName; console.log(colors.green('代理端口: ' + port)); console.log(colors.green(`证书地址:${crlUrl}`)); console.log(colors.green(`扫一扫安装证书:`)); qrcode.generate(`${crlUrl}`, {small: true}); server.on('error', function (e) { console.error(colors.red(e)); }); server.on('request', function (req, res) { var ssl = false; requestHandler(req, res, ssl); }); // tunneling for https server.on('connect', function (req, cltSocket, head) { connectHandler(req, cltSocket, head); }); // TODO: handler WebSocket server.on('upgrade', function (req, socket, head) { var ssl = false; upgradeHandler(req, socket, head, ssl); }); }); }, createCA: function createCA() { var caBasePath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : config.getDefaultCABasePath(); return tlsUtils.initCA(caBasePath); } }; ================================================ FILE: packages/chameleon-dev-proxy/package.json ================================================ { "name": "chameleon-dev-proxy", "version": "1.0.8", "description": "cml开发环境代理服务模块", "main": "index.js", "author": "Chameleon-Team", "license": "Apache", "mail": "ChameleonCore@didiglobal.com", "dependencies": { "babel-polyfill": "^6.26.0", "node-mitmproxy": "^3.1.0", "qrcode-terminal": "^0.12.0" }, "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-errors-webpack-plugin/.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/chameleon-errors-webpack-plugin/LICENSE ================================================ MIT License Copyright (c) 2016 Geoffroy Warin 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/chameleon-errors-webpack-plugin/index.js ================================================ const FriendlyErrorsWebpackPlugin = require('./src/friendly-errors-plugin'); module.exports = FriendlyErrorsWebpackPlugin; ================================================ FILE: packages/chameleon-errors-webpack-plugin/package.json ================================================ { "version": "1.0.8", "name": "chameleon-errors-webpack-plugin", "main": "index.js", "author": "Chameleon-Team", "license": "Apache", "description": "fork from friendly-errors-webpack-plugin", "mail": "ChameleonCore@didiglobal.com", "dependencies": { "chalk": "^1.1.3", "error-stack-parser": "^2.0.0", "string-width": "^2.0.0" }, "devDependencies": { "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-loader": "^6.3.0", "babel-plugin-transform-async-to-generator": "^6.22.0", "babel-preset-react": "^6.23.0", "eslint": "^3.16.1", "eslint-loader": "^1.6.1", "expect": "^1.20.2", "jest": "^18.1.0", "memory-fs": "^0.4.1", "webpack": "^2.2.1" }, "files": [ "src", "index.js" ], "keywords": [ "friendly", "errors", "webpack", "plugin" ], "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/core/extractWebpackError.js ================================================ 'use strict'; const ErrorStackParser = require('error-stack-parser'); const RequestShortener = require("webpack/lib/RequestShortener"); // TODO: allow the location to be customized in options const requestShortener = new RequestShortener(process.cwd()); /* This logic is mostly duplicated from webpack/lib/Stats.js#toJson() See: https://github.com/webpack/webpack/blob/2f618e733aab4755deb42e9d8e859609005607c0/lib/Stats.js#L89 */ function extractError (e) { return { message: e.message, file: getFile(e), origin: getOrigin(e), name: e.name, severity: 0, webpackError: e, originalStack: getOriginalErrorStack(e) }; } function getOriginalErrorStack(e) { while (e.error != null) { e = e.error; } if (e.stack) { return ErrorStackParser.parse(e); } return []; } function getFile (e) { if (e.file) { return e.file; } else if (e.module && e.module.readableIdentifier && typeof e.module.readableIdentifier === "function") { return e.module.readableIdentifier(requestShortener); } } function getOrigin (e) { let origin = ''; if (e.dependencies && e.origin) { origin += '\n @ ' + e.origin.readableIdentifier(requestShortener); e.dependencies.forEach(function (dep) { if (!dep.loc) return; if (typeof dep.loc === "string") return; if (!dep.loc.start) return; if (!dep.loc.end) return; origin += ' ' + dep.loc.start.line + ':' + dep.loc.start.column + '-' + (dep.loc.start.line !== dep.loc.end.line ? dep.loc.end.line + ':' : '') + dep.loc.end.column; }); var current = e.origin; while (current.issuer && typeof current.issuer.readableIdentifier === 'function') { current = current.issuer; origin += '\n @ ' + current.readableIdentifier(requestShortener); } } return origin; } module.exports = extractError; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/core/formatErrors.js ================================================ 'use strict'; /** * Applies formatters to all AnnotatedErrors. * * A formatter has the following signature: FormattedError => Array. * It takes a formatted error produced by a transformer and returns a list * of log statements to print. * */ function formatErrors(errors, formatters, errorType) { const format = (formatter) => formatter(errors, errorType) || []; const flatten = (accum, curr) => accum.concat(curr); return formatters.map(format).reduce(flatten, []) } module.exports = formatErrors; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/core/transformErrors.js ================================================ 'use strict'; const extractError = require('./extractWebpackError'); /** * Applies all transformers to all errors and returns "annotated" * errors. * * Each transformer should have the following signature WebpackError => AnnotatedError * * A WebpackError has the following fields: * - message * - file * - origin * - name * - severity * - webpackError (original error) * * An AnnotatedError should be an extension (Object.assign) of the WebpackError * and add whatever information is convenient for formatting. * In particular, they should have a 'priority' field. * * The plugin will only display errors having maximum priority at the same time. * * If they don't have a 'type' field, the will be handled by the default formatter. */ function processErrors (errors, transformers) { const transform = (error, transformer) => transformer(error); const applyTransformations = (error) => transformers.reduce(transform, error); return errors.map(extractError).map(applyTransformations); } module.exports = processErrors; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/formatters/defaultError.js ================================================ 'use strict'; const concat = require('../utils').concat; const formatTitle = require('../utils/colors').formatTitle; function displayError(severity, error) { const baseError = formatTitle(severity, severity); return concat( `${baseError} ${removeLoaders(error.file)}`, '', error.message, (error.origin ? error.origin : undefined), '', error.infos ); } function removeLoaders(file) { if (!file) { return ""; } const split = file.split('!'); const filePath = split[split.length - 1]; return `in ${filePath}`; } function isDefaultError(error) { return !error.type; } /** * Format errors without a type */ function format(errors, type) { return errors .filter(isDefaultError) .reduce((accum, error) => ( accum.concat(displayError(type, error)) ), []); } module.exports = format; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/formatters/eslintError.js ================================================ 'use strict'; const concat = require('../utils').concat; const chalk = require('chalk'); const infos = [ 'You may use special comments to disable some warnings.', 'Use ' + chalk.yellow('// eslint-disable-next-line') + ' to ignore the next line.', 'Use ' + chalk.yellow('/* eslint-disable */') + ' to ignore all warnings in a file.' ]; function displayError(error) { return [error.message, ''] } function format(errors, type) { const lintErrors = errors.filter(e => e.type === 'lint-error'); if (lintErrors.length > 0) { const flatten = (accum, curr) => accum.concat(curr); return concat( lintErrors .map(error => displayError(error)) .reduce(flatten, []), infos ) } return []; } module.exports = format; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/formatters/moduleNotFound.js ================================================ 'use strict'; const concat = require('../utils').concat; function isRelative (module) { return module.startsWith('./') || module.startsWith('../'); } function formatFileList (files) { const length = files.length; if (!length) return ''; return ` in ${files[0]}${files[1] ? `, ${files[1]}` : ''}${length > 2 ? ` and ${length - 2} other${length === 3 ? '' : 's'}` : ''}`; } function formatGroup (group) { const files = group.errors.map(e => e.file).filter(Boolean); return `* ${group.module}${formatFileList(files)}`; } function forgetToInstall (missingDependencies) { const moduleNames = missingDependencies.map(missingDependency => missingDependency.module); if (missingDependencies.length === 1) { return `To install it, you can run: npm install --save ${moduleNames.join(' ')}`; } return `To install them, you can run: npm install --save ${moduleNames.join(' ')}`; } function dependenciesNotFound (dependencies) { if (dependencies.length === 0) return; return concat( dependencies.length === 1 ? 'This dependency was not found:' : 'These dependencies were not found:', '', dependencies.map(formatGroup), '', forgetToInstall(dependencies) ); } function relativeModulesNotFound (modules) { if (modules.length === 0) return; return concat( modules.length === 1 ? 'This relative module was not found:' : 'These relative modules were not found:', '', modules.map(formatGroup) ); } function groupModules (errors) { const missingModule = new Map(); errors.forEach((error) => { if (!missingModule.has(error.module)) { missingModule.set(error.module, []) } missingModule.get(error.module).push(error); }); return Array.from(missingModule.keys()).map(module => ({ module: module, relative: isRelative(module), errors: missingModule.get(module), })); } function formatErrors (errors) { if (errors.length === 0) { return []; } const groups = groupModules(errors); const dependencies = groups.filter(group => !group.relative); const relativeModules = groups.filter(group => group.relative); return concat( dependenciesNotFound(dependencies), dependencies.length && relativeModules.length ? ['', ''] : null, relativeModulesNotFound(relativeModules) ); } function format (errors) { return formatErrors(errors.filter((e) => ( e.type === 'module-not-found' ))); } module.exports = format; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/friendly-errors-plugin.js ================================================ 'use strict'; const path = require('path'); const chalk = require('chalk'); const os = require('os'); const transformErrors = require('./core/transformErrors'); const formatErrors = require('./core/formatErrors'); const output = require('./output'); const utils = require('./utils'); const concat = utils.concat; const uniqueBy = utils.uniqueBy; const defaultTransformers = [ require('./transformers/babelSyntax'), require('./transformers/moduleNotFound'), require('./transformers/esLintError'), ]; const defaultFormatters = [ require('./formatters/moduleNotFound'), require('./formatters/eslintError'), require('./formatters/defaultError'), ]; class FriendlyErrorsWebpackPlugin { constructor(options) { options = options || {}; this.compilationSuccessInfo = options.compilationSuccessInfo || {}; this.onErrors = options.onErrors; this.shouldClearConsole = options.clearConsole == null ? true : Boolean(options.clearConsole); this.formatters = concat(defaultFormatters, options.additionalFormatters); this.transformers = concat(defaultTransformers, options.additionalTransformers); this.cmlType = options.cmlType || ''; this.showWarning = options.showWarning || false; } apply(compiler) { const doneFn = stats => { this.clearConsole(); const hasErrors = stats.hasErrors(); let hasWarnings = stats.hasWarnings(); if (!this.showWarning) { hasWarnings = false; } if (!hasErrors && !hasWarnings) { this.displaySuccess(stats); return; } if (hasErrors) { this.displayErrors(extractErrorsFromStats(stats, 'errors'), 'error'); return; } if (hasWarnings) { this.displayErrors(extractErrorsFromStats(stats, 'warnings'), 'warning'); } }; const invalidFn = () => { this.clearConsole(); cml.log.notice(this.cmlType + ' Compiling...') }; let first = false; const beforeCompile = (compilation, callback) => { if (!first) { cml.log.notice(this.cmlType + ' Compiling...') first = true; } return callback(); } if (compiler.hooks) { const plugin = { name: 'FriendlyErrorsWebpackPlugin' }; compiler.hooks.done.tap(plugin, doneFn); compiler.hooks.invalid.tap(plugin, invalidFn); compiler.hooks.beforeCompile.tap(plugin, beforeCompile); } else { compiler.plugin('before-compile', beforeCompile); compiler.plugin('done', doneFn); compiler.plugin('invalid', invalidFn); } } clearConsole() { if (this.shouldClearConsole) { output.clearConsole(); } } displaySuccess(stats) { const time = getCompileTime(stats); cml.log.notice(this.cmlType + ' Compiled successfully in ' + time + 'ms') if (this.compilationSuccessInfo.messages) { this.compilationSuccessInfo.messages.forEach(message => output.info(message)); } if (this.compilationSuccessInfo.notes) { output.log(); this.compilationSuccessInfo.notes.forEach(note => output.note(note)); } } displayErrors(errors, severity) { const processedErrors = transformErrors(errors, this.transformers); const topErrors = getMaxSeverityErrors(processedErrors); const nbErrors = topErrors.length; const subtitle = severity === 'error' ? `Failed to compile with ${nbErrors} ${severity}s` : `Compiled with ${nbErrors} ${severity}s`; output.title(severity, severity.toUpperCase(), subtitle); if (this.onErrors) { this.onErrors(severity, topErrors); } formatErrors(topErrors, this.formatters, severity) .forEach(chunk => output.log(chunk)); } } function extractErrorsFromStats(stats, type) { if (isMultiStats(stats)) { const errors = stats.stats .reduce((errors, stats) => errors.concat(extractErrorsFromStats(stats, type)), []); // Dedupe to avoid showing the same error many times when multiple // compilers depend on the same module. return uniqueBy(errors, error => error.message); } return stats.compilation[type]; } function getCompileTime(stats) { if (isMultiStats(stats)) { // Webpack multi compilations run in parallel so using the longest duration. // https://webpack.github.io/docs/configuration.html#multiple-configurations return stats.stats .reduce((time, stats) => Math.max(time, getCompileTime(stats)), 0); } return stats.endTime - stats.startTime; } function isMultiStats(stats) { return stats.stats; } function getMaxSeverityErrors(errors) { const maxSeverity = getMaxInt(errors, 'severity'); return errors.filter(e => e.severity === maxSeverity); } function getMaxInt(collection, propertyName) { return collection.reduce((res, curr) => { return curr[propertyName] > res ? curr[propertyName] : res; }, 0) } module.exports = FriendlyErrorsWebpackPlugin; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/output.js ================================================ 'use strict'; const colors = require('./utils/colors'); const chalk = require('chalk'); const stringWidth = require('string-width'); const readline = require('readline'); class Debugger { constructor () { this.enabled = true; this.capturing = false; this.capturedMessages = []; } enable () { this.enabled = true; } capture () { this.enabled = true; this.capturing = true; } endCapture () { this.enabled = false; this.capturing = false; this.capturedMessages = []; } log () { if (this.enabled) { this.captureConsole(Array.from(arguments), console.log); } } info (message) { if (this.enabled) { const titleFormatted = colors.formatTitle('info', 'I'); this.log(titleFormatted, message); } } note (message) { if (this.enabled) { const titleFormatted = colors.formatTitle('note', 'N'); this.log(titleFormatted, message); } } title (severity, title, subtitle) { if (this.enabled) { const date = new Date(); const dateString = chalk.grey(date.toLocaleTimeString()); const titleFormatted = colors.formatTitle(severity, title); const subTitleFormatted = colors.formatText(severity, subtitle); const message = `${titleFormatted} ${subTitleFormatted}` // In test environment we don't include timestamp if(process.env.NODE_ENV === 'test') { this.log(message); this.log(); return; } // Make timestamp appear at the end of the line let logSpace = process.stdout.columns - stringWidth(message) - stringWidth(dateString) if (logSpace <= 0) { logSpace = 10 } this.log(`${message}${' '.repeat(logSpace)}${dateString}`); this.log(); } } clearConsole () { if (!this.capturing && this.enabled && process.stdout.isTTY) { // Fill screen with blank lines. Then move to 0 (beginning of visible part) and clear it const blank = '\n'.repeat(process.stdout.rows) console.log(blank) readline.cursorTo(process.stdout, 0, 0) readline.clearScreenDown(process.stdout) } } captureLogs (fun) { try { this.capture(); fun.call(); return this.capturedMessages; } catch (e) { throw e; } finally { this.endCapture(); } } captureConsole (args, method) { if (this.capturing) { this.capturedMessages.push(chalk.stripColor(args.join(' ')).trim()); } else { method.apply(console, args); } } } function capitalizeFirstLetter (string) { return string.charAt(0).toUpperCase() + string.slice(1); } module.exports = new Debugger(); ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/transformers/babelSyntax.js ================================================ 'use strict'; /** * This will be removed in next versions as it is not handled in the babel-loader * See: https://github.com/geowarin/friendly-errors-webpack-plugin/issues/2 */ function cleanStackTrace(message) { return message .replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, ''); // at ... ...:x:y } function cleanMessage(message) { return message // match until the last semicolon followed by a space // this should match // linux => "(SyntaxError: )Unexpected token (5:11)" // windows => "(SyntaxError: C:/projects/index.js: )Unexpected token (5:11)" .replace(/^Module build failed.*:\s/, 'Syntax Error: '); } function isBabelSyntaxError(e) { return e.name === 'ModuleBuildError' && e.message.indexOf('SyntaxError') >= 0; } function transform(error) { if (isBabelSyntaxError(error)) { return Object.assign({}, error, { message: cleanStackTrace(cleanMessage(error.message) + '\n'), severity: 1000, name: 'Syntax Error', }); } return error; } module.exports = transform; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/transformers/esLintError.js ================================================ 'use strict'; function isEslintError (e) { return e.originalStack .some(stackframe => stackframe.fileName && stackframe.fileName.indexOf('eslint-loader') > 0); } function transform(error) { if (isEslintError(error)) { return Object.assign({}, error, { name: 'Lint error', type: 'lint-error', }); } return error; } module.exports = transform; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/transformers/moduleNotFound.js ================================================ 'use strict'; const TYPE = 'module-not-found'; function isModuleNotFoundError (e) { const webpackError = e.webpackError || {}; return webpackError.dependencies && webpackError.dependencies.length > 0 && e.name === 'ModuleNotFoundError' && e.message.indexOf('Module not found') === 0; } function transform(error) { const webpackError = error.webpackError; if (isModuleNotFoundError(error)) { const module = webpackError.dependencies[0].request; return Object.assign({}, error, { message: `Module not found ${module}`, type: TYPE, severity: 900, module, name: 'Module not found' }); } return error; } module.exports = transform; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/utils/colors.js ================================================ 'use strict'; const chalk = require('chalk'); function formatTitle(severity, message) { return chalk[bgColor(severity)].black('', message, ''); } function formatText(severity, message) { return chalk[textColor(severity)](message); } function bgColor(severity) { const color = textColor(severity); return 'bg'+ capitalizeFirstLetter(color) } function textColor(serverity) { switch (serverity.toLowerCase()) { case 'success': return 'green'; case 'info': return 'blue'; case 'note': return 'white'; case 'warning': return 'yellow'; case 'error': return 'red'; default: return 'red'; } } function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } module.exports = { bgColor: bgColor, textColor: textColor, formatTitle: formatTitle, formatText: formatText }; ================================================ FILE: packages/chameleon-errors-webpack-plugin/src/utils/index.js ================================================ 'use strict'; /** * Concat and flattens non-null values. * Ex: concat(1, undefined, 2, [3, 4]) = [1, 2, 3, 4] */ function concat() { const args = Array.from(arguments).filter(e => e != null); const baseArray = Array.isArray(args[0]) ? args[0] : [args[0]]; return Array.prototype.concat.apply(baseArray, args.slice(1)); } /** * Dedupes array based on criterion returned from iteratee function. * Ex: uniqueBy( * [{ id: 1 }, { id: 1 }, { id: 2 }], * val => val.id * ) = [{ id: 1 }, { id: 2 }] */ function uniqueBy(arr, fun) { const seen = {}; return arr.filter(el => { const e = fun(el); return !(e in seen) && (seen[e] = 1); }) } module.exports = { concat: concat, uniqueBy: uniqueBy }; ================================================ FILE: packages/chameleon-linter/.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/chameleon-linter/.gitignore ================================================ .DS_Store .vscode .nyc_output ================================================ FILE: packages/chameleon-linter/bin/cli.js ================================================ #!/usr/bin/env node // --inspect-brk const program = require('commander'); const packageJson = require('../package.json'); const main = require('../index'); process.on('unhandledRejection', error => { // Will print "unhandledRejection err is not defined" console.log('unhandledRejection', error.message); }); program .version(packageJson.version) .parse(process.argv); let currentWorkspace = process.cwd(); global.workspace = currentWorkspace; process.on('unhandledRejection', error => { console.log(error); }); main(currentWorkspace, true); ================================================ FILE: packages/chameleon-linter/checkers/index.js ================================================ module.exports = { // json校验 json: require('./json'), // 脚本校验 script: require('./script'), // 样式校验 style: require('./style'), // 模板校验 Template: require('./template') }; ================================================ FILE: packages/chameleon-linter/checkers/json.js ================================================ const config = require('../config'); const cmlUtils = require('chameleon-tool-utils'); /** * 查找token * * @param {Object} ast 语法树 * @param {Array} tokens token列表 * @param {string} template 模板 * @return {Object} 查找结果 */ let findTokens = (ast, tokens, template) => { let result = {}; if (ast) { ast.program.body[0].expression.right.properties.forEach(property => { if (property.key.value == tokens[0]) { if (tokens.length == 1) { result = { line: property.key.loc.start.line - 1, column: property.key.loc.start.column, token: tokens.join('.'), msg: template.replace(/\{\{token\}\}/, tokens.join(',')) }; } else if (tokens.length === 2) { property.value.properties.forEach(property => { if (property.key.value == tokens[1]) { result = { line: property.key.loc.start.line - 1, column: property.key.loc.start.column, token: tokens.join('.'), msg: template.replace(/\{\{token\}\}/, tokens.join('.')) }; } }); } else { property.value.properties.forEach(property => { if (property.key.value == tokens[1]) { if (property.value.properties) { property.value.properties.forEach(property => { if (property.key.value == tokens[2]) { result = { line: property.value.loc.start.line - 1, column: property.value.loc.start.column, token: tokens.join('.'), msg: template }; } }); } } }); } } }); } return result; } let checkComponentName = (name) => { let keys = [ 'a', 'div', 'image', 'indicator', 'input', 'list', 'cell', 'recycle-list', 'loading', 'refresh', 'scroller', 'slider', 'switch', 'text', 'textarea', 'video', 'waterfall', 'web', 'richtext' ]; if (keys.indexOf(name) > -1) { return false; } return true; }; module.exports = (result) => { if (result.json && result.json.obj) { let json = result.json.obj; let platforms = config.getPlatforms(); if (!result.json.platform || result.json.platform == 'weex') { let usingComponents = (json.base || {}).usingComponents || {}; for (let key in usingComponents) { if (!checkComponentName(key)) { if (result.json.ast) { result.json.messages.push(findTokens(result.json.ast, ['base', 'usingComponents', key], 'component [' + key + '] is conflicted with weex buildin component, please rename the component!')); } } } let platformUsingComponents = ((json[result.json.platform] || {}).usingComponents) || {}; for (let key in platformUsingComponents) { if (!checkComponentName(key)) { if (result.json.ast) { result.json.messages.push(findTokens(result.json.ast, ['weex', 'usingComponents', key], 'component [' + key + '] is conflicted with weex buildin component, please rename the component')); } } } } // 分平台 if (result.json.platform) { platforms.forEach((item) => { if (json[item] && item != result.json.platform) { if (result.json.ast) { result.json.messages.push(findTokens(result.json.ast, [item], 'Useless fields: {{token}}')); } } }); } // 不分平台 else { platforms.forEach((item) => { if (json[item] && json[item].usingComponents) { if (result.json.ast) { result.json.messages.push(findTokens(result.json.ast, [item, 'usingComponents'], 'Useless fields: {{token}}')); } } }); } ['base'].concat(platforms).forEach(item => { if (json[item] && json[item].usingComponents) { for (let key in json[item].usingComponents) { let filePath = json[item].usingComponents[key]; filePath = filePath.split('?')[0]; let currentWorkspace = config.getCurrentWorkspace(); let componentInfo = cmlUtils.lintHandleComponentUrl(currentWorkspace, result.json.file, filePath); if (!componentInfo.filePath && componentInfo.refUrl) { if (componentInfo.refUrl.indexOf('plugin://') != 0 && result.json.ast) { result.json.messages.push(findTokens(result.json.ast, [item, 'usingComponents', key], 'component: [' + filePath + '] is not found')); } } } } }); } }; ================================================ FILE: packages/chameleon-linter/checkers/script.js ================================================ const traverse = require('@babel/traverse')['default']; const utils = require('../utils'); /** * 获取接口定义 * * @param {Object} ast ast * @return {Object} 分析结果 */ const getInterfaces = (ast) => { let result = { name: '', properties: {} }; ast.program.body.forEach(function (node) { if (node.type == 'InterfaceDeclaration') { let interfaceName = node.id.name; result.name = interfaceName; result.loc = { line: node.id.loc.start.line, column: node.id.loc.start.column }; node.body.properties.map((property) => { result.properties[property.key.name] = { type: property.value.type.replace(/TypeAnnotation/g, ''), line: property.key.loc.start.line, column: property.key.loc.start.column }; }); } }); return result; }; /** * 获取类定义 * @param {Object} ast ast * @param {Object} isComp a flag indentify whether the ast is component or an interface portion * @return {Object} 类定义 */ const getClass = (ast, isComp) => { return isComp ? getCompClassDef(ast) : getInterfacePortionClassDef(ast); }; function getCompClassDef(ast) { let classes = []; let clazz = { interfaces: [], properties: [], events: [], methods: [] }; traverse(ast, { ClassDeclaration(path) { // 接口 if (path.node['implements']) { path.node['implements'].forEach(implament => { clazz.interfaces.push(implament.id.name); }); } path.node.body.body.forEach(define => { if (define.key.name == 'props') { define.value.properties.forEach(property => { clazz.properties.push(property.key.name); }); } else if (define.key.name == 'methods') { define.value.properties.filter(property => { return property.type === 'ObjectMethod'; }).forEach(property => { clazz.methods.push(property.key.name); }); } }); classes.push(clazz); }, 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 event = null; let nameArg = parentNode.get('arguments')[0]; if (nameArg.isStringLiteral()) { event = { event: nameArg.node.value, line: nameArg.node.loc.start.line, column: nameArg.node.loc.start.column }; } 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') { event = { event: possibleInit.value, line: nameArg.node.loc.start.line, column: nameArg.node.loc.start.column }; } } if (event) { clazz.methods.push(event.event); clazz.events.push(event); } } } } } }); return classes; } function getInterfacePortionClassDef(ast) { let classes = []; traverse(ast, { enter(path) { if (path.node.type == 'ClassDeclaration') { let clazz = { interfaces: [], properties: [], events: [], methods: [] }; // 接口 if (path.node['implements']) { path.node['implements'].forEach(implament => { clazz.interfaces.push(implament.id.name); }); } path.node.body.body.forEach(define => { if (define.type == 'ClassProperty') { clazz.properties.push(define.key.name); } else if (define.type === 'ClassMethod') { define.key.name && (clazz.methods.push(define.key.name)); } }); classes.push(clazz); } } }); return classes; } /** * 校验接口与脚本 * * @param {Object} interfaceAst 接口ast * @return {Array} 数组 */ const checkScript = async (result) => { let validPlatforms = Object.keys(result) .filter(platform => { return platform && (!~['json', 'template', 'style', 'script'].indexOf(platform)); }) .filter(platform => { return platform && (platform != 'interface'); }); // add a script type for multi-file components. result['interface'] && validPlatforms.concat('script').forEach(platform => { let script; let isComp = (platform === 'script'); if (result[platform] && result[platform].ast) { script = result[platform]; } if (result['interface'] && result['interface'].ast && script && script.ast) { const interfaceDefine = getInterfaces(result['interface'].ast); const classDefines = getClass(script.ast, isComp); classDefines.forEach(clazz => { let define = null; clazz.interfaces.forEach(interfaceName => { define = interfaceDefine.name === interfaceName ? interfaceDefine.properties : null; if (!define) { result['interface'].messages.push({ msg: `The implement class name: "${interfaceName}" used in file: "${utils.toSrcPath(script.file)}" doesn\'t match the name defined in it\'s interface file: "${utils.toSrcPath(result['interface'].file)}"` }); return; } for (let key of Object.keys(define)) { if ((define[key] && define[key].type == 'Generic') && clazz.properties.indexOf(key) == -1) { result['interface'].messages.push({ line: define[key].line, column: define[key].column, token: key, msg: `interface property "${key}" is not defined for platform ${script.platform} in file "${utils.toSrcPath(script.file)}"` }); } else if ((define[key] && define[key].type == 'Function' && clazz.methods.indexOf(key) === -1)) { result['interface'].messages.push({ line: define[key].line, column: define[key].column, token: key, msg: `interface method "${key}" is not defined for platform ${script.platform} in file "${utils.toSrcPath(script.file)}"` }); } } }); define && clazz.events.forEach(event => { if (!define[event.event] || (define[event.event] && (define[event.event].type != 'Function'))) { script.messages.push({ line: event.line, column: event.column, token: event.event, msg: 'event "' + event.event + '" is not defined in interface file "' + utils.toSrcPath(result['interface'].file) + '"' }); } }); }); } }); }; module.exports = checkScript; ================================================ FILE: packages/chameleon-linter/checkers/style.js ================================================ const config = require('../config'); function getCmlType(mediaParams = '') { let types = []; if (mediaParams) { let typeStr = /\((.*)\)/.exec(mediaParams); if (typeStr && typeStr[1]) { types = typeStr[1].split(',') .filter(type => !!type.trim()) .map(type => { return type.trim().toLowerCase(); }); } } return types; } function detectFloatProp(rule, result) { rule.walkDecls('float', (decl) => { result.style.messages.push({ line: decl.source.start.line, column: decl.source.start.column, token: decl.prop, msg: `Weex does not support style property: "${decl.prop}", therefor you should not use "float" under a media rule with weex parameter or use "float" in a template file with platform type set to weex` }); }); } function isMediaNode(node) { return node && node.type === 'atrule' && node.name === 'media'; } const RULER_MAP = { 'important': { rule: function (decl, platform) { return config.neexLintWeex() && (!platform || platform == 'weex') && decl.important === true; }, msg: function (decl) { return `The CSS attribute "${decl.prop}" does not support "!important"` } }, 'percentages': { rule: function (decl, platform) { return config.neexLintWeex() && (!platform || platform == 'weex') && /%\s*$/g.test(decl.value); }, msg: function (decl, platform) { return `The CSS attribute "${decl.prop}" does not support percentages`; } }, 'lineHeight': { rule: function (decl, platform) { return config.neexLintWeex() && (!platform || platform == 'weex') && decl.prop == 'line-height' && /^\d+$/.test(decl.value); }, msg: function (decl) { return `The CSS attribute "${decl.prop}" does not support the number type unit`; } }, 'cpx': { rule: function (decl, platform) { return config.getRuleOption('cpx-support') && !platform && /\d+\s*(rpx|px)\s*$/g.test(decl.value); }, msg: function (decl) { return `The CSS attribute "${decl.prop}" must use \'cpx\' as it\'s unit`; } }, 'display': { rule: function (decl, platform) { return config.neexLintWeex() && (!platform || platform == 'weex') && decl.prop == 'display' && (decl.value == 'none' || decl.value == 'inline-block'); }, msg: function (decl) { return 'The CSS attribute "display" does not support "' + decl.value + '" as it\'s value'; } } // 'flex': { // rule: function (decl, platform) { // if (config.neexLintWeex() && (!platform || platform === 'weex') && decl.prop == 'display' && decl.value == 'flex') { // let flag = true; // decl.parent.nodes.forEach(node => { // if (node.prop == 'flex-direction') { // flag = false; // } // }); // return flag; // } // }, // msg: function (decl) { // return 'When the CSS attribute "display" value is "flex", you need to add the attribute "flex-direction" at the same time'; // } // } }; const SELECTOR_MAP = { 'pseudo': { rule: function (selector, platform) { let flag = false; if (config.neexLintWeex() && (!platform || platform == 'weex')) { selector.replace(/\:([^\s]+)/g, (match, pseudo) => { if (['active', 'focus', 'disabled', 'enabled'].indexOf(pseudo) == -1) { flag = true; } }); } return flag; }, msg: function (selector) { return 'The CSS selector "' + selector + '" only supports pseudo-classes of "active, focus, disabled, enabled"'; } } }; module.exports = (result) => { if (result.style && result.style.ast) { let platform = result.style.platform; let root = result.style.ast; root.walk((node) => { (node.selectors || []).forEach(selector => { Object.keys(SELECTOR_MAP).forEach(key => { if (SELECTOR_MAP[key].rule(selector, platform)) { result.style.messages.push({ line: node.source.start.line, column: node.source.start.column, token: node.value, msg: SELECTOR_MAP[key].msg(selector) }); } }); }); }); root.walkDecls((decl) => { Object.keys(RULER_MAP).forEach(key => { let stylePolymorphic = false; let media = decl.parent && decl.parent.parent; if (media && media.name == 'media') { if (new RegExp(/cml\-type\s*\(\s*[\S]+\s*\)/g).test(media.params)) { stylePolymorphic = true; } } if (!stylePolymorphic && RULER_MAP[key].rule(decl, platform)) { result.style.messages.push({ line: decl.source.start.line, column: decl.source.start.column, token: decl.value, msg: RULER_MAP[key].msg(decl) }); } }); }); // polymorphic components forbid float property. if (config.neexLintWeex() && platform === 'weex') { detectFloatProp(root, result); } // single file components. if (config.neexLintWeex() && (platform === undefined || platform === 'cml')) { root.walkRules((rule) => { if (!isMediaNode(rule.parent) || ~getCmlType(rule.parent.params).indexOf('weex')) { detectFloatProp(rule, result); } }); } } }; ================================================ FILE: packages/chameleon-linter/checkers/template/index.js ================================================ const jsAstParser = require('./lib/js-ast-parser'); const templateAstParser = require('./lib/template-ast-parser'); const jsonAstParser = require('./lib/json-ast-parser'); const commonEvents = require('../../config/common-events.json'); class TemplateChecker { /** * constructor * @param {Object} trees * { * templateAst: {}, * scriptAst: {}, * jsonAst: {} * } */ constructor(filePath = '', lintedResult = {}) { this._filePath = filePath; this._platform = lintedResult.template.platform; this._templateAst = lintedResult.template.ast; this._scriptAst = lintedResult.script.ast; this._jsonAst = lintedResult.json.obj; this._parsedTemplateResults = ''; this._parsedScriptResults = ''; this._usingComponents = ''; this.parseAllParts(); } parseAllParts() { this._usingComponents = jsonAstParser.getUsingComponents(this._jsonAst, this._filePath); this._parsedScriptResults = jsAstParser.getParseResults(this._scriptAst); this._parsedTemplateResults = templateAstParser.getParseResults(this._templateAst, { usingComponents: Object.keys(this._usingComponents), platform: this._platform }); } checkCustomizedComponents() { let issues = []; let usingComponents = this._usingComponents; let customizedComponets = this._parsedTemplateResults.customizedComponents; customizedComponets.forEach((component) => { Object.entries(component).forEach((compInfo) => { let compName = compInfo[0]; if (usingComponents[compName] && usingComponents[compName].isCml) { let { props, events } = component[compName]; let { props: usingProps, events: usingEvents } = usingComponents[compName]; usingProps = usingProps .map((prop) => prop.name) .join('|'); usingEvents = usingEvents .map((event) => event.name) .concat(commonEvents.events) .join('|'); usingProps = `|${usingProps}|`; usingEvents = `|${usingEvents}|`; props.filter((prop) => usingProps.indexOf('|' + prop.name + '|') === -1).forEach((prop) => { issues.push({ line: prop.pos[0], column: prop.pos[1], token: prop.rawName, msg: `The property "${prop.rawName}" is not a property of component "${compName}" which path is: ${usingComponents[compName].path}` }); }); events.filter((event) => usingEvents.indexOf('|' + event.name + '|') === -1).forEach((event) => { issues.push({ line: event.pos[0], column: event.pos[1], token: event.name, msg: `The event "${event.name}" is not defined in component "${compName}" which path is: ${usingComponents[compName].path}` }); }); } }); }); return issues; } checkTemplateAndScript() { let jsAstResults = this._parsedScriptResults; let templateAstResults = this._parsedTemplateResults; let issues = []; templateAstResults.methods.forEach(method => { if (jsAstResults.methods.indexOf(method.name) === -1) { issues.push({ line: method.pos[0], column: method.pos[1], token: method.name, msg: `method: "${method.name}" is not defined.` }); } }); templateAstResults.vars.forEach((varItem) => { if (jsAstResults.vars.indexOf(varItem.name) === -1) { issues.push({ line: varItem.pos[0], column: varItem.pos[1], token: varItem.name, msg: `variable: "${varItem.name}" is not defined.` }); } }); return issues; } check() { return [...this.checkCustomizedComponents(), ...this.checkTemplateAndScript()]; } } module.exports = TemplateChecker; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/js-ast-parser.js ================================================ const CmlJsAstTreeParser = require('cml-js-parser'); const config = require('../../../config'); function getParseResults(astTree) { let parser = new CmlJsAstTreeParser({astTree}, config.getParserConfig().script); return parser.getParseResults(); } module.exports = { getParseResults } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/json-ast-parser.js ================================================ const Parser = require('cml-component-parser'); const cmlUtils = require('chameleon-tool-utils'); const utils = require('../../../utils'); const config = require('../../../config'); function getUsingComponents(jsonAst, filePath = '') { let results = {}; let currentWorkspace = config.getCurrentWorkspace(); let interfaceParser = new Parser(null, config.getParserConfig().script, currentWorkspace); if (jsonAst && jsonAst.base && jsonAst.base.usingComponents) { Object .entries(jsonAst.base.usingComponents) .map((componentInfoPair) => { return { name: utils.toDash(componentInfoPair[0]), path: componentInfoPair[1] }; }) .filter((infoPair) => { return !(infoPair.path.indexOf('plugin://') === 0); }) .forEach((infoPair) => { // filePath: is a full absolute path of the target template file // inforPair.path: is the original path of base: {usingComponents: 'path/to/referenced component'} let interfaceInfo = cmlUtils.findInterfaceFile(currentWorkspace, filePath, infoPair.path); let componentInfo = cmlUtils.lintHandleComponentUrl(currentWorkspace, filePath, infoPair.path); let useDefine = interfaceInfo; if (!useDefine.filePath) { if (componentInfo && componentInfo.filePath) { useDefine = componentInfo; } } results[infoPair.name] = { path: useDefine.filePath }; results[infoPair.name] = { ...results[infoPair.name], ...interfaceParser.resetPath(results[infoPair.name].path).getParseResults() } results[infoPair.name].isCml = !!interfaceInfo.filePath || componentInfo.isCml; results[infoPair.name].isOrigin = !results[infoPair.name].isCml; // if we can not get filePath then we delete this component, because the component is not well coded. if (!useDefine.filePath) { delete results[infoPair.name] } }); } return results; } module.exports = { getUsingComponents }; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/classes/customized-node.js ================================================ /** * A class represents a customized component. */ class CustomizedNode { constructor(tag, lang = 'cml') { this.lang = lang; this.name = tag.name; this.attribs = {...tag.attribs}; } get attrs() { let attrs = this.attribs; return Object.keys(attrs).map((attrName) => { return { name: attrName, namePos: attrs[attrName].nameLineCol, value: attrs[attrName].value, valuePos: attrs[attrName].valueLineCol }; }); } } module.exports = CustomizedNode; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/classes/rule-loader.js ================================================ class RuleLoader { constructor(rules) { this.rulesMap = {}; if (rules) { this.addSubscribers(rules); } } addSubscribers(rules) { rules.forEach(function(rule) { rule.subscribers = []; rules.forEach((onRule) => { if (onRule.on && ~onRule.on.indexOf(rule.name)) { rule.subscribers.push(onRule); } }); this.rulesMap[rule.name] = rule; }.bind(this)); } getRuleByName(ruleName) { return this.rulesMap[ruleName]; } } module.exports = RuleLoader; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/classes/suspicious-node.js ================================================ /** * A class reprents nodes that are waiting for passing to getting variable rules. */ class SuspiciousNode { constructor({name = '', rawValue = '', valuePos = [], lang = 'cml', scope = [], isTextNode = false}) { this.name = name; this.rawValue = rawValue; this.valuePos = valuePos; this.lang = lang; /** * loopScopes property is holding the for loop varibables. For example for v-for * directive in vue.js, say v-for='(item , index) in array', then we will append an * array with value: * [item, index] * into loopScopes property. */ this.loopScopes = scope; this.isTextNode = isTextNode; this.isAttr = !this.isTextNode; } } module.exports = SuspiciousNode; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/index.js ================================================ const astTreeTraversal = require('./lib/ast-tree-traversal'); const suspiciousNodeDispatcher = require('./lib/suspicious-node-dispatcher'); const customizedNodeDispatcher = require('./lib/customized-node-dispatcher'); const options = require('./options'); /** * Get all possible properties' value that may be a variable or a method name. * @param {Object} templateAst * @param {Object} usingComponents An array contains customized components' names * @returns {Object} {vars: [], methods: [], customizedComponents: {comp: {props: [], events: []}}} */ function getParseResults(templateAst, {usingComponents = [], platform = 'cml'}) { let rootTag = templateAst[0]; let templateLang = 'cml'; if (rootTag.attribs.lang && ~options.getOption('langs').indexOf(rootTag.attribs.lang.value = rootTag.attribs.lang.value.trim())) { templateLang = rootTag.attribs.lang.value; } // ast tree traversal to get attribute nodes, textNodes and customized components. let {components: customizedComponents, nodes} = astTreeTraversal.travel({ root: rootTag, lang: templateLang, platform, usingComponents }); let varandMethods = suspiciousNodeDispatcher.getResults(nodes); let propandEvents = customizedNodeDispatcher.getResults(customizedComponents); return { vars: varandMethods.filter((node) => node.variable), methods: varandMethods.filter((node) => node.method), customizedComponents: propandEvents }; } module.exports = { getParseResults } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/lib/ast-tree-traversal.js ================================================ const SuspiciousNode = require('../classes/suspicious-node'); const CustomizedNode = require('../classes/customized-node'); const Tools = require('../tools'); let _usingComponents = []; let _platform = 'cml'; let _lang = 'cml'; let _customizedComponents = []; let _allSuspiciousNodes = []; function _clean() { _customizedComponents = []; _allSuspiciousNodes = []; } function saveToCustomizedComponents(tag) { (_usingComponents.indexOf(tag.name) > -1) && _customizedComponents.push(new CustomizedNode(tag, Tools.isOriginComponent(tag) ? _platform : _lang)); } function saveToTextNodes(node, scope) { if (Tools.isSuspiciousTextnode({lang: _lang, text: node.data})) { _allSuspiciousNodes.push(new SuspiciousNode({ name: '', rawValue: node.data, valuePos: node.lineCol, lang: _lang, scope: scope, isTextNode: true })); } } function saveToPossibleAttrs(attrs) { _allSuspiciousNodes = _allSuspiciousNodes.concat(attrs.map(attr => { return new SuspiciousNode({ name: attr.name, rawValue: attr.value, valuePos: attr.valueLineCol, lang: attr.lang, scope: attr.scope, isTextNode: false }); })); } function _travel(tag) { if (tag.type === 'tag') { saveToCustomizedComponents(tag); !tag._cmlScopes && (tag._cmlScopes = []); if (Tools.hasForLoopDirective({tag, lang: _lang})) { tag._cmlScopes.push(Tools.getScopeFromTag({tag, lang: _lang})); } saveToPossibleAttrs(Object.keys(tag.attribs).map((item) => { // becasue attribute's valueLineCol is actually calculated based on rawValue, so we need readjusting the column. tag.attribs[item].rawValue && (tag.attribs[item].valueLineCol[1] = tag.attribs[item].valueLineCol[1] + tag.attribs[item].rawValue.indexOf(tag.attribs[item].value)); return { name: item, lang: Tools.isOriginComponent(tag) ? _platform : _lang, scope: [...tag._cmlScopes], value: tag.attribs[item].value, valueLineCol: tag.attribs[item].valueLineCol }; })); } if (tag.children && tag.children.length > 0) { tag.children.filter((item) => { if (item.type === 'text') { saveToTextNodes(item, [...tag._cmlScopes]); } return item.type === 'tag'; }).forEach((childTag) => { childTag._cmlScopes = [...tag._cmlScopes]; _travel(childTag); }); } } function travel({root, lang = 'cml', platform = 'cml', usingComponents = []}) { _usingComponents = usingComponents; _platform = platform; _lang = lang; _clean(); // make a deep copy of root ast, so we won't effect oiriginal structure during our process. _travel({...root}); return { components: _customizedComponents, nodes: _allSuspiciousNodes }; } module.exports = { travel }; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/lib/customized-node-dispatcher.js ================================================ const rules = require('../rules/component'); const options = require('../options'); const RuleLoader = require('../classes/rule-loader'); module.exports.getResults = function(nodes) { let allOptions = options.getAllOptions(); let results = []; let loader = new RuleLoader(rules); nodes && nodes.forEach((node) => { if (~options.getOption('langs').indexOf(node.lang)) { let matchRule = loader.getRuleByName('attr'); if (matchRule) { results = results.concat(matchRule.run(node, allOptions)); } } }); // attrResults && attrResults.forEach((compNode) => { // Object.assign(results, compNode); // }); return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/lib/suspicious-node-dispatcher.js ================================================ const rules = require('../rules/template'); const options = require('../options'); const RuleLoader = require('../classes/rule-loader'); module.exports.getResults = function(nodes) { let allOptions = options.getAllOptions(); let results = []; let loader = new RuleLoader(rules); nodes && nodes.forEach((node) => { if (~options.getOption('langs').indexOf(node.lang)) { let matchRule = loader.getRuleByName(node.lang); if (matchRule) { results = results.concat(matchRule.run(node, allOptions)); } } }); return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/options.js ================================================ const _options = { langs: ['cml', 'vue'], cmlSystemVarNames: ['$event', 'Math', 'Date'], vueSystemVarNames: ['$event', 'Math', 'Date'] } module.exports.getOption = function(optName) { return _options[optName]; } module.exports.getAllOptions = function() { return {..._options}; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/attr.js ================================================ const Tools = require('../../tools'); module.exports = { name: 'attr' } module.exports.run = function(node, opts) { let subs = this.subscribers; let result = {}; let attrResults = []; subs && subs.forEach((sub) => { if (Tools.isRuleMatch(node, sub)) { node.attrs.forEach((attr) => { attrResults = attrResults.concat(sub.run(attr, opts)); }); } }); result[node.name] = {props: [], events: []}; attrResults.forEach((attNode) => { if (attNode.prop) { result[node.name].props.push(attNode); } else { result[node.name].events.push(attNode); } }); return [result]; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/cml-method-node.js ================================================ const methodRegex = /c-bind:(\w+)/; module.exports = { name: 'cml-method-node', on: 'cml', filter: { key: 'name', run: function(value) { return methodRegex.test(value); } } } module.exports.run = function(attr, opts) { let methodName = methodRegex.exec(attr.name)[1]; return { name: methodName, pos: [attr.namePos[0], attr.namePos[1] + attr.name.indexOf(methodName)], prop: false, event: true }; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/cml-prop-node.js ================================================ const Tools = require('../../tools'); module.exports = { name: 'cml-prop-node', on: 'cml', filter: { key: 'name', run: function(value) { return !~['id', 'class', 'style', 'ref'].indexOf(value) && !/data-(.+)/.test(value) && !/c-(.+)/.test(value); } } } module.exports.run = function(attr, opts) { return { rawName: attr.name, name: Tools.dashtoCamelcase(attr.name), pos: attr.namePos, prop: true, event: false } } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/cml.js ================================================ const Tools = require('../../tools'); module.exports = { name: 'cml', on: ['attr'], filter: { key: 'lang', run: function(value) { return value === 'cml' } } } module.exports.run = function(attr, opts) { let subs = this.subscribers; let attrResults = []; subs && subs.forEach((sub) => { if (Tools.isRuleMatch(attr, sub)) { attrResults = attrResults.concat(sub.run(attr, opts)); } }); return attrResults; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/index.js ================================================ const bulk = require('bulk-require'); const ruleExports = bulk(__dirname, '!(index).js'); module.exports = Object.values(ruleExports); ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/vue-method-node.js ================================================ const methodRegex = /^(?:v-on:|v-once:|@)(\w+)/; module.exports = { name: 'vue-method-node', on: 'vue', filter: { key: 'name', run: function(value) { return methodRegex.test(value); } } } module.exports.run = function(attr, opts) { let methodName = methodRegex.exec(attr.name)[1]; return [{ name: methodName, pos: [attr.namePos[0], attr.namePos[1] + attr.name.indexOf(methodName)], prop: false, event: true }]; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/vue-prop-node.js ================================================ const Tools = require('../../tools'); const propRegex = /^v-bind|^:/; module.exports = { name: 'vue-prop-node', on: 'vue', filter: { key: 'name', run: function(value) { return propRegex.test(value); } } } module.exports.run = function(attr, opts) { let preArg = /^(?:\:|v-bind:)([\w-]*)/.exec(attr.name); let props = []; if (preArg) { preArg = preArg[1]; props.push({ rawName: preArg, name: Tools.dashtoCamelcase(preArg), pos: [attr.namePos[0], attr.namePos[1] + attr.name.indexOf(preArg)], prop: true, event: false }); } else { props = Tools.getPropsFromObjectExpression(attr.value); props.map((prop) => { prop.rawName = prop.name; prop.name = Tools.dashtoCamelcase(prop.name); prop.pos = [attr.valuePos[0] + prop.pos[0] - 1, attr.valuePos[1] + prop.pos[1]]; prop.prop = true; prop.event = false; return prop; }); } return props; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/component/vue.js ================================================ const Tools = require('../../tools'); module.exports = { name: 'vue', on: ['attr'], filter: { key: 'lang', run: function(value) { return value === 'vue' } } } module.exports.run = function(attr, opts) { let subs = this.subscribers; let attrResults = []; subs && subs.forEach((sub) => { if (Tools.isRuleMatch(attr, sub)) { attrResults = attrResults.concat(sub.run(attr, opts)); } }); return attrResults; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/cml.js ================================================ const Tools = require('../../tools'); const options = require('../../options'); module.exports = { name: 'cml' } module.exports.run = function(node, opts) { let subs = this.subscribers; let results = []; subs && subs.forEach((sub) => { if (Tools.isRuleMatch(node, sub)) { results = results.concat(sub.run(node, opts)); } }); results = results.filter((item) => { return options.getOption('cmlSystemVarNames').indexOf(item.name) === -1; }); return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/index.js ================================================ const bulk = require('bulk-require'); const ruleExports = bulk(__dirname, '!(index).js'); module.exports = Object.values(ruleExports); ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/method-node.js ================================================ const Tools = require('../../tools'); const normalMethodRegex = /\(.*\)/; module.exports = { name: 'mehtod-node', on: ['cml', 'vue'], filter: { key: 'name', run: function(value) { return /^c-bind|^v-on|^v-once|^@/.test(value); } } } /** * * @return {Array} An array contains info about variables and methods. * {name: 'varibale', method: false, variable: true, pos: [line, column]} */ module.exports.run = function(node, opts) { let results = []; let varsandMethods = []; let loopScopes = Tools.flatArray(node.loopScopes); // if current method only contains a method name without any parentheses, then we make one with argument literal string "true", // which won't be recognized as a variable and can avoid a systax error when the method name is match with some keywords of Javascript. if (!normalMethodRegex.test(node.rawValue)) { node.rawValue += '("true")'; } varsandMethods = Tools.parseSingleExpression(node.rawValue); results = varsandMethods.map((item) => { item.pos = [item.pos[0] + node.valuePos[0] - 1, item.pos[1] + node.valuePos[1]]; return item; }); if (loopScopes.length) { results = results.filter(resNode => !~loopScopes.indexOf(resNode.name)); } return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/mustache-node.js ================================================ const Tools = require('../../tools'); const mustacheRegex = /{{(.*?)}}/g; module.exports = { name: 'mustache-node', on: ['cml', 'vue'], filter: { key: 'rawValue', run: function(value) { return mustacheRegex.test(value); } } } /** * * @return {Array} An array contains info about variables and methods. * {name: 'varibale', method: false, variable: true, pos: [line, column]} */ module.exports.run = function(node, opts) { let results = []; let expressionResults = []; let loopScopes = Tools.flatArray(node.loopScopes); node.rawValue && node.rawValue.replace(mustacheRegex, (match, expressionStr, offset) => { expressionResults = expressionResults.concat(Tools.parseSingleExpression(expressionStr)); expressionResults.map((resNode) => { resNode.pos[1] = resNode.pos[1] + offset + match.indexOf(expressionStr); return resNode; }); }); // reset positons of variables and methods based on rawValue. results = expressionResults.map((resNode) => { resNode.pos = Tools.getVarOffsetPosFromText(node.rawValue, resNode.name, resNode.pos[1]); resNode.pos = [resNode.pos[0] + node.valuePos[0], resNode.pos[0] > 0 ? (resNode.pos[1] + 1) : (resNode.pos[1] + node.valuePos[1])]; return resNode; }); if (loopScopes.length) { results = results.filter(resNode => !~loopScopes.indexOf(resNode.name)); } return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/vue-directive-node.js ================================================ const Tools = require('../../tools'); module.exports = { name: 'vue-directive-node', on: ['vue'], filter: { key: 'name', run: function(value) { return ~['v-else-if', 'v-for', 'v-html', 'v-if', 'v-model', 'v-show', 'v-text'].indexOf(value) || /^:|^v-bind/.test(value); } } } module.exports.run = function(node, opts) { let results = []; let varsandMethods = []; let loopScopes = Tools.flatArray(node.loopScopes); varsandMethods = Tools.parseSingleExpression(node.rawValue); results = varsandMethods.map((item) => { item.pos = [item.pos[0] + node.valuePos[0] - 1, item.pos[1] + node.valuePos[1]]; return item; }); if (loopScopes.length) { results = results.filter(resNode => !~loopScopes.indexOf(resNode.name)); } return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/rules/template/vue.js ================================================ const Tools = require('../../tools'); const options = require('../../options'); module.exports = { name: 'vue' } module.exports.run = function(node, opts) { let subs = this.subscribers; let results = []; subs && subs.forEach((sub) => { if (Tools.isRuleMatch(node, sub)) { results = results.concat(sub.run(node, opts)); } }); results = results.filter((item) => { return options.getOption('vueSystemVarNames').indexOf(item.name) === -1; }); return results; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/dash-to-camelcase.js ================================================ module.exports.dashtoCamelcase = function(str = '') { return str && str.replace(/-(\w)/g, (all, letter) => letter.toUpperCase()); } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/flat-array.js ================================================ module.exports.flatArray = function(arr) { let tempArr = []; if (Array.isArray(arr)) { arr.forEach((item) => { if (Array.isArray(item)) { tempArr.push(...item); } else { tempArr.push(item); } }); } return tempArr; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/get-offset-pos-from-text.js ================================================ module.exports.getVarOffsetPosFromText = function (text = '', varName = '', offset = 0) { if (!text || !varName) {return [1, 0];} let preText = text.substr(0, text.indexOf(varName, offset)); let lines = preText.split('\n'); return [lines.length - 1, lines[lines.length - 1].length]; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/get-props-from-object-expression.js ================================================ const bablePaser = require('@babel/parser'); /** * @return {Array} [{name: 'propName', pos: [line, columm]}] */ module.exports.getPropsFromObjectExpression = function(expressionStr) { let props = []; try { let expression = bablePaser.parseExpression(expressionStr); if (expression.type === 'ObjectExpression') { expression.properties && expression.properties.forEach((prop) => { if (prop.key.type === 'Identifier') { props.push({ name: prop.key.name, pos: [prop.loc.start.line, prop.loc.start.column] }); } if (prop.key.type === 'StringLiteral') { props.push({ name: prop.key.value, pos: [prop.loc.start.line, prop.loc.start.column + prop.key.extra.raw.indexOf(prop.key.value)] }); } }); } } catch (err) { console.log(err); } return props; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/index.js ================================================ const bulk = require('bulk-require'); const toolExports = bulk(__dirname, '!(index).js'); let tools = {}; Object.values(toolExports).forEach(tool => { Object.assign(tools, tool); }); module.exports = tools; ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/is-origin-component.js ================================================ module.exports.isOriginComponent = function(tag) { return tag ? /^origin-\w+/.test(tag.name) : false; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/is-suspicious-textnode.js ================================================ const suspiciousRegexes = { cml: /{{(.*?)}}/g, vue: /{{(.*?)}}/g, wx: /{{(.*?)}}/g }; module.exports.isSuspiciousTextnode = function({lang = 'cml', text = ''}) { return suspiciousRegexes[lang].test(text); } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/loop-scope-handler.js ================================================ const bablePaser = require('@babel/parser'); const forLoopDirectives = { vue: { for: 'v-for' }, cml: { for: 'c-for', item: 'c-for-item', index: 'c-for-index' }, wx: { for: 'wx:for', item: 'wx:for-item', index: 'wx:for-index' } }; module.exports.hasForLoopDirective = function({tag = {}, lang = 'cml'}) { return tag.attribs ? ~Object.keys(tag.attribs).indexOf(forLoopDirectives[lang]['for']) : false; } /** * @return {Array} scope [itemName, indexName] */ module.exports.getScopeFromTag = function({tag = {}, lang = 'cml'}) { let scope = []; if (lang === 'vue') { let expressionNode = {}; try { expressionNode = bablePaser.parseExpression(tag.attribs[forLoopDirectives[lang]['for']].value); } catch (err) { console.trace(err); } // if (expressionNode.type === 'Identifier') { // scope.push(...['item', 'index']); // } if (expressionNode.type === 'BinaryExpression') { if (expressionNode.left.type === 'SequenceExpression') { expressionNode.left.expressions.forEach((identifierNode) => { if (identifierNode.type === 'Identifier') { scope.push(identifierNode.name); } }); } if (expressionNode.left.type === 'Identifier') { scope.push(expressionNode.left.name); } } } else if (lang === 'cml' || lang === 'wx') { if (tag.attribs[forLoopDirectives[lang].item]) { scope.push(tag.attribs[forLoopDirectives[lang].item].value); } else { scope.push('item'); } if (tag.attribs[forLoopDirectives[lang].index]) { scope.push(tag.attribs[forLoopDirectives[lang].index].value); } else { scope.push('index'); } } return scope; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/parse-single-expression.js ================================================ const parser = require('@babel/parser'); const traverse = require('@babel/traverse')['default']; const objectBlockStatement = /^\s*{(.*)}\s*$/; const fakePrefix = 'cml='; /** * @param {Object} loc * @param {Boolean} isFakeBlock */ function getStartPos(loc, isFakeBlock = false) { let pos = [0, 0]; // loc.start.column begins with 0 if (loc && loc.start) { if (isFakeBlock) { pos = [loc.start.line, loc.start.column - fakePrefix.length]; } else { pos = [loc.start.line, loc.start.column]; } } return pos; } function getVarFromIdentifier(node, isFakeBlock, method = false) { return { name: node.name, method: method, variable: !method, pos: getStartPos(node.loc, isFakeBlock) }; } module.exports.parseSingleExpression = function(expressinoStr = '') { let nodes = []; let ast = null; let isFakeBlock = false; /** * !!We are using babel parser to get an ast of current expression, which takes experssion as * an entire program. So when we pass something like {a:1,b:3} this will be considered as * a blockstatement and obviously it contains a syntax error. * So we wrap the expression we assingment expression in those particular cases. */ if (objectBlockStatement.test(expressinoStr)) { expressinoStr = fakePrefix + expressinoStr; isFakeBlock = true; } try { ast = parser.parse(expressinoStr); } catch (err) { console.trace(err); } ast && traverse(ast, { Identifier(path) { if (path.parent.type === 'ExpressionStatement') { nodes.push(getVarFromIdentifier(path.node, isFakeBlock)); } }, MemberExpression(path) { if (path.get('object').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.object, isFakeBlock)); } if (path.get('property').isIdentifier() && path.node.computed) { nodes.push(getVarFromIdentifier(path.node.property, isFakeBlock)); } }, LogicalExpression(path) { if (path.get('left').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.left, isFakeBlock)); } if (path.get('right').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.right, isFakeBlock)); } }, BinaryExpression(path) { if (path.get('left').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.left, isFakeBlock)); } if (path.get('right').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.right, isFakeBlock)); } }, LogicalExpression(path) { if (path.get('left').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.left, isFakeBlock)); } if (path.get('right').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.right, isFakeBlock)); } }, SequenceExpression(path) { path.get('expressions').forEach((ele) => { if (ele.isIdentifier()) { nodes.push(getVarFromIdentifier(ele.node, isFakeBlock)); } }); }, ConditionalExpression(path) { ['test', 'alternate', 'consequent'].forEach((key) => { if (path.get(key).isIdentifier()) { nodes.push(getVarFromIdentifier(path.node[key], isFakeBlock)); } }); }, CallExpression(path) { if (path.get('callee').isIdentifier()) { nodes.push(getVarFromIdentifier(path.node.callee, isFakeBlock, true)); } path.get('arguments').forEach((arg) => { if (arg.isIdentifier()) { nodes.push(getVarFromIdentifier(arg.node, isFakeBlock)); } }); }, ArrayExpression(path) { path.get('elements').forEach((ele) => { if (ele.isIdentifier()) { nodes.push(getVarFromIdentifier(ele.node, isFakeBlock)); } }); }, ObjectExpression(path) { path.get('properties').forEach((ele) => { if (ele.isIdentifier()) { nodes.push(getVarFromIdentifier(ele.node, isFakeBlock)); } }); } }); return nodes; } ================================================ FILE: packages/chameleon-linter/checkers/template/lib/template-ast-parser/tools/rule-filter-match.js ================================================ module.exports.isRuleMatch = function(node, rule) { return node && rule && rule.filter && rule.filter.run(node[rule.filter.key]); } ================================================ FILE: packages/chameleon-linter/classes/message.js ================================================ /** * A class represents an error message. */ class Message { constructor({ line = undefined, column = undefined, token = '', msg = '' }) { this.line = line; this.column = column; this.token = token; this.msg = msg || 'An unknown error occurred' } } module.exports = Message; ================================================ FILE: packages/chameleon-linter/config/.cmllintrc ================================================ { // 核心文件检查 'core-files-check': true, // cml文件校验 'cml-files-check': true, // 接口文件校验 'interface-files-check': true, 'cpx-support': true, 'platforms': ['weex', 'wx', 'web', 'baidu', 'alipay'] } ================================================ FILE: packages/chameleon-linter/config/built-in-components/index.js ================================================ const fs = require('fs'); const config = require('../../config'); const Parser = require('cml-component-parser'); const parser = new Parser(null, config.getParserConfig().script); let getCml = function () { let result = {}; let inDir = config.getCurrentWorkspace() + '/node_modules/chameleon-ui-builtin/components'; if (fs.existsSync(inDir)) { Parser.flatEntrance(inDir).forEach(filterFile => { let content = parser.resetPath(filterFile).getJsonResultsWithComponentName(); content && (result[content.name] = content.content); }); } return result; } module.exports = { getCml }; ================================================ FILE: packages/chameleon-linter/config/common-events.json ================================================ { "events": ["tap", "click", "touchstart", "touchmove", "touchend"] } ================================================ FILE: packages/chameleon-linter/config/fakeComps/index.js ================================================ const list = [{ name: 'app', allowAttrs: { vars: ['routerConfig'], methods: [], props: [{ name: 'routerConfig', required: false }], events: [] } }]; module.exports = list; ================================================ FILE: packages/chameleon-linter/config/globalVars.js ================================================ const globalVars = { WEEX: ['weex', 'global'], WX: ['wx', 'global'], BAIDU: ['swan', 'global'], ALIPAY: ['my', 'global'], QQ: ['qq', 'global'], TT: ['tt', 'global'], WEB: [ 'postMessage', 'blur', 'focus', 'close', 'frames', 'self', 'window', 'parent', 'opener', 'top', 'length', 'closed', 'location', 'document', 'origin', 'name', 'history', 'locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar', 'toolbar', 'status', 'frameElement', 'navigator', 'customElements', 'external', 'screen', 'innerWidth', 'innerHeight', 'scrollX', 'pageXOffset', 'scrollY', 'pageYOffset', 'screenX', 'screenY', 'outerWidth', 'outerHeight', 'devicePixelRatio', 'clientInformation', 'screenLeft', 'screenTop', 'defaultStatus', 'defaultstatus', 'styleMedia', 'onanimationend', 'onanimationiteration', 'onanimationstart', 'onsearch', 'ontransitionend', 'onwebkitanimationend', 'onwebkitanimationiteration', 'onwebkitanimationstart', 'onwebkittransitionend', 'isSecureContext', 'onabort', 'onblur', 'oncancel', 'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'onclose', 'oncontextmenu', 'oncuechange', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'ondurationchange', 'onemptied', 'onended', 'onerror', 'onfocus', 'oninput', 'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup', 'onload', 'onloadeddata', 'onloadedmetadata', 'onloadstart', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onpause', 'onplay', 'onplaying', 'onprogress', 'onratechange', 'onreset', 'onresize', 'onscroll', 'onseeked', 'onseeking', 'onselect', 'onstalled', 'onsubmit', 'onsuspend', 'ontimeupdate', 'ontoggle', 'onvolumechange', 'onwaiting', 'onwheel', 'onauxclick', 'ongotpointercapture', 'onlostpointercapture', 'onpointerdown', 'onpointermove', 'onpointerup', 'onpointercancel', 'onpointerover', 'onpointerout', 'onpointerenter', 'onpointerleave', 'onafterprint', 'onbeforeprint', 'onbeforeunload', 'onhashchange', 'onlanguagechange', 'onmessage', 'onmessageerror', 'onoffline', 'ononline', 'onpagehide', 'onpageshow', 'onpopstate', 'onrejectionhandled', 'onstorage', 'onunhandledrejection', 'onunload', 'performance', 'stop', 'open', 'alert', 'confirm', 'prompt', 'print', 'requestAnimationFrame', 'cancelAnimationFrame', 'requestIdleCallback', 'cancelIdleCallback', 'captureEvents', 'releaseEvents', 'getComputedStyle', 'matchMedia', 'moveTo', 'moveBy', 'resizeTo', 'resizeBy', 'getSelection', 'find', 'webkitRequestAnimationFrame', 'webkitCancelAnimationFrame', 'fetch', 'btoa', 'atob', 'createImageBitmap', 'scroll', 'scrollTo', 'scrollBy', 'onappinstalled', 'onbeforeinstallprompt', 'crypto', 'ondevicemotion', 'ondeviceorientation', 'ondeviceorientationabsolute', 'indexedDB', 'webkitStorageInfo', 'sessionStorage', 'localStorage', 'chrome', 'visualViewport', 'speechSynthesis', 'webkitRequestFileSystem', 'webkitResolveLocalFileSystemURL', 'openDatabase', 'applicationCache', 'caches', 'whichAnimationEvent', 'animationendEvent', 'infinity', 'SETTING', 'AppView', 'ExtensionOptions', 'ExtensionView', 'WebView', 'iconPath', '_app', '_ZOOM_', 'Feed', 'md5', '$', 'jQuery', 'Search', 'windmill', 'Lethargy', 'alertTimeOut', 'supportApps', 'lethargyX', 'lethargyY', 'iView', 'onModuleResLoaded', 'iEditDelete', 'infinityDrag', 'i', 'array', 'TEMPORARY', 'PERSISTENT', 'addEventListener', 'removeEventListener', 'dispatchEvent' ] }; module.exports = globalVars; ================================================ FILE: packages/chameleon-linter/config/index.js ================================================ let fs = require('fs'); let path = require('path'); let parserConfig = require('./parser-config'); let config = {}; let currentWorkspace; /** * 获取lint的配置 * * @param {string} workspace 工作空间 * @return {Object} 配置 */ let getLintConfig = (workspace) => { let config = {}; let cfgFilePath = path.resolve(workspace, '.cmllintrc'); let defaultCfgFilePath = path.resolve(__filename, '../.cmllintrc'); let defaultConfig = {}; let defaultCfgContent = fs.readFileSync(defaultCfgFilePath); defaultConfig = (new Function('return ' + defaultCfgContent))(); if (fs.existsSync(cfgFilePath)) { try { let cfgContent = fs.readFileSync(cfgFilePath); let userConfig = (new Function('return ' + cfgContent))(); config = Object.assign({}, defaultConfig, userConfig); } catch (e) { config = defaultConfig; } } else { config = defaultConfig; } return config; } /** * 初始化 * * @param {string} workspace 工作空间 */ let init = (workspace) => { currentWorkspace = workspace; config = getLintConfig(workspace); }; /** * 获取当前的工作空间 * * @return {string} 工作空间 */ let getCurrentWorkspace = () => { return currentWorkspace; }; /** * 获取规则数据 * * @param {string} ruleName 规则名称 * @return {Object} 规则内容 */ let getRuleOption = (ruleName) => { return config[ruleName]; }; let isChameleonProject = () => { let cmlCfgFilePath = path.resolve(currentWorkspace, './chameleon.config.js'); if (!fs.existsSync(cmlCfgFilePath)) { return false } return true; }; let getParserConfig = () => { return parserConfig; } let getPlatforms = () => { return config.platforms; }; let getCurrentProjectPlatforms = () => { let defaultPlatforms = getPlatforms(); if (global.cml) { return global.cml.config.get().platforms || defaultPlatforms; } return defaultPlatforms; }; let neexLintWeex = () => { let platforms = getCurrentProjectPlatforms(); if (platforms.indexOf('weex') > -1) { return true; } return false; } module.exports = { init, getCurrentWorkspace, getRuleOption, isChameleonProject, getParserConfig, getPlatforms, neexLintWeex } ================================================ FILE: packages/chameleon-linter/config/parser-config.js ================================================ module.exports = { script: { sourceType: 'module', allowImportExportEverywhere: true, // consistent with espree 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' ] }, style: { rules: { 'block-no-empty': null, 'selector-max-compound-selectors': 1, 'selector-type-no-unknown': [true, { 'ignore': ['custom-elements'], 'ignoreTypes': ['page'] }] } } }; ================================================ FILE: packages/chameleon-linter/config/tag-embed-rules.json ================================================ { "text": { "required": "", "excludes": "", "includes": ["text"] }, "scroller": { "required": "", "excludes": ["textarea","video"], "includes": "" }, "list": { "required": "", "excludes": ["textarea","video"], "includes": "" }, "video": { "required": "", "excludes": "", "includes": ["text"] } } ================================================ FILE: packages/chameleon-linter/config/white-list/cml-white-list.js ================================================ /** * tags: Technically, it only holds all availiable tags that cml has. */ module.exports = { attrs: ['c-if', 'c-else', 'c-else-if', 'c-for', 'c-for-index', 'c-for-item', 'c-model', 'c-text', 'c-show', 'c-bind', 'c-catch', 'c-key', 'c-animation'], tags: [ 'template', 'view', 'text', 'block', 'scroller', 'list', 'cell', 'image', 'switch', 'video', 'input', 'textarea', 'richtext', 'button', 'radio', 'checkbox', 'page', 'router-view', 'cover-view', 'slot', 'aside', 'col', 'container', 'foot', 'head', 'main', 'row', 'carousel', 'carousel-item', 'component' ] } ================================================ FILE: packages/chameleon-linter/config/white-list/index.js ================================================ const cmlWhiteList = require('./cml-white-list'); const vueWhiteList = require('./vue-white-list'); const webWhiteList = require('./web-white-list'); const weexWhiteList = require('./weex-white-list'); const wxWhiteList = require('./wx-white-list'); module.exports = { web: webWhiteList, vue: vueWhiteList, wx: wxWhiteList, weex: weexWhiteList, cml: cmlWhiteList } module.exports.getAllowedTags = function() { return this.cml.tags; } module.exports.getForbiddenAttrsByLang = function(lang = 'cml') { return (lang === 'cml') ? this.vue.attrs : this.cml.attrs; } ================================================ FILE: packages/chameleon-linter/config/white-list/vue-white-list.js ================================================ /** * tags: Technically, it only holds tags that are not a html tag. */ module.exports = { attrs: ['v-if', 'v-else', 'v-else-if', 'v-for', 'v-on', 'v-bind', 'v-html', 'v-show', 'v-model', 'v-pre', 'v-once', 'slot-scope'], tags: ['template', 'view', 'text', 'block', 'scroller', 'list', 'cell', 'image', 'switch', 'video', 'input', 'button', 'radio', 'checkbox', 'page', 'router-view', 'slot'] } ================================================ FILE: packages/chameleon-linter/config/white-list/web-white-list.js ================================================ module.exports = { attrs: ['accesskey', 'class', 'contenteditable', 'contextmenu', 'dir', 'draggable', 'dropzone', 'hidden', 'id', 'lang', 'spellcheck', 'style', 'tabindex', 'title', 'translate'], // "data-*" tags: ['a', 'abbr', 'acronym', 'address', 'applet', 'area', 'article', 'aside', 'audio', 'b', 'base', 'basefont', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details', 'dir', 'div', 'dfn', 'dialog', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'keygen', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike', 'strong', 'style', 'slot', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr'] } ================================================ FILE: packages/chameleon-linter/config/white-list/weex-white-list.js ================================================ module.exports = { attrs: ['v-if', 'v-else', 'v-else-if', 'v-for', 'v-on', 'v-bind', 'v-html', 'v-show', 'v-model', 'v-pre', 'v-once', 'slot-scope', 'is'], tags: ['a', 'div', 'image', 'indicator', 'input', 'list', 'cell', 'recycle-list', 'loading', 'refresh', 'scroller', 'slider', 'textarea', 'text', 'richtext', 'video', 'waterfall', 'web'] } ================================================ FILE: packages/chameleon-linter/config/white-list/wx-white-list.js ================================================ /** * tags: Technically, it only holds tags that are not a html tag. * attrs: Attributes that only existing in mini-app can be listed here. */ module.exports = { attrs: ['wx:if', 'wx:elif', 'wx:else', 'wx:for', 'wx:for-item', 'wx:for-index', 'wx:key', 'bindtap', 'catchtap'], tags: [ 'template', 'view', 'block', 'scroll-view', 'swiper', 'movable-view', 'movable-area', 'cover-view', 'cover-image', 'icon', 'text', 'rich-text', 'progress', 'lable', 'input', 'form', 'checkbox', 'picker', 'picker-view', 'radio', 'switch', 'slider', 'textarea', 'navigator', 'functional-page-navigator', 'camera', 'live-player', 'live-pusher', 'map', 'open-data', 'web-view', 'ad', 'official-account', 'slot' ] } ================================================ FILE: packages/chameleon-linter/docs/cml-file-specification.md ================================================ # cml文件规范 ``` ├── components │ ├── c-title │ │ ├── c-title.web.cml │ │ ├── c-title.weex.cml │ │ └── c-title.wx.cml ``` ================================================ FILE: packages/chameleon-linter/docs/components/buildin/button.md ================================================ # button --- |属性名|类型|说明| | ------ | ------ | ------ | |text|String| | |size|String| | |type|String| | |disabled|Boolean| | |btnStyle|String| | |textStyle|String| | |disabledStyle|String| | |c-bind:onclick|EventHandle|事件返回参数 eventDetail [类型: EventDetail]
其中 EventDetail 的定义为
 {type: String , disabled: Boolean}
| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/checkbox.md ================================================ # checkbox --- |属性名|类型|说明| | ------ | ------ | ------ | |checked|Boolean| | |label|String| | |disabled|Boolean| | |position|String| | |groupIndex|Number| | |c-bind:changeevent|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/input.md ================================================ # input --- |属性名|类型|说明| | ------ | ------ | ------ | |value|String| | |type|String| | |placeholder|String| | |disabled|Boolean| | |focus|Boolean| | |maxlength|Number| | |returnKeyType|String| | |placerHolderColor|String| | |inputStyle|String| | |c-bind:inputevent|EventHandle|事件返回参数 eventDetail [类型: inputEventDetail]
其中 inputEventDetail 的定义为
 {value: String}
| |c-bind:blurevent|EventHandle|事件未定义返回参数| |c-bind:focusevent|EventHandle|事件未定义返回参数| |c-bind:confirmevent|EventHandle|事件未定义返回参数| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/list.md ================================================ # list --- |属性名|类型|说明| | ------ | ------ | ------ | |bottomOffset|Number| | |c-bind:customscroll|EventHandle|事件返回参数 eventDetail [类型: scrollDetail]
其中 scrollDetail 的定义为
 {scrollHeight: Number , scrollLeft: Number , scrollTop: Number , scrollWidth: Number}
| |c-bind:scrolltobottom|EventHandle|事件返回参数 eventDetail [类型: bottomDetail]
其中 bottomDetail 的定义为
 {direction: String}
| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/page.md ================================================ # page --- |属性名|类型|说明| | ------ | ------ | ------ | |title|String| | |c-bind:back|EventHandle|事件未定义返回参数| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/radio.md ================================================ # radio --- |属性名|类型|说明| | ------ | ------ | ------ | |checked|Boolean| | |label|String| | |disabled|Boolean| | |position|String| | |groupIndex|Number| | |c-bind:changeevent|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/richtext.md ================================================ # richtext --- |属性名|类型|说明| | ------ | ------ | ------ | |richData|richData|其中 richData 的定义为
 {message: String , rich_message: richMessageArray}
其中 richMessageArray 的定义为
 [richConfig]
其中 richConfig 的定义为
 {color: String , font_size: Number , start: Number , end: Number}| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/scroller.md ================================================ # scroller --- |属性名|类型|说明| | ------ | ------ | ------ | |cstyle|String| | |bottomOffset|Number| | |scrollDirection|String| | |c-bind:customscroll|EventHandle|事件返回参数 eventDetail [类型: scrollEventDetail]
其中 scrollEventDetail 的定义为
 {deltaX: Number , deltaY: Number , scrollHeight: Number , scrollLeft: Number , scrollTop: Number , scrollWidth: Number}
| |c-bind:scrolltobottom|EventHandle|事件返回参数 eventDetail [类型: scrolltobottomEventDetail]
其中 scrolltobottomEventDetail 的定义为
 {direction: String}
| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/switch.md ================================================ # switch --- |属性名|类型|说明| | ------ | ------ | ------ | |checked|Boolean| | |disabled|Boolean| | |label|String| | |c-bind:changeevent|EventHandle|事件返回参数 eventDetail [类型: valueDetail]
其中 valueDetail 的定义为
 {value: Boolean}
| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/textarea.md ================================================ # textarea --- |属性名|类型|说明| | ------ | ------ | ------ | |value|String| | |type|String| | |placeholder|String| | |disabled|Boolean| | |focus|Boolean| | |maxlength|Number| | |returnKeyType|String| | |placerHolderColor|String| | |inputStyle|String| | |rows|Number| | |c-bind:inputevent|EventHandle|事件返回参数 eventDetail [类型: inputEventDetail]
其中 inputEventDetail 的定义为
 {value: String}
| |c-bind:blurevent|EventHandle|事件未定义返回参数| |c-bind:focusevent|EventHandle|事件未定义返回参数| |c-bind:confirmevent|EventHandle|事件未定义返回参数| ================================================ FILE: packages/chameleon-linter/docs/components/buildin/video.md ================================================ # video --- |属性名|类型|说明| | ------ | ------ | ------ | |controls|Boolean| | |autoplay|Boolean| | |loop|Boolean| | |src|String| | |c-bind:customstart|EventHandle|事件未定义返回参数| |c-bind:custompause|EventHandle|事件未定义返回参数| |c-bind:customfinish|EventHandle|事件未定义返回参数| |c-bind:customfail|EventHandle|事件未定义返回参数| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-actionsheet.md ================================================ # c-actionsheet --- |属性名|类型|说明| | ------ | ------ | ------ | |show|Boolean| | |title|String| | |cancelTxt|String| | |headerStyle|String| | |cancelStyle|String| | |contentStyle|String| | |activeStyle|String| | |data|Array| | |pickerStyle|Boolean| | |active|Number| | |c-bind:cancel|EventHandle|待填写| |c-bind:select|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-aside.md ================================================ # c-aside --- |属性名|类型|说明| | ------ | ------ | ------ | |asideStyle|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-checkbox-group.md ================================================ # c-checkbox-group --- |属性名|类型|说明| | ------ | ------ | ------ | |option|Array| | |position|String| | |horizontal|Boolean| | |c-bind:groupchange|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-col.md ================================================ # c-col --- |属性名|类型|说明| | ------ | ------ | ------ | |width|Number| | |height|Number| | |backgroundColor|String| | |margin|Number| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-container.md ================================================ # c-container --- |属性名|类型|说明| | ------ | ------ | ------ | |direction|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-dialog.md ================================================ # c-dialog --- |属性名|类型|说明| | ------ | ------ | ------ | |show|Boolean| | |mask|Boolean| | |title|String| | |content|String| | |type|String| | |showClose|Boolean| | |closeSrc|String| | |cancelText|String| | |confirmText|String| | |iconType|String| | |iconUrl|String| | |iconStyle|Object| | |c-bind:show|EventHandle|待填写| |c-bind:cancel|EventHandle|待填写| |c-bind:confirm|EventHandle|待填写| |c-bind:close|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-foot.md ================================================ # c-foot --- |属性名|类型|说明| | ------ | ------ | ------ | |footStyle|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-form-item.md ================================================ # c-form-item --- |属性名|类型|说明| | ------ | ------ | ------ | |label|String| | |justify|String| | |align|String| | |labelClass|String| | |labelStyle|Object| | |contentClass|String| | |contentStyle|Object| | |errorClass|String| | |errorStyle|Object| | |rules|Array| | |required|Boolean| | |feedback|String| | |validateStatus|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-form.md ================================================ # c-form --- |属性名|类型|说明| | ------ | ------ | ------ | |model|Object| | |labelWidth|String| | |labelPosition|String| | |rules|Array| | |inline|Boolean| | |submit|Function| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-head.md ================================================ # c-head --- |属性名|类型|说明| | ------ | ------ | ------ | |headStyle|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-main.md ================================================ # c-main --- |属性名|类型|说明| | ------ | ------ | ------ | |mainStyle|String| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-picker-item.md ================================================ # c-picker-item --- |属性名|类型|说明| | ------ | ------ | ------ | |data|ArrayType|其中 ArrayType 的定义为
 [String]| |defaultIndex|Number| | |height|Number| | |textAlign|String| | |c-bind:selectchange|EventHandle|事件返回参数 eventDetail [类型: EventDetail]
其中 EventDetail 的定义为
 {index: Number}
| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-picker-panel.md ================================================ # c-picker-panel --- |属性名|类型|说明| | ------ | ------ | ------ | |show|Boolean| | |height|Number| | |title|String| | |headerHeight|Number| | |cancelBtnStyle|String| | |confirmBtnStyle|String| | |c-bind:cancel|EventHandle|事件未定义返回参数| |c-bind:confirm|EventHandle|事件未定义返回参数| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-picker.md ================================================ # c-picker --- |属性名|类型|说明| | ------ | ------ | ------ | |show|Boolean| | |height|Number| | |headerHeight|Number| | |textAlign|String| | |dataScrollerHeight|Number| | |data|Array| | |defaultIndex|Number| | |cancelBtnStyle|String| | |confirmBtnStyle|String| | |c-bind:cancel|EventHandle|待填写| |c-bind:confirm|EventHandle|待填写| |c-bind:selectchange|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-popup.md ================================================ # c-popup --- |属性名|类型|说明| | ------ | ------ | ------ | |mask|Boolean| | |show|Boolean| | |center|Boolean| | |position|String| | |c-bind:close|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-radio-group.md ================================================ # c-radio-group --- |属性名|类型|说明| | ------ | ------ | ------ | |option|Array| | |position|String| | |horizontal|Boolean| | |c-bind:groupchange|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-refresh.md ================================================ # c-refresh --- |属性名|类型|说明| | ------ | ------ | ------ | |display|Boolean| | |direction|String| | |customUi|Boolean| | |c-bind:refreshevent|EventHandle|事件返回参数 eventDetail [类型: valueDetail]
其中 valueDetail 的定义为
 {value: Boolean}
| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-row.md ================================================ # c-row --- |属性名|类型|说明| | ------ | ------ | ------ | |justify|String| | |align|String| | |height|Number| | |wrap|Boolean| | |margin|Number| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-tab-pane-item.md ================================================ ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-tab-pane.md ================================================ # c-tab-pane --- |属性名|类型|说明| | ------ | ------ | ------ | |tabs|Array| | |activeLabel|Array| | ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-tabs-item.md ================================================ # c-tabs-item --- |属性名|类型|说明| | ------ | ------ | ------ | |tab|Object| | |inline|Boolean| | |activeLabel|String| | |activeLabelStyle|String| | |c-bind:tabclick|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-tabs.md ================================================ # c-tabs --- |属性名|类型|说明| | ------ | ------ | ------ | |tabs|Array| | |activeLabel|String| | |inline|Boolean| | |lineStyle|String| | |activeLabelStyle|String| | |c-bind:tabclick|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-tip.md ================================================ # c-tip --- |属性名|类型|说明| | ------ | ------ | ------ | |show|Boolean| | |direction|String| | |closeUrl|String| | |offsetLeft|Number| | |offsetTop|Number| | |offsetRight|Number| | |offsetBottom|Number| | |c-bind:close|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/components/chameleon-ui/c-toast.md ================================================ # c-toast --- |属性名|类型|说明| | ------ | ------ | ------ | |type|String| | |message|String| | |duration|Number| | |show|Boolean| | |mask|Boolean| | |needIcon|Boolean| | |c-bind:show|EventHandle|待填写| ================================================ FILE: packages/chameleon-linter/docs/directory-specification.md ================================================ # 目录规范 ``` ├── chameleon.config.js //项目的配置文件 ├── dist //打包产出目录 ├── mock //模拟数据目录 ├── node_modules //npm包依赖 ├── package.json └── src //项目源代码 ├── app //app入口 ├── components //组件文件夹 ├── pages //页面文件夹 ├── router.config.json //路由配置文件 └── store //全局状态管理 ``` ================================================ FILE: packages/chameleon-linter/docs/interface-specification.md ================================================ # 接口校验语法 > 接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)进行沟通。 ## 类型说明 **注意:建议定义类型的时候取值为 Number String Boolean Null Undefined(Void) Object Array Function Date RegExp** 目前chameleon接口定义支持简单类型和复合类型。 其中简单类型包括以下类型: - Number(number) - String(string) - Boolean(bool) - Undefined(void) - Null 复合类型包括以下类型: - Function - Object - Array - Date - RegExp - Promise ## 接口语法 接口的使用分两个过程: 1. 定义一个接口。 2. 定义实现接口的类。 ### 接口定义 #### 范式: interface [接口名称] { // 接口中的属性 [属性名称]: [类型], // 接口中的方法 [方法名称]([传入参数1名称]: [传入参数1类型], [传入参数2名称]: [传入参数2类型], ...): [返回类型] } #### 举例: // 一个名为interface1的接口 interface interface1 { // foo1: 传入分别为string和number的两个数据,返回值类型为string值 foo1(a: string, b: number): string; // foo2: 传入分别为string和Callback(上文定义)的两个数据,返回值类型为bool值 foo2(c: string, d: Callback): string; } ### 实现接口(定义类) #### 范式: class [类名称] implaments [接口名称] { // 实现接口中的属性 [属性名称]: [类型] // 实现接口中的方法 [方法名称]([传入参数1名称], [传入参数2名称], ...) { return [返回值]; } } #### 举例: // 实现一个名称为Clazz,实现上文定义的interface1接口 class Clazz implaments interface1 { // 实现interface1定义的foo1方法,输入值和输出值要满足定义 foo1(a, b) { return 'hello ' + a + ' : ' + (b + 1); } // 实现interface1定义的foo2方法,输入值和输出值要满足定义 foo2(c, d) { return 'balabala...'; } } ## 复合类型的定义范式 type [类型名称] = [类型定义] 不同的复合类型,类型定义也不相同,下面会对三种复合类型做详细说明。 ### Function类型定义 #### 范式: type [Function类型名称] = ([传入参数1名称]: [传入参数1类型], [传入参数2名称]: [传入参数2类型], ...) => [返回类型] #### 举例: // 定义一个传参分别为number,string,bool类型的三个参数,返回值为number的函数类型 type Callback = (a: number, b: string, c: bool) => number; ### Object类型定义 #### 范式: type [Object类型名称] = { [属性名称1]: [类型1], [属性名称2]: [类型2] } #### 举例: // 定义含有a,b,c三个属性的复合类型 type Scheme = { a: string, b: bool, c: number } ### Array类型定义 #### 范式: type [Array类型名称] = [ [类型1] ] #### 举例: // 定义名称为arrayType1的数组类型,数组元素为number类型 type arrayType1 = [ number ] ## 复合类型中的相互嵌套 Function、Object、Array三种复合类型可以互相嵌套: // 定义一个传参分别为number,string,bool类型的三个参数,返回值为number的函数类型 type Callback = (a: number, b: string, c: bool) => number; // 定义名称为arrayType1的数组类型,数组元素为number类型 type arrayType1 = [ number ] // 定义名称为Scheme的,含有Array类型和Function类型属性的Object类型 type Scheme = { a: arrayType1, b: Callback, } // 定义名称为Plan,含有Scheme类型和Callback的属性的Object类型 type Plan = { a: string, b: Scheme, c: Callback } // 定义名称为arrayType1类型,元素为Plan类型 type arrayType1 = [ Plan ] ### Promise 类型的定义 对于 async函数,由于该函数调用之后的返回值是 Promise对象,所以这样的函数的返回值要声明成 Promise; ```javascript interface EntryInterface { appEntry(): Promise; appEntry2() : Promise; } ``` 在 methods 中 ```javascript class Method implements EntryInterface { async appEntry(num) { } appEntry2(){ return new Promise((resolve,reject) => { setTimeout(resolve,2000); }) } } ``` ### Date 类型的定义 如果函数参数或者返回值是 Date 数据类型,那么可以按照下面的方式进行定义; ```javascript interface EntryInterface { handleDate(d:Date) : Date } ``` ```javascript class Method implements EntryInterface { handleDate(d){ return new Date(); } } ``` ## RegExp 类型的定义 如果函数参数或者返回值是 RegExp 数据类型,那么可以按照下面的方式进行定义; ```javascript interface EntryInterface { handleDate(d:RegExp) : RegExp } ``` ```javascript class Method implements EntryInterface { handleDate(r){ return new RegExp(); } } ``` ================================================ FILE: packages/chameleon-linter/docs/templates/template-functionalities.md ================================================ ## 模板校验功能点 该文档汇集模板校验支持所有检查点。 ### 模板语言校验 模板可以指定模板语言,指定方式为在 template 标签上指定 lang 属性, 其合法值为 "cml" 和 "vue"。 校验点: + template 可以忽略 lang 属性,此时其默认值为 cml + template lang 属性如果指定,则必须为 "cml" 或者 "vue" > 报错信息:'the tag template lang attribute: "<%= lang %>" is not valid'. ### 模板 template 标签校验 校验点:每个模板只能切必须有一对 template 根标签。 > 报错信息:"Each template can only have one group of template tags." ### 模板内 tags 校验 每个模板都有一个模板语言和一个平台类型,其中模板语言由 template 的 lang 属性指定,平台类型由模板文件的文件名解析出来。 对于多态组件平台类型可以直接从文件名解析出来, 比如 index.web.cml, index.weex.cml, index.wx.cml 对应的平台类型分别为 web, weex, wx。 对于单文件组件,由于其模板要跨三端,故模板中只能使用 chameleon 原生支持的内建标签。 校验点: + 单文件组件只能使用 chameleon 内建标签,使用非内建标签校验不通过。 - chameleon 内建标签有: ['template','view','text','block','scroller','list','cell','image','switch','video','input','button','radio','checkbox', 'page', 'router-view', 'slot'] + 多态组件可以使用 chameleon 内建标签加上各平台类型所支持的原生标签,使用其他标签验证不同过。 - web 平台原生支持标签: ["a","abbr","acronym","address","applet","area","article","aside","audio","b","base","basefont","bdi","bdo","big","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","command","datalist","dd","del","details","dir","div","dfn","dialog","dl","dt","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","i","iframe","img","input","ins","kbd","keygen","label","legend","li","link","map","mark","menu","menuitem","meta","meter","nav","noframes","noscript","object","ol","optgroup","option","output","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strike","strong","style","slot","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr"] - weex 平台原生支持标签: ['a','div','image','indicator','input','list','cell','recycle-list','loading','refresh','scroller','slider','textarea','text','richtext','video','waterfall','web'] - wx 平台原生支持标签: ['template','view','block','scroll-view','swiper','movable-view','movable-area','cover-view','cover-image','icon','text','rich-text','progress','lable','input','form','checkbox','picker','picker-view','radio','switch','slider','textarea','navigator','functional-page-navigator','camera','live-player','live-pusher','map','open-data','web-view','ad','official-account','slot'] > 报错信息:'tag: "<%= tag %>" is either not allowed in this template or not referenced as a component' ### 模板指令校验 每个模板可以使用的指令由模板语言和平台类型共同决定。 校验点: + 单文件模板只能使用模板语言对应的指令,使用模板语言指令之外的指令校验不通过 - lang='cml' 支持的指令:['c-if','c-else','c-else-if','c-for','c-for-index','c-for-item','c-model','c-text','c-show','c-bind','c-catch','c-key'] - lang='vue' 支持的指令:['v-if','v-else','v-else-if','v-for','v-on','v-bind','v-html','v-show','v-model','v-pre','v-once','slot-scope','is','@',':'] + 多态组件可以使用**模板语言指令**加**平台原生指令**,使用此外其他指令校验不通过 - web 平台支持指令:无模板指令 - weex 平台支持指令同 vue.js 框架: ['v-if','v-else','v-else-if','v-for','v-on','v-bind','v-html','v-show','v-model','v-pre','v-once','slot-scope','is','@',':'] - wx 平台支持指令:['wx:if','wx:elif','wx:else','wx:for','wx:for-item','wx:for-index','wx:key','bindtap','catchtap'] > 报错信息:'attribute name: "<%= name %>" is not allowed in this template' ### 组件属性和事件名称校验 在使用组件的时,会对使用过程中属性名和绑定的事件名称进行校验。组件属性校验分为内建组件与自定义组件两部分。 校验点: + 内建组件:使用的属性名和绑定事件必须在组件内有定义否则校验不通过 - chameleon 内建组件有:['view','text','scroller','list','cell','image','switch','video','input','button','radio','checkbox', 'page', 'router-view'] + 自定义组件:模板校验时和根据 usingComponents 配置解析对应组件,使用组件时属性名和事件名必须在组件内有定义否则校验不通过。 > 报错信息: > "The property <%= propName %> is not a property of component <%= componentName %> which path is: <%= componentPath %>" > 报错信息: > "The event <%= eventName %> is not defined in component <%= componentName %> which path is: <%= componentPath %>" ### 内置组件嵌套规则校验 在使用 chameleon 内置组件时,内置组件之间需要遵循一定的嵌套关系。 校验点: + text 组件 - text 组件不能够包含任何子组件 + scroller 组件 - scroller 组件不能包含 textarea 或者 video 组件 + list - list 组件不能包含 textarea 或者 video 组件 + video - video 组件如果包含子组件,那么只能是 text 组件 > 报错信息 > 'tag "<%= parent %>" can not have any child elements, therefor tag "<%= forbiddenTag %>" is not allowed as it\'s children' > 报错信息 > 'tag "<%= parent %>" can only have "<%= elements %>" as it\'s child elements, therefor tag "<%= forbiddenTag %>" is not allowed as it\'s children' > 报错信息 > 'tag "<%= parent %>" can not have "<%= forbiddenTag %>" as it\'s child elements, and element in this list: "<%= elements %>" is forbidden as well' ================================================ FILE: packages/chameleon-linter/docs/templates/template-internal.md ================================================ # 模板校验说明 ### 模板语法校验 模板校验通过自定义修改 npm module [htmllint](https://github.com/htmllint/htmllint) 实现。 #### 校验规则 ##### 标签校验 标签校验采用白名单机制,即只有在给定的标签列表里的标签才能使用。校验代码入口 [linters/template/index.js](../linters/template/index.js) 校验白名单由三部分组成: - cml 或者 vue 模板支持的标签: [cml 模板支持的标签](../linters/template/config/cml-white-list.js) | [vue 模板支持的标签](../linters/template/config/vue-white-list.js) - 三端平台对应的 native 标签: [wx 小程序原生支持标签](../linters/template/config/wx-white-list.js) | [weex 原生支持的标签](../linters/template/config/weex-white-list.js) | [web 原生支持标签](../linters/template/config/web-white-list.js) - 模板中通过 usingComponents 配置引入的自定义标签 白名单获取规则: 假设当前组件名为 index 那么对应三端的模板文件分别为 index.web.cml | index.weex.cml | index.wx.cml 对应三端百名单组成 - index.web.cml + template 的 lang 为 cml: cml 支持的标签 + 模板中自定义的标签 + web 原生支持的标签 + template 的 lang 为 vue: vue 支持的标签 + 模板中自定义的标签 + web 原生支持的标签 - index.weex.cml + template 的 lang 为 cml: cml 支持的标签 + 模板中自定义的标签 + weex 原生支持的标签 + template 的 lang 为 vue: vue 支持的标签 + 模板中自定义的标签 + weex 原生支持的标签 - index.wx.cml + template 的 lang 为 cml: cml 支持的标签 + 模板中自定义的标签 + wx 原生支持的标签 + template 的 lang 为 vue: vue 支持的标签 + 模板中自定义的标签 + wx 原生支持的标签 使用方法: 给 htmllint 新加 ruler: tag-white-list.js 并且增加配置项 tag-only-allowed-names 用于接收标签白名单。 使用时只需要传入配置项 options['tag-only-allowed-names] = ['view','template', ...] 即可。 ##### 属性校验 属性校验通过黑名单机制实现,同样是基于 npm module [htmllint](https://github.com/htmllint/htmllint) 实现。 属性校验的两种方式: - 通过设置 htmllint 的 attr-bans 直接添加禁止的属性列表,比如: cml 模板禁止 v-if | v-show | v-for 等。 - 通过给 htmllint 新加的 ruler: attr-name-forbidden.js,给定一个正则表达式来匹配禁止的事件绑定以及自定属性绑定指令,比如: cml 模板禁止 v-bind:src | :src | v-on:click | @click 等。 两种方式可以通过把 attr-bans 的列表和 attr-name-fordden ruler 的正则表达式合并为一个表达式来统一,为了语义清晰目前使用的是两种方式并存互补的形式。 黑名单获取规则: 目前需要校验的只有两类指令 cml 与 类vue 指令。现阶段校验 lang 为 cml 的模板时,要求模板里不能出现 vue 的指令,lang 为 vue 时不能出现 cml 的指令。 使用方法: 在使用时给 attr-bans 配置指令黑名单,给 attr-name-forbidden-regex 传入匹配非法指令的正则表达式。 具体代参见 [cml-htmllinter 配置项](../linters/template/index.js) ### 模板事件与模板变量校验 #### 校验规则 校验规则要求,模板里使用的事件方法和变量名要在 script 的 class 有相应的定义。 ##### script class 定义方法名和变量名获取 具体提取方法参见源码:[js-ast-parser.js](../checkers/template/lib/js-ast-parser.js) | [template-ast-parser.js](../checkers/template/lib/template-ast-parser.js) - 方法名: 方法名从 class 定义中 methods 属性提取 - 变量名: 从 class 定义中 props、data、computed 属性中提取 ##### template 模板中方法名和变量名获取 - 方法名 + template lang 为 cml: 从 c-bind 、 c-catch 指令对应的属性值获取 + template lang 为 vue: 从 v-on、 v-once 、 @ 指令对应的属性值获取 - 变量名 + template lang 为 cml: 从 属性值由 {{}} 给出的表达式中获取 + template lang 为 vue: 从 v-if、v-show、v-else-if、v-model、: 等直接绑定数据值得指令获取,具提参见 [template-ast-parser.js](../checkers/template/lib/template-ast-parser.js) ### 资料 [htmllint](https://github.com/htmllint/htmllint) [htmlparser2](https://github.com/fb55/htmlparser2) cml-htmllinter ================================================ FILE: packages/chameleon-linter/docs/templates/template.md ================================================ ### 模板书写规范 chameleon 模板书写规范尊从 HTML5 基本规范。 ## 模板规范 chameleon 支持三端(三种 native 环境),每个组件在每个环境对应有一个模板。模板命名格式 `组件名称+端名称.cml` 比如:c-title 组件 ``` ├── components │ ├── c-title │ │ ├── c-title.web.cml │ │ ├── c-title.weex.cml │ │ └── c-title.wx.cml ``` 其中: c-title.web.cml 为 web 端模板,c-title.weex.cml 为 iOS、Android 端,c-title.wx.cml 为微信小程序端。 本节模板规范就是指对这三个模板文件的编写规范。 ### 模板语言指定 每个端的模板都可以并且必须选择两种语法规范中的一个,cml 语法规范 或者 类 vue 语法规范。指定语法规范的方式为在根节点 template 标签上给属性 lang 指定 "cml" 或者 "vue"。 列如指定模板为 cml 语法规范 ``` ``` > 注意:每个模板只能够有一个根节点并且必须为 template 标签,template 便签每个模板只能有一个。 ### 模板标签使用规范 每个模板可以使用的标签,由模板语言、模板所属的 native 环境以及自定义组件三部分共同组成。 若模板语言为 cml 的模板,除自定义组件的标签还可以使用 chamelon 支持的便签以及对应 native 环境的原始组件标签。 若模板语言为类 vue 的模板,截止目前[2018-11-05] chamelon 在类 vue 模板语言下能够使用的标签和模板语言为 cml 的模板一致。 > chamelon 支持的标签有: template、view、text、block、scroller、list、cell、image、switch、video、input、button、radio、checkbox #### 举例 仍以 c-title 组件为例,假设各个模板都有自定义组件配置 ``` ``` + c-title.web.cml - 可以使用 chameleon 支持的 view、text、block 等基本标签,web 原生标签 div、p、span 等,以及自定义组件 tickets。 + c-title.weex.cml - 可以使用 chameleon 支持的 view、text、block 等基本标签,weex 支持的标签,以及自定义组件 tickets。如果以 vue 作为 weex 使用的前端框架,那么 weex 支持的标签基本和 vue 框架支持的标签基本一致,其中有部分不支持的标签比如:transition 标签,具体请参见 weex 文档。 + c-title.wx.cml - 可以使用 chameleon 支持的 view、text、block 等基本标签,wx 原生标签比如 swiper、movable-area、cover-view、web-view 等,以及自定义组件 tickets。 ### 模板指令使用规范 每个模板可使用的指令由模板语言支持的指令和 native 环境支持的指令两部分构成。不同模板语言的模板之间不能相互使用彼此的指令。lang = "cml" 时不能使用 类 vue 语法的指令,lang = "vue" 时不能使用 chameleon 的指令。 + 模板语言为 cml 时支持的指令有:c-if、c-else、c-else-if、c-for、c-for-index、c-for-item、c-model、c-text、c-show、c-bind、c-catch + 模板语言为类 vue 时支持的指令有:v-if、v-else、v-else-if、v-for、v-on、v-bind、v-html、v-show、v-model、v-pre、v-once、slot-scope、is、@、: > 类 vue 语法支持上述列表中的指令,其他 vue.js 的指令如 v-cloak 是不支持的。 #### 举例 若模板语言为 "cml" 即 template 标签 lang 属性为 "cml",native 环境为微信小程序。还是以 c-title 组件为例,那么此时对应的是 c-title.wx.cml 模板。 ``` c-title.wx.cml: ``` 那么模板里可以使用 chameleon 支持的指令: c-if、c-else、c-else-if、c-for、c-for-index、c-for-item、c-model、c-text、c-show、c-bind、c-catch, 以及微信小程序原生支持的指令 wx:if、wx:elif、wx:else、wx:for、wx:for-item、wx:for-index、wx:key、bindtap、catchtap。 ================================================ FILE: packages/chameleon-linter/file-spec.js ================================================ const fs = require('fs'); const chalk = require('chalk'); const utils = require('./utils'); const linters = require('./linters'); const checkers = require('./checkers'); const config = require('./config'); /** * 文件语法检查 * * @param {Object} parts 片段列表 * @return {Promise} promise */ const lintCmlFile = async (parts) => { let result = {}; // 语法检查 for (let partName of ['json', 'interface', 'template', 'script', 'style']) { let part = parts[partName]; // 不存在的片段直接跳过 if (!part) { continue; } switch (partName) { // 模板 case 'template': if (result.json && result.json.obj) { result.template = await linters.templatelint(part, result.json.obj); } break; // 接口 case 'interface': result['interface'] = await linters.scriptlint(part); break; // 脚本 case 'script': result.script = await linters.scriptlint(part); break; // json case 'json': result.json = await linters.jsonlint(part); break; // 样式 case 'style': result.style = await linters.stylelint(part); break; default: break; } result[partName] = result[partName] || {}; result[partName].start = part.line; result[partName].type = part.type; result[partName].file = part.file; result[partName].platform = part.platformType; } return result; } /** * 文件逻辑检查 * * @param {Object} lintedResult 语法校验后的结果 * @param {string} filepath 文件路径 * @return {Promise} promise */ const checkFile = async (lintedResult, filepath) => { // 校验style checkers.style(lintedResult); // 校验json checkers.json(lintedResult); // 校验脚本 checkers.script(lintedResult); // 模板校验 BEGIN if ( lintedResult.template && lintedResult.template.ast && lintedResult.script && lintedResult.script.ast && lintedResult.json && lintedResult.json.obj ) { let templateChecker = new checkers.Template(filepath, lintedResult); lintedResult.template.messages.push(...templateChecker.check()); } // 模板校验 END return lintedResult; }; /** * 处理过程 * * @param {string} filepath 文件路径 * @return {Promise} promise */ const checkFileContent = async (filepath) => { let parts = utils.getCmlParts(filepath); let result = await lintCmlFile(parts); result = await checkFile(result, filepath); return result; }; /** * 检查CML文件格式 * * @param {string} filepath 文件路径 * @return {Object} 校验结果 */ const checkCMLFileSpecification = async (filepath) => { let platforms = config.getPlatforms(); let result = { core: { messages: [] } }; if (new RegExp('([^/]*?)\.(' + platforms.join('|') + ')\.cml$', 'g').test(filepath)) { let interfaceFile = filepath.replace(new RegExp('\.(' + platforms.join('|') + ')\.cml$', 'g'), '.interface'); if (!fs.existsSync(interfaceFile)) { result.core.messages.push(chalk.red('[error]') + ' file: ' + interfaceFile + ' is not exist!'); } // cml多文件格式 else { Object.assign(result, await checkFileContent(filepath)); } } // cml单文件格式 else { Object.assign(result, await checkFileContent(filepath)); } return result; } /** * 检查接口文件规范 * * @param {string} filepath 文件路径 * @return {Promise} promise */ const checkInterfaceFileSpecification = async (filepath) => { let {parts, messages} = utils.getInterfaceParts(filepath); let result = {}; let keys = Object.keys(parts); if (messages.length == 0 && keys.length > 1) { for (let key in parts) { if (parts.hasOwnProperty(key)) { let part = parts[key]; result[key] = await linters.scriptlint(part); result[key] = result[key] || {}; result[key].start = part.line; result[key].type = part.type; result[key].file = part.file; result[key].platform = part.platformType; } } // 校验脚本 checkers.script(result); return result; } else { return { core: { type: 'core', messages, file: filepath } }; } } /** * 校验文件格式 * * @param {string} filepath 文件路径 * @param {string} filetype 文件类型 * @return {Promise} promise */ const checkFileSpecification = async (filepath, filetype) => { if (filetype == 'cml') { return checkCMLFileSpecification(filepath); } else if (filetype == 'interface') { return checkInterfaceFileSpecification(filepath); } }; module.exports = checkFileSpecification; module.exports.lintCmlFile = lintCmlFile; module.exports.checkFile = checkFile; ================================================ FILE: packages/chameleon-linter/file-structure.js ================================================ const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const config = require('./config'); /** * 文件列表 * * @type {Array} */ const fileList = [ 'chameleon.config.js', 'src/app/app.cml', 'src/router.config.json' ]; /** * 校验文件是否存在 * * @return {Object} 校验结果 */ const checkFilesExist = () => { let currentWorkspace = config.getCurrentWorkspace(); let messages = []; let result = {core: {messages}}; fileList.forEach(filepath => { let fileExist = fs.existsSync(path.resolve(currentWorkspace, filepath)); if (!fileExist) { messages.push(chalk.red('[error]') + ' file: ' + filepath + ' is not exist!'); } }); return result; }; module.exports = checkFilesExist; ================================================ FILE: packages/chameleon-linter/index.js ================================================ const glob = require('glob'); const fileStruture = require('./file-structure'); const fileSpec = require('./file-spec'); const utils = require('./utils'); const config = require('./config'); const chalk = require('chalk'); // process.on('uncaughtException', function (err) { // console.log(err); // }); // process.on('unhandledRejection', error => { // // Will print "unhandledRejection err is not defined" // console.log('unhandledRejection', error.message); // }); /** * 获取cml文件列表 * * @return {array} 文件列表 */ const getCMLFileList = () => { return new Promise((resolve, reject) => { glob('src/**/*.cml', {}, function (er, files) { resolve(files); }); }); }; /** * 获取interface文件列表 * * @return {Array} 文件列表 */ const getInterfaceList = () => { return new Promise((resolve, reject) => { glob('src/**/*.interface', {}, function (er, files) { resolve(files); }); }); } module.exports = async (currentWorkspace, needOutputWarnings = true) => { await config.init(currentWorkspace); if (!config.isChameleonProject()) { console.log(chalk.red('[ERROR] ') + 'The current project is not a chameleon project!'); return; } let results = []; if (config.getRuleOption('core-files-check')) { // 1. 校验目录结构 results.push(fileStruture()); } // 2. 检查所有的cml文件 if (config.getRuleOption('cml-files-check')) { let files = await getCMLFileList(); for (let filepath of files) { let result = await fileSpec(filepath, 'cml'); if (result && Object.keys(result).length) { results.push(result); } } } // 3. 检查所有的interface文件 if (config.getRuleOption('interface-files-check')) { let interfaces = await getInterfaceList(); for (let filepath of interfaces) { let result = await fileSpec(filepath, 'interface'); if (result && Object.keys(result).length) { results.push(result); } } } // 4. 检查其他文件 // todo // 5. 输出 if (needOutputWarnings) { console.log(chalk.cyan('[INFO] ') + 'Syntax check results:'); let hasOutput = false; results.forEach((result) => { if (utils.outputWarnings(result)) { hasOutput = true; } }); if (!hasOutput) { console.log('Congratulations! Everything is OK!'); } } return results; }; ================================================ FILE: packages/chameleon-linter/linters/index.js ================================================ module.exports = { stylelint: require('./style'), templatelint: require('./template'), scriptlint: require('./script'), jsonlint: require('./json') }; ================================================ FILE: packages/chameleon-linter/linters/json.js ================================================ const jsonlint = require('json-lint'); const config = require('../config/parser-config'); const parse = require('@babel/parser').parse; module.exports = async (part) => { let messages = []; let result = jsonlint(part.content, { comments: false }); if (result.error) { messages.push({ line: result.line, column: result.character, token: result.c, msg: result.error }); } const opts = config.script; let obj = null; try { obj = (new Function('return ' + part.content.replace(/^\n*/g, '') + ';'))(); } catch (e) {} return { start: part.line, ast: result.error ? null : parse('module.exports = \n' + part.content, opts), obj: obj, messages }; }; ================================================ FILE: packages/chameleon-linter/linters/script.js ================================================ const parse = require('@babel/parser').parse; const config = require('../config/parser-config'); const traverse = require('@babel/traverse')['default']; const globalVars = require('../config/globalVars'); const uniq = require('lodash.uniq'); const Map = { watch: 'watcher', computed: 'computed property', methods: 'method' }; function checkArrowFun(path) { let messages = []; if (path.node) { let properties = path.get('value').get('properties'); switch (path.node.key.name) { case 'watch': case 'computed': case 'methods': if (properties.forEach) { (properties || []).forEach(property => { messages = messages.concat(handleProperty(property, path.node.key.name)); }); } break; case 'beforeCreate': case 'created': case 'beforeMount': case 'mounted': case 'beforeDestroy': case 'destroyed': messages = messages.concat(handleProperty(path, path.node.key.name)); break; default: break; } } return messages; } function handleProperty(property, propertyName) { let messages = []; if (property.get('value').isArrowFunctionExpression()) { let node = property.get('key').node; let name = node.name; messages.push({ line: node.loc.start.line, column: node.loc.start.column + 1, msg: Map[propertyName] ? (Map[propertyName] + ' "' + name + '" cannot be used as an arrow function') : ('lifecycle hook "' + name + '" cannot be used as an arrow function') }); } return messages; } function getForbiddenGlobalTokens(platform = 'all') { let tokenList = []; platform = platform.toUpperCase(); Object.keys(globalVars).forEach(key => { if (platform === 'ALL' || key !== platform) { tokenList = tokenList.concat(globalVars[key]); } }); tokenList = uniq(tokenList); return tokenList; } function checkGlobal(path, tokenList) { let messages = []; let programScope = path.scope; Object.keys(programScope.globals).forEach(tokenName => { if (~tokenList.indexOf(tokenName)) { messages.push({ line: programScope.globals[tokenName].loc.start.line, column: programScope.globals[tokenName].loc.start.column, token: tokenName, msg: 'global variable: "' + tokenName + '" should not be used in this file' }); } }); return messages; } /** * 校验语法 * * @param {Object} part 片段 * @return {Object} 语法检查结果 */ const checkSyntax = function (part) { let messages = []; const opts = config.script; let ast; let tokenList = []; try { ast = parse(part.content, opts); } catch (err) { messages.push({ line: err.loc.line, column: err.loc.column + 1, msg: err.message.replace(/ \((\d+):(\d+)\)$/, '') }); } try { tokenList = getForbiddenGlobalTokens(part.platformType || 'all'); traverse(ast, { // check arrow function: we do not allow arrow functions in life cycle hooks. ClassProperty(path) { messages = messages.concat(checkArrowFun(path)); }, // check global variables: we shall never use any platform specified global variables such as wx, global, window etc. Program(path) { messages = messages.concat(checkGlobal(path, tokenList)); } }); } catch (e) { console.log(e); } return { start: part.line, ast, messages }; } module.exports = function (part) { return checkSyntax(part); }; ================================================ FILE: packages/chameleon-linter/linters/style.js ================================================ const stylelint = require('stylelint'); const stylus = require('stylus/lib/stylus'); const config = require('../config'); const parserConfig = require('../config/parser-config'); const postcss = require('postcss'); const converter = require('stylus-converter'); const path = require('path'); module.exports = async (part) => { return new Promise(function (resolve, reject) { let messages = []; if (config.neexLintWeex() && (!part.platformType || part.platformType == 'weex')) { parserConfig.style.rules['selector-max-compound-selectors'] = 1; } else { parserConfig.style.rules['selector-max-compound-selectors'] = 0; } let lang = part.params.lang || 'less'; if (lang == 'stylus') { stylus.render(part.content, {...{ paths: [path.dirname(path.resolve(config.getCurrentWorkspace(), part.file))] }, ...parserConfig.style}, function (err) { if (err) { let isImportStyl = err.filename && (path.extname(err.filename) === '.styl'); let message = { line: isImportStyl ? 0 : err.lineno, column: isImportStyl ? 0 : err.column, msg: err.message }; // guess line coloumn from err message if (err.lineno === undefined) { err.message.replace(/stylus:\s*(\d+):\s*(\d+)/g, (match, line, column) => { message.line = +line; message.column = +column; }); } messages.push(message); } let ast; try { let sassCode = converter.converter(part.content, {conver: 'sass', autoprefixer: false}); sassCode = sassCode.replace(/\n\s*\}/g, '}'); ast = postcss.parse(sassCode); } catch (e) { ast = null; } resolve({ start: part.line, ast: ast, messages }); }); } else { let options = { code: part.content, config: parserConfig.style }; if (~['less', 'sass', 'scss'].indexOf(lang)) { options.syntax = lang; } stylelint .lint(options) .then(function(result) { if (result.errored) { result.results.forEach(result => { result.errored && result.warnings.forEach(warn => { messages.push({ line: warn.line, column: warn.column, msg: warn.text.replace(/\(.*?\)$/g, '') }); }); }); } let ast; try { ast = postcss.parse(part.content); } catch (e) { ast = null; } resolve({ start: part.line, ast: ast, messages }); }); } }) }; ================================================ FILE: packages/chameleon-linter/linters/template.js ================================================ const path = require('path'); const htmllint = require('cml-htmllint'); const config = require('../config'); const whiteListConifg = require('../config/white-list'); const builtinComponents = require('../config/built-in-components'); const tagEmbedRules = require('../config/tag-embed-rules.json'); const fakeComps = require('../config/fakeComps'); const utils = require('../utils'); const linter = new htmllint.Linter(); const CML_METHODNAME_REGEX = /(?:c-bind:|c-catch:)(\w+)/; const VUE_METHODNAME_REGEX = /(?:v-on:|v-once:|@)(\w+)/; const VUE_PROP_REGEX = /(?:v-bind:|:)((?:\w|-)+)/; const APP_ENTRANCE_FILENAME = 'app.cml'; /** * Parse json ast and retrive the custumized component names. * @param {Object} jsonAst * @returns An array contains all the component names. ej. [{name: 'view', isOrigin: true}, {name:'picker', isOrigin: false}] */ function getCustimizedTags(jsonAst, {platform = '', templatePath = ''}) { let result = []; let componentsObj = {}; if (jsonAst) { let baseJson = jsonAst.base || {}; let platformJson = {}; if (platform) { platformJson = jsonAst[platform] || {}; } Object.assign(componentsObj, baseJson.usingComponents, platformJson.usingComponents); } result = Object.entries(componentsObj) .map((infoPair) => { let [name, basePath] = infoPair; return { name: utils.toDash(name), isOrigin: !utils.isCmlComponent(path.resolve(config.getCurrentWorkspace(), templatePath), basePath) } }); return result; } /** * Getting options for html linter. * @params {Object} * platformType: 'web'|'wx'|'weex' | '' // '' stands for single file component * lang: 'cml'|'vue' * custimizedTags: tags appeding by usingComponents. */ function getLintOptions(params) { let options = { 'line-no-trailing-whitespace': false, 'line-end-style': false, 'attr-name-style': false, 'indent-width': false, 'class-style': 'none', 'template-req-lang': false, 'attr-quote-style': 'quoted', 'spec-char-escape': false, 'attr-req-value': false, 'tag-close': true // ignoring tag close detection for now, a better solution will be provide in the future. }; options['template-lang-allows'] = ['cml', 'vue']; options['template-lang'] = params.lang; options['attr-bans'] = whiteListConifg.getForbiddenAttrsByLang(params.lang); options['tag-only-allowed-names'] = whiteListConifg .getAllowedTags() .concat(params.custimizedTags .map((tag) => { return tag.name; })); // origin components that are referenced by usingComponents. options['origin-tag-list'] = params.custimizedTags .filter((tag) => { return tag.isOrigin; }) .map((tag) => { return tag.name; }); // options associated with directives. if (params.lang === 'vue') { options['directive-name-forbidden-regex'] = /^(c-(?:bind|catch)(?!\w))/; } else { options['directive-name-forbidden-regex'] = /^((?:v-(?:bind|on|once)(?!\w))|:|@)/; options['text-ignore-regex'] = /{{(.*?)}}/; } options['cml-directives'] = whiteListConifg.getForbiddenAttrsByLang('vue'); options['cml-valid-value-regex'] = /^\s*{{.*}}\s*$/; // built-in components configurations. options['component-allow-attr'] = builtinComponents.getCml(); if (params.lang === 'cml') { options['component-event-regex'] = CML_METHODNAME_REGEX; options['component-prop-regex'] = false; } else { options['component-event-regex'] = VUE_METHODNAME_REGEX; options['component-prop-regex'] = VUE_PROP_REGEX; } // built-in embed tags' rules options['tag-embed-tags'] = tagEmbedRules; // origin components rules options['origin-tag-forbiden-directive-regex'] = new RegExp('^(' + whiteListConifg.getForbiddenAttrsByLang('vue').join('|') + ')'); return options; } /** * A temporary solution for application entrance file app.cml * @param {Object} options lint options */ function addFakeComp(options, comp) { if (options['component-allow-attr']) { options['component-allow-attr'][comp.name] = comp.allowAttrs; } if (options['tag-only-allowed-names']) { options['tag-only-allowed-names'].push(comp.name); } } function isAppEntranceFile(file) { return file && path.basename(file) === APP_ENTRANCE_FILENAME; } /** * @part {Object} * line: line offset of template part. * rawContent: the whole tempalte string. * platformType:'web'|'wx'|'weex' * params: { * lang:'cml' * } * @jsonAst {Object} An ast of json part. */ module.exports = (part, jsonAst) => { return new Promise(async (resolve, reject) => { let ast = {}; let messages = []; let lintResults = []; let lintOptions = {}; let templateMatches = []; part.params.lang || (part.params.lang = 'cml'); ast = linter.parser.parse(part.rawContent); templateMatches = part.rawContent.match(/<\/?\s*?template/ig); // eslint-disable-next-line no-magic-numbers if (!templateMatches || templateMatches.length !== 2) { return resolve({ start: part.line, ast, messages: [{ line: part.line + 1, column: 0, token: 'template', msg: 'Each template can only have one group of template tags' }] }); } lintOptions = getLintOptions({ ...part.params, platformType: part.platformType || 'cml', ...{custimizedTags: getCustimizedTags(jsonAst, { templatePath: part.file, platform: part.platformType })} }); // adding fake comps const allowedFakeComps = ['component']; if (isAppEntranceFile(part.file)) { allowedFakeComps.push('app'); } fakeComps && fakeComps.forEach((comp) => { if (~allowedFakeComps.indexOf(comp.name)) { addFakeComp(lintOptions, comp); } }); lintResults = await htmllint(part.rawContent, lintOptions); lintResults && lintResults.forEach((item) => { messages.push({ line: item.line, column: item.column, token: htmllint.messages.guessToken(item), msg: htmllint.messages.renderIssue(item) }); }); resolve({ start: part.line, ast, messages }); }); }; ================================================ FILE: packages/chameleon-linter/package.json ================================================ { "name": "chameleon-linter", "version": "1.0.8", "description": "cml规范校验工具", "main": "index.js", "scripts": { "test": "nyc ./node_modules/mocha/bin/mocha ./test/**/*.test.js --timeout=3000", "non-report-test": "./node_modules/mocha/bin/mocha ./test/template/checker/template-lib-template.test.js", "break-non-report": "./node_modules/mocha/bin/mocha --inspect-brk ./test/template/linter/**/*.test.js" }, "bin": { "cml-linter": "./bin/cli.js" }, "keywords": [ "linter", "cml", "chameleon" ], "author": "Chameleon-Team", "email": "ChameleonCore@didiglobal.com", "license": "Apache", "dependencies": { "@babel/parser": "^7.1.3", "@babel/traverse": "^7.1.4", "bulk-require": "^1.0.1", "chalk": "^2.4.1", "chameleon-tool-utils": "1.0.8", "cml-component-parser": "1.0.8", "cml-htmllint": "1.0.8", "cml-js-parser": "1.0.8", "commander": "^2.19.0", "glob": "^7.1.3", "json-lint": "^0.1.0", "lodash.filter": "^4.6.0", "lodash.groupby": "^4.6.0", "lodash.map": "^4.6.0", "lodash.uniq": "^4.5.0", "postcss": "^7.0.5", "stylelint": "^9.7.0", "stylus": "^0.54.5", "stylus-converter": "^0.8.1", "traverse": "^0.6.6" }, "devDependencies": { "chai": "^4.2.0", "mocha": "*", "nyc": "^13.1.0" }, "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-linter/test/checker/cml/json/nonstandard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/json/nonstandard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/json/nonstandard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/json/nonstandard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/json/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/global-variable/standard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/global-variable/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/global-variable/standard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/global-variable/standard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-interface-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-src-cml-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-src-cml-mis-prop-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-src-js-class-name-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-src-js-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/include-src-js-mis-prop-fail.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/origin-comp-interface.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/origin-interface.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/src/someplatform-mis-class.js ================================================ class Method implements ExtendInterfaceInterface { getMsg(msg) { return 'web:' + msg; } } export default new Method(); ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/src/someplatform-mis-prop.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/include/src/someplatform-mis-prop.js ================================================ class Method implements OnlyInterfaceInterface { getMsg(msg) { return 'web:' + msg; } } export default new Method(); ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/interfaces/prop-not-defined.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/no-global-variable/standard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/no-global-variable/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/no-global-variable/standard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/no-global-variable/standard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/nointerface/nonstandard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/nonstandard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/properties-methods/event-not-defined.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/properties-methods/event-not-defined.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/properties-methods/property-not-defined.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/properties-methods/property-not-defined.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/standard/standard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/standard/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/standard/standard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/standard/standard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/checker/cml/script/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/cml.test.js ================================================ const config = require('../config'); const jsonLinter = require('../linters/json'); const styleLinter = require('../linters/style'); const scriptLinter = require('../linters/script'); const checkers = require('../checkers/index'); const utils = require('../utils'); const fileSpec = require('../file-spec'); const chai = require('chai'); const assert = chai.assert; const expect = chai.expect; const path = require('path'); describe('cml', function() { describe('lint-json', function() { before(async function() { config.init(path.resolve(__dirname, '../linter/cml/json/')); }); it('json-standard', async function() { const cmlPath = path.resolve(__dirname, './linter/cml/json/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await jsonLinter(parts.json); assert.equal(result.messages.length, 0); }); it('json-no-quotes', async function() { const cmlPath = path.resolve(__dirname, './linter/cml/json/no-quotes.cml'); const parts = utils.getCmlParts(cmlPath); const result = await jsonLinter(parts.json); expect(result.messages).to.deep.equal([{ line: 9, column: 7, token: 'b', msg: 'Unknown Character \'b\', expecting a string for key statement.' }]); }); it('json-no-bracket', async function() { const cmlPath = path.resolve(__dirname, './linter/cml/json/no-bracket.cml'); const parts = utils.getCmlParts(cmlPath); const result = await jsonLinter(parts.json); expect(result).to.deep.equal({ start: 1, ast: null, obj: null, messages: [ { line: 16, column: 0, token: '\n', msg: 'EOF Error, expecting closing \'}\'.' } ] }); }); it('json-no-comma', async function() { const cmlPath = path.resolve(__dirname, './linter/cml/json/no-comma.cml'); const parts = utils.getCmlParts(cmlPath); const result = await jsonLinter(parts.json); expect(result).to.deep.equal({ start: 1, ast: null, obj: null, messages: [ { line: 7, column: 3, token: '"', msg: 'Unknown Character \'"\', expecting a comma or a closing \'}\'' } ] }); }); }); describe('lint-style', function () { before(async function() { config.init(path.resolve(__dirname, '../linter/cml/style/')); }); it('style-standard', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); assert.equal(result.messages.length, 0); }); it('style-no-bracket', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/no-bracket.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal([{ line: 4, column: 1, msg: 'Unclosed block ' }]); }); // 支持嵌套(未来) it('style-nest', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/nest.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal([{ line: 11, column: 3, msg: 'Expected ".app-root .a" to have no more than 1 compound selector ' } ]); }); // 支持stylus it('style-stylus', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/standard.stylus.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal([]); }); // 支持stylus 错误情况 it('style-stylus-error', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/no-standard.stylus.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal([ { 'column': 3, 'line': 7, 'msg': 'stylus:7:3\n 3| /*只作用于 web body下的first-child 节点*/\n 4| .app-root\n 5| position:\n 6| top: 0px;\n 7| bottom: 0px;\n--------^\n 8| left: 0px;\n 9| right: 0px;\n 10| \n\nexpected \"indent\", got \";\"\n' } ]); }); // 支持stylus it('style-important', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/no-standard-important.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal([]); }); // 缺少分号 it('style-no-semicolon', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/style/no-semicolon.cml'); const parts = utils.getCmlParts(cmlPath); const result = await styleLinter(parts.style); expect(result.messages).to.deep.equal( [ { column: 8, line: 6, msg: 'Missed semicolon ' } ] ); }); }); describe('lint-script', function () { it('standard', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/script/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await scriptLinter(parts.script); assert.equal(result.messages.length, 0); }); it('nonstandard', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/script/nonstandard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await scriptLinter(parts.script); expect(result.messages).to.deep.equal([ { line: 12, column: 3, msg: 'Unexpected token, expected ","' } ]); }); it('nonstandard-arrow', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/script/nonstandard-arrow.cml'); const parts = utils.getCmlParts(cmlPath); const result = await scriptLinter(parts.script); expect(result.messages).to.deep.equal( [ { 'column': 5, 'line': 19, 'msg': 'computed property "hasApplyList" cannot be used as an arrow function' }, { 'column': 3, 'line': 30, 'msg': 'lifecycle hook "mounted" cannot be used as an arrow function' } ] ); }); it('syntax-error', async function () { const projectRoot = path.resolve(__dirname, './template/docs/'); config.init(projectRoot); const cmlPath = path.resolve(__dirname, './linter/cml/cml/syntaxError/standard.weex.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.script(result); expect(result.script.messages).to.deep.equal([ { 'column': 3, 'line': 14, 'msg': 'Unexpected token, expected \",\"' } ]); }); }); describe('lint-cml-file', function () { it('standard-single-file-spec', async function () { const projectRoot = path.resolve(__dirname, './checker/cml/cml'); config.init(projectRoot); const cmlPath = path.resolve(__dirname, './linter/cml/cml/standard.cml'); const result = await fileSpec(cmlPath, 'cml'); let flag = false; Object.keys(result).forEach(key => { if (result[key].messages.length) { flag = true; } }); assert.equal(flag, false); }); it('standard-single-file', async function () { const projectRoot = path.resolve(__dirname, './template/docs/'); config.init(projectRoot); const cmlPath = path.resolve(__dirname, './linter/cml/cml/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); let flag = false; Object.keys(result).forEach(key => { if (result[key].messages.length) { flag = true; } }); assert.equal(flag, false); }); it('standard-multi-file', async function () { const projectRoot = path.resolve(__dirname, './template/docs/'); config.init(projectRoot); const cmlPath = path.resolve(__dirname, './linter/cml/cml/standard/standard.wx.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); let flag = false; Object.keys(result).forEach(key => { if (result[key].messages.length) { flag = true; } }); assert.equal(flag, false); }); it('nonstandard', async function () { const cmlPath = path.resolve(__dirname, './linter/cml/cml/nonstandard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); let flag = false; Object.keys(result).forEach(key => { if (result[key].messages.length) { flag = true; } }); assert.equal(flag, true); }); }); describe('check-json', function () { it('standard', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/json/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.json(result); expect(result.json.messages).to.deep.equal([]); }); it('nonstandard', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/json/nonstandard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.json(result); expect(result.json.messages).to.deep.equal([ { 'column': 4, 'line': 4, 'msg': 'Useless fields: weex.usingComponents', 'token': 'weex.usingComponents' } ]); }); it('nonstandard-distinguish-platform', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/json/nonstandard.wx.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.json(result); expect(result.json.messages).to.deep.equal([{ 'column': 2, 'line': 16, 'msg': 'Useless fields: weex', 'token': 'weex' }]); }); it('nonstandard-component-was-not-found', async function () { const projectRoot = path.resolve(__dirname, './checker/cml/json'); config.init(projectRoot); const cmlPath = path.resolve(__dirname, './checker/cml/json/nonstandard.weex.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.json(result); expect(result.json.messages).to.deep.equal([{ column: 20, line: 5, msg: 'component: [chameleon-ui-builtin/components/scroller/scroller] is not found', token: 'base.usingComponents.c-scroller' }]); }); }); describe('check-script', function () { before(function() { const projectRoot = path.resolve(__dirname, './checker/cml/script'); config.init(projectRoot); }); it('standard', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/script/standard/standard.wx.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.script(result); expect(result.script.messages).to.deep.equal([]); }); it('global-variable', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/script/global-variable/standard.wx.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.script(result); assert.equal(result.script.messages.length, 1); }); it('no-global-variable', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/script/no-global-variable/standard.wx.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.script(result); assert.equal(result.script.messages.length, 0); }); it('global-variable-xx', async function () { const cmlPath = path.resolve(__dirname, './checker/cml/script/standard.cml'); const parts = utils.getCmlParts(cmlPath); const result = await fileSpec.lintCmlFile(parts); checkers.script(result); assert.equal(result.script.messages.length, 0); }); it('property-not-defined', async function() { const cmlPath = path.resolve(__dirname, './checker/cml/script/properties-methods/property-not-defined.wx.cml'); const parts = utils.getCmlParts(cmlPath); let result = await fileSpec.lintCmlFile(parts); checkers.script(result); assert.equal(result['interface'].messages.length, 1); expect(result['interface'].messages[0]).to.include({ line: 24, column: 2, token: 'text' }); }); it('event-not-defined', async function() { const cmlPath = path.resolve(__dirname, './checker/cml/script/properties-methods/event-not-defined.web.cml'); const parts = utils.getCmlParts(cmlPath); let result = await fileSpec.lintCmlFile(parts); checkers.script(result); assert.equal(result['interface'].messages.length, 1); expect(result['interface'].messages[0]).to.include({ line: 26, column: 2, token: 'top' }); assert.equal(result.script.messages.length, 2); expect(result.script.messages).to.deep.equal([{ line: 33, column: 18, token: 'create', msg: 'event "create" is not defined in interface file "properties-methods/event-not-defined.interface"' }, { line: 37, column: 18, token: 'refOne', msg: 'event "refOne" is not defined in interface file "properties-methods/event-not-defined.interface"' }]); }); it('interface-prop-not-defined', async function() { const cmlPath = path.resolve(__dirname, './checker/cml/script/interfaces/prop-not-defined.interface'); const result = await fileSpec(cmlPath, 'interface'); expect(result['interface'].messages).to.have.lengthOf(1); expect(result['interface'].messages[0]).to.include({ line: 3, column: 2, token: 'platform' }); }); }); describe('check-interface-include', async function() { before(async function() { config.init(path.resolve(__dirname, './checker/cml/script/')); }); it('should fail because of not found include interface file', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-interface-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result).to.have.property('core'); expect(result.core.messages[0].msg).to.contain('include/include-interface-fail.interface'); expect(result.core.file).to.equal(interfacePath); }); it('should fail because of not found include src js file', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-src-js-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result).to.have.property('core'); expect(result.core.messages[0].msg).to.contain('someplatform.js'); expect(result.core.file).to.equal(interfacePath); }); it('should fail because of not found include src cml file', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-src-cml-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result).to.have.property('core'); expect(result.core.messages[0].msg).to.contain('someplatform.cml'); expect(result.core.file).to.equal(interfacePath); }); it('should fail test because class name mismatch', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-src-js-class-name-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result['interface'].messages[0].msg).to.contain('ExtendInterfaceInterface'); }); it('should fail test because missing prop', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-src-js-mis-prop-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result['interface'].messages[0].token).to.equal('setTitle'); expect(result['interface'].messages[0].msg).to.contain('someplatform-mis-prop.js'); }); it('should fail test because missing prop in someplatform cml file', async function() { const interfacePath = path.resolve(__dirname, './checker/cml/script/include/include-src-cml-mis-prop-fail.interface'); const result = await fileSpec(interfacePath, 'interface'); expect(result['interface'].messages[0].token).to.equal('age'); expect(result['interface'].messages[0].msg).to.contain('someplatform-mis-prop.cml'); }); }); }); ================================================ FILE: packages/chameleon-linter/test/config.test.js ================================================ const config = require('../config'); const assert = require('chai').assert; const path = require('path'); describe('lintrc', function() { describe('lintrc-check', function() { it('standard', function() { const projectRoot = path.resolve(__dirname, './lintrc/standard'); config.init(projectRoot); assert.equal(config.getRuleOption('core-files-check'), false); }); it('nonstandard', function() { const projectRoot = path.resolve(__dirname, './lintrc/nonstandard'); config.init(projectRoot); assert.equal(config.getRuleOption('core-files-check'), true); }); }); }); ================================================ FILE: packages/chameleon-linter/test/core/standard/chameleon.config.js ================================================ // 设置静态资源的线上路径 const publicPath = 'https://static.chameleon.com/pinche/hkcml'; // 设置api请求前缀 const apiPrefix = 'https://api.chameleon.com'; cml.config.merge({ cmlComponents: [ 'chameleon-ui', ], buildInfo: { wxAppId: '123456', wxEntryPage: '', webPath: 'https://api.chameleon.com/h5/commentinfo' }, check: { enable: true, enableTypes: ['Object', 'Array', 'Nullable'] }, 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/chameleon-linter/test/core/standard/src/app/app.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/core/standard/src/router.config.json ================================================ { "mode": "history", "domain": "https://api.chameleon.com", "routes":[ { "url": "/beatles/h5/user/user/homev2", "path": "pages/index/index", "name": "我的主页", "mock": "index.php" }, { "url": "/beatles/h5/errorpage", "path": "pages/error_page/error_page", "name": "出错啦", "mock": "errorpage.php" } ] } ================================================ FILE: packages/chameleon-linter/test/core/standard/src/store/index.js ================================================ import actions from './actions' import getters from './getters' import state from './state' import mutations from './mutations' import createStore from "chameleon-store"; export default createStore({ actions, getters, state, mutations }) ================================================ FILE: packages/chameleon-linter/test/core.test.js ================================================ const config = require('../config'); const fileStruture = require('../file-structure'); const assert = require('chai').assert; const path = require('path'); describe('core', function() { describe('core-file-check', function() { it('standard', function() { const projectRoot = path.resolve(__dirname, './core/standard'); config.init(projectRoot); const result = fileStruture(); assert.equal(result.core.messages.length, 0); }); it('nonstandard', function() { const projectRoot = path.resolve(__dirname, './core/nonstandard'); config.init(projectRoot); const result = fileStruture(); assert.equal(result.core.messages.length, 3); }); it('checkproject', function() { const projectRoot = path.resolve(__dirname, './core/nonstandard'); config.init(projectRoot); const result = config.isChameleonProject(); assert.equal(result, false); }); }); }); ================================================ FILE: packages/chameleon-linter/test/interface/common.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/interface.test.js ================================================ const config = require('../config'); const utils = require('../utils'); const fileSpec = require('../file-spec'); const chai = require('chai'); const assert = chai.assert; const should = chai.should; const expect = chai.expect; const path = require('path'); describe('interface', function() { describe('lint-interface', function() { it('interface-standard', async function() { const interfacePath = path.resolve(__dirname, './interface/common.interface'); const result = await fileSpec(interfacePath, 'interface'); let flag = false; Object.keys(result).forEach(key => { if (result[key].messages.length) { flag = true; } }); assert.equal(flag, false); }); }); }); ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/nointerface/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/nonstandard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/standard/standard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/standard/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/standard/standard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/standard/standard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/syntaxError/standard.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/syntaxError/standard.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/syntaxError/standard.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/cml/syntaxError/standard.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/json/.cmllintrc ================================================ { // 核心文件检查 'core-files-check': true, // cml文件校验 'cml-files-check': true, // 接口文件校验 'interface-files-check': true, 'cpx-support': true, 'platforms': ['weex', 'wx', 'web', 'baidu', 'alipay'] } ================================================ FILE: packages/chameleon-linter/test/linter/cml/json/no-bracket.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/json/no-comma.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/json/no-quotes.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/json/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/script/nonstandard-arrow.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/script/nonstandard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/script/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/.cmllintrc ================================================ { // 核心文件检查 'core-files-check': true, // cml文件校验 'cml-files-check': true, // 接口文件校验 'interface-files-check': true, 'cpx-support': true, 'platforms': ['weex', 'wx', 'web', 'baidu', 'alipay'] } ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/nest.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/no-bracket.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/no-semicolon.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/no-standard-important.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/no-standard.stylus.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/standard.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/linter/cml/style/standard.stylus.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/lintrc/nonstandard/.cmllintrc ================================================ { ================================================ FILE: packages/chameleon-linter/test/lintrc/standard/.cmllintrc ================================================ { 'core-files-check': false, } ================================================ FILE: packages/chameleon-linter/test/template/checker/template-lib-class.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const linters = require('../../../linters'); const checkers = require('../../../checkers'); const utils = require('../../../utils'); const config = require('../../../config'); const jsonAstParser = require('../../../checkers/template/lib/json-ast-parser'); const jsAstParser = require('../../../checkers/template/lib/js-ast-parser'); const templateAstParser = require('../../../checkers/template/lib/template-ast-parser'); describe('template lib check', function() { let parts = {}; let parseResults = {}; let filepath = path.resolve(__dirname, '../docs/check/success/index-lib-check-implements.cml'); let usingComponents = {}; let parsedScriptResults = {}; let parsedTemplateResults = {}; before(async function() { config.init(path.resolve(__dirname, '../docs/check/success')); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); }); it('should pass json ast parser', function() { usingComponents = jsonAstParser.getUsingComponents(parseResults.json.obj, filepath); }); it('should pass js ast parser', function() { parsedScriptResults = jsAstParser.getParseResults(parseResults.script.ast); }); it('should pass template parser', function() { parsedTemplateResults = templateAstParser.getParseResults(parseResults.template.ast, Object.keys(usingComponents)); }) }); ================================================ FILE: packages/chameleon-linter/test/template/checker/template-lib-export.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const linters = require('../../../linters'); const checkers = require('../../../checkers'); const utils = require('../../../utils'); const config = require('../../../config'); const jsonAstParser = require('../../../checkers/template/lib/json-ast-parser'); const jsAstParser = require('../../../checkers/template/lib/js-ast-parser'); const templateAstParser = require('../../../checkers/template/lib/template-ast-parser'); describe('template lib check', function() { let parts = {}; let parseResults = {}; let filepath = path.resolve(__dirname, '../docs/check/success/index-lib-check-export.cml'); let usingComponents = {}; let parsedScriptResults = {}; let parsedTemplateResults = {}; before(async function() { config.init(path.resolve(__dirname, '../docs/check/success')); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); }); it('should pass json ast parser', function() { usingComponents = jsonAstParser.getUsingComponents(parseResults.json.obj, filepath); }); it('should pass js ast parser', function() { parsedScriptResults = jsAstParser.getParseResults(parseResults.script.ast); }); it('should pass template parser', function() { parsedTemplateResults = templateAstParser.getParseResults(parseResults.template.ast, Object.keys(usingComponents)); }) }); ================================================ FILE: packages/chameleon-linter/test/template/checker/template-lib-js.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const utils = require('../../../utils'); const config = require('../../../config'); const linters = require('../../../linters'); const jsAstParser = require('../../../checkers/template/lib/js-ast-parser'); async function getParseResults(filepath) { let parts = {}; let parseResults = {}; filepath = path.resolve(__dirname, filepath); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); return {parseResults, filepath}; } describe('template lib js', function() { before(function() { config.init(path.resolve(__dirname)); }); it('should pass template lib js: class model', async function() { let filepath = '../docs/check/success/index-lib-js-class.cml'; let {parseResults} = await getParseResults(filepath); let results = jsAstParser.getParseResults(parseResults.script.ast); expect(results).to.have.deep.property('vars', ['show', 'dataOne', 'computedOne', 'computedTwo']); expect(results).to.have.deep.property('methods', ['onTap', 'onClick']); }); it('should pass template lib js: export model', async function() { let filepath = '../docs/check/success/index-lib-js-export.cml'; let {parseResults} = await getParseResults(filepath); let results = jsAstParser.getParseResults(parseResults.script.ast); expect(results).to.have.deep.property('vars', ['show', 'dataOne', 'computedOne', 'computedTwo', 'computedShow', 'getterHide']); expect(results).to.have.deep.property('methods', ['onTap', 'onClick']); }); }); ================================================ FILE: packages/chameleon-linter/test/template/checker/template-lib-json.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const utils = require('../../../utils'); const config = require('../../../config'); const linters = require('../../../linters'); const jsonAstParser = require('../../../checkers/template/lib/json-ast-parser'); async function getParseResults(filepath) { let parts = {}; let parseResults = {}; filepath = path.resolve(__dirname, filepath); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); return {parseResults, filepath}; } describe('template lib json', function() { let scrollerPath = path.resolve(__dirname, '../docs/components/scroller/scroller.interface'); let radioPath = path.resolve(__dirname, '../docs/components/radio/radio.cml'); before(async function() { config.init(path.resolve(__dirname)); }); it('should pass template lib json process: polymorphic model', async function() { let filepath = '../docs/check/success/index-lib-json-polymorphic.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let results = jsonAstParser.getUsingComponents(parseResults.json.obj, fullFilePath); expect(results).to.have.property('c-scroller'); expect(results['c-scroller']).to.have.deep.property('vars', ['cstyle', 'bottomOffset', 'scrollDirection']); expect(results['c-scroller']).to.have.deep.property('methods', ['customscroll', 'scrolltobottom']); expect(results['c-scroller']).to.have.nested.property('events[0].name', 'customscroll'); expect(results['c-scroller']).to.have.property('path', scrollerPath); }); it('should pass template lib json process: single file model', async function() { let filepath = '../docs/check/success/index-lib-json-single.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let results = jsonAstParser.getUsingComponents(parseResults.json.obj, fullFilePath); expect(results).to.have.property('c-radio'); expect(results['c-radio']).to.have.deep.property('vars', ['checked']); expect(results['c-radio']).to.have.deep.property('methods', ['check']); expect(results['c-radio']).to.have.nested.property('events[0].name', 'radiocheked'); expect(results['c-radio']).to.have.property('path', radioPath); }); it('should pass plugin:// prefix component test', async function() { let filepath = '../docs/check/success/index-lib-check-plugin.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let results = jsonAstParser.getUsingComponents(parseResults.json.obj, fullFilePath); expect(results).to.have.property('c-radio'); expect(results).to.not.have.property('c-plugin'); }) }); ================================================ FILE: packages/chameleon-linter/test/template/checker/template-lib-template.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const utils = require('../../../utils'); const config = require('../../../config'); const linters = require('../../../linters'); const jsonAstParser = require('../../../checkers/template/lib/json-ast-parser'); const templateAstParser = require('../../../checkers/template/lib/template-ast-parser'); async function getParseResults(filepath) { let parts = {}; let parseResults = {}; filepath = path.resolve(__dirname, filepath); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); return {parseResults, filepath}; } describe('template lib template', function() { before(async function() { config.init(path.resolve(__dirname)); }); it('should pass template ast parse process: cml', async function() { let filepath = '../docs/check/success/index-lib-template-cml.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let usingComponents = jsonAstParser.getUsingComponents(parseResults.json.obj, fullFilePath) let results = templateAstParser.getParseResults(parseResults.template.ast, { usingComponents: Object.keys(usingComponents), platform: 'cml' }); expect(results).to.have.deep.property('methods', [{ name: 'onTap', method: true, variable: false, pos: [8, 66] }, { name: 'onScroll', method: true, variable: false, pos: [8, 90] }], 'methods mismatch'); expect(results).to.have.deep.property('vars', [{ name: 'messages', method: false, variable: true, pos: [2, 18] }, { name: 'show', method: false, variable: true, pos: [5, 17] }, { name: 'childView', method: false, variable: true, pos: [6, 24] }, { name: 'child', method: false, variable: true, pos: [6, 36] }, { name: 'disX', method: false, variable: true, pos: [8, 46] }, { name: 'logicLeft', method: false, variable: true, pos: [9, 23] }, { name: 'logicRight', method: false, variable: true, pos: [9, 36] }], 'variables mismatch'); expect(results.customizedComponents[0]['show-scroller']).to.have.deep.property('props', [{ event: false, name: 'scrollX', rawName: 'scroll-x', prop: true, pos: [8, 34] }], 'customized component properties check fail'); expect(results.customizedComponents[0]['show-scroller']).to.have.deep.property('events', [{ name: 'tap', event: true, pos: [8, 61], prop: false }, { name: 'onscroll', event: true, pos: [8, 80], prop: false }]); }); it('should pass template ast parse process: vue', async function() { let filepath = '../docs/check/success/index-lib-template-vue.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let usingComponents = jsonAstParser.getUsingComponents(parseResults.json.obj, fullFilePath) let results = templateAstParser.getParseResults(parseResults.template.ast, { usingComponents: Object.keys(usingComponents), platform: 'vue' }); expect(results).to.have.deep.property('methods', [{ name: 'onTap', method: true, variable: false, pos: [8, 57] }, { name: 'onScroll', method: true, variable: false, pos: [8, 81] }], 'methods mismatch'); expect(results).to.have.deep.property('vars', [{ name: 'messages', method: false, variable: true, pos: [2, 32] }, { name: 'show', method: false, variable: true, pos: [5, 15] }, { name: 'childView', method: false, variable: true, pos: [6, 23] }, { name: 'child', method: false, variable: true, pos: [6, 35] }, { name: 'disX', method: false, variable: true, pos: [8, 41] }], 'variables mismatch'); expect(results.customizedComponents[0]['show-scroller']).to.have.deep.property('props', [{ event: false, name: 'scrollX', rawName: 'scroll-x', prop: true, pos: [8, 31] }], 'customized component properties check fail'); expect(results.customizedComponents[0]['show-scroller']).to.have.deep.property('events', [{ name: 'tap', event: true, pos: [8, 52], prop: false }, { name: 'onscroll', event: true, pos: [8, 71], prop: false }]); }); }); ================================================ FILE: packages/chameleon-linter/test/template/checker/template.test.js ================================================ const path = require('path'); const chai = require('chai'); const expect = chai.expect; const config = require('../../../config'); const linters = require('../../../linters'); const checkers = require('../../../checkers'); const utils = require('../../../utils'); async function getParseResults(filepath) { let parts = {}; let parseResults = {}; filepath = path.resolve(__dirname, filepath); parts = utils.getCmlParts(filepath); parseResults.script = await linters.scriptlint(parts.script); parseResults.json = await linters.jsonlint(parts.json); parseResults.template = await linters.templatelint(parts.template, parseResults.json.obj); return {parseResults, filepath}; } function resortIssues(issues = []) { return issues.sort((pre, next) => { return pre.line * 1000 + pre.column - next.line * 1000 - next.column; }); } describe('template check', function() { let scrollerPath = path.resolve(__dirname, '../docs/components/scroller/scroller.interface'); before(function() { config.init(path.resolve(__dirname)); }); describe('template language: cml', async function() { it('should report variable and method not defined error', async function() { let filepath = '../docs/check/fail/index-vars-methods-cml.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let templateChecker = new checkers.Template(fullFilePath, parseResults); expect(resortIssues(templateChecker.check())).to.be.an.instanceOf(Array).to.deep.equal([{ line: 2, column: 17, msg: 'variable: "showFirstView" is not defined.', token: 'showFirstView' }, { line: 2, column: 33, msg: 'variable: "show" is not defined.', token: 'show' }, { line: 2, column: 40, msg: 'variable: "hidden" is not defined.', token: 'hidden' }, { line: 3, column: 23, msg: 'method: "onTap" is not defined.', token: 'onTap' }]); }); it('should report props and events not defined error', async function() { let filepath = '../docs/check/fail/index-props-events-cml.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let templateChecker = new checkers.Template(fullFilePath, parseResults); expect(resortIssues(templateChecker.check())).to.be.an.instanceOf(Array).to.deep.equal([{ line: 2, column: 43, msg: `The property "top-offset" is not a property of component "show-scroller" which path is: ${scrollerPath}`, token: 'top-offset' }, { line: 2, column: 104, msg: `The event "scrolltotop" is not defined in component "show-scroller" which path is: ${scrollerPath}`, token: 'scrolltotop' }]); }); }); describe('template language: vue', async function() { it('should report variable and method not defined error', async function() { let filepath = '../docs/check/fail/index-vars-methods-vue.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let templateChecker = new checkers.Template(fullFilePath, parseResults); expect(resortIssues(templateChecker.check())).to.be.an.instanceOf(Array).to.deep.equal([{ line: 2, column: 15, msg: 'variable: "showFirstView" is not defined.', token: 'showFirstView' }, { line: 2, column: 31, msg: 'variable: "show" is not defined.', token: 'show' }, { line: 2, column: 38, msg: 'variable: "hidden" is not defined.', token: 'hidden' }, { line: 2, column: 65, msg: 'variable: "font" is not defined.', token: 'font' }, { line: 3, column: 21, msg: 'method: "onTap" is not defined.', token: 'onTap' }]); }); it('should report props and events not defined error', async function() { let filepath = '../docs/check/fail/index-props-events-vue.cml'; let {parseResults, filepath: fullFilePath} = await getParseResults(filepath); let templateChecker = new checkers.Template(fullFilePath, parseResults); expect(resortIssues(templateChecker.check())).to.be.an.instanceOf(Array).to.deep.equal([{ line: 4, column: 12, msg: `The property "top-offset" is not a property of component "show-scroller" which path is: ${scrollerPath}`, token: 'top-offset' }, { line: 6, column: 10, msg: `The event "scrolltotop" is not defined in component "show-scroller" which path is: ${scrollerPath}`, token: 'scrolltotop' }, { line: 7, column: 15, msg: `The property "data-show" is not a property of component "show-scroller" which path is: ${scrollerPath}`, token: 'data-show' }]); }); }); }); ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-props-events-cml.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-props-events-vue.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-props-methods-polymorphic.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-props-methods-single.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-vars-events.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-vars-methods-cml.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/fail/index-vars-methods-vue.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-check-class.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-check-export.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-check-implements.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-check-plugin.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-check-vue-class.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-js-class.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-js-export.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-json-polymorphic.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-json-single.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-template-cml.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/check/success/index-lib-template-vue.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/c-checkbox/c-checkbox.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/radio/radio.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/scroller/scroller.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/scroller/scroller.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/scroller/scroller.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/scroller/scroller.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/show-component/scroller.interface ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/show-component/scroller.web.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/show-component/scroller.weex.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/show-component/scroller.wx.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/docs/components/single/single.cml ================================================ ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/built-in-embed-rule-excludes-error.js ================================================ module.exports = [{ desc: 'should fail built-in components emebed rule: excludes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 1, msg: 'tag "scroller" can not have "textarea" as it\'s direct children or descendant(s), and element in this list: "textarea or video" is forbidden as well', token: '' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/built-in-embed-rule-includes-error.js ================================================ module.exports = [{ desc: 'should fail built-in components emebed rule: includes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 1, msg: 'tag "video" can only have "text" as it\'s direct children or descendant(s), therefor tag "textarea" is not allowed as it\'s direct children or descendant(s)', token: '' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/built-in-props-error.js ================================================ module.exports = [{ desc: 'should fail built-in props check', input: { part: { line: 0, rawContent: "", params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 9, msg: 'component "button" doesn\'t have a defined property named "close-type"', token: 'button' }, { line: 2, column: 38, msg: 'component "button" doesn\'t have a defined event named "up"', token: 'button' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/built-in-props-lang-vue-error.js ================================================ module.exports = [{ desc: 'should fail built-in props check with vue-like template language', input: { part: { line: 0, rawContent: "", params: { lang: 'vue' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 9, msg: 'component "button" doesn\'t have a defined property named "close-type"', token: 'button' }, { line: 2, column: 45, msg: 'component "button" doesn\'t have a defined event named "up"', token: 'button' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/directive-cml-error.js ================================================ module.exports = [{ desc: 'should fail directive check with cml template language', input: { part: { line: 0, rawContent: "", params: { lang: 'cml' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 7, msg: 'directive "v-if" is not allowed to be used in this template, as the template language is set to "cml"', token: 'v-if' }, { line: 2, column: 23, msg: 'directive "v-bind" is not allowed to be used in this template, as the template language is set to "cml"', token: 'v-bind' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/directive-vue-error.js ================================================ module.exports = [{ desc: 'should fail directive check with vue-like template language', input: { part: { line: 0, rawContent: "", params: { lang: 'vue' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 7, msg: 'directive "c-if" is not allowed to be used in this template, as the template language is set to "vue"', token: 'c-if' }, { line: 2, column: 23, msg: 'directive "c-bind" is not allowed to be used in this template, as the template language is set to "vue"', token: 'c-bind' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/index.js ================================================ const bulk = require('bulk-require'); const casesExports = bulk(__dirname, '!(index).js'); const entries = Object.keys(casesExports); module.exports = entries.reduce((cases, currentEntry) => { return cases.concat(casesExports[currentEntry]); }, []); ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/lang-error.js ================================================ module.exports = [{ desc: 'should fail template lang error', input: { part: { line: 0, rawContent: "", params: { lang: '' }, file: '/src/app.cml' }, jsonAst: {} }, output: { start: 0, messages: [{ line: 1, column: 16, msg: 'the tag template lang attribute: "web" is not valid', token: '' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/origin-component-cml-directive-forbiden-error.js ================================================ module.exports = [{ desc: 'should fail chamelon built-in directives check for origin components', input: { part: { line: 0, rawContent: "", params: { lang: 'cml' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 14, msg: 'tag "origin-view" is prefixed with "origin-" directive, so it\'s not allowed to use a chameleon built-in directive:"c-bind"', token: 'origin-view' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/origin-component-skip-rules-error.js ================================================ module.exports = [{ desc: 'should fail origin component skip emebed rule: excludes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 3, column: 11, msg: 'tag "text" can only have "text" as it\'s direct children or descendant(s), therefor tag "view" is not allowed as it\'s direct children or descendant(s)', token: '' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/platform-specific-tags-error.js ================================================ module.exports = [{ desc: 'should fail platform specific tags check', input: { part: { line: 0, rawContent: '', platformType: 'cml', params: { lang: 'cml' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 2, column: 1, msg: 'tag: "scroll-view" is either not allowed in this template or not referenced as a component', token: 'scroll-view' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/template-tag-error.js ================================================ module.exports = [{ desc: 'should fail template tag check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 1, column: 0, msg: 'Each template can only have one group of template tags', token: 'template' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/fail/usingcomponent-ref-error.js ================================================ module.exports = [{ desc: 'should fail usingComponents check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [{ line: 1, column: 11, msg: 'tag: "c-geerup" is either not allowed in this template or not referenced as a component', token: 'c-geerup' }] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/built-in-embed-rule-excludes-pass.js ================================================ module.exports = [{ desc: 'should pass built-in components emebed rule: excludes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/built-in-embed-rule-includes-pass.js ================================================ module.exports = [{ desc: 'should pass built-in components emebed rule: includes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/built-in-props-lang-vue-pass.js ================================================ module.exports = [{ desc: 'should pass built-in props check with vue-like template language', input: { part: { line: 0, rawContent: "", params: { lang: 'vue' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/built-in-props-pass.js ================================================ module.exports = [{ desc: 'should pass built-in props check', input: { part: { line: 0, rawContent: ["'].join('\n'), params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/component-is-pass.js ================================================ module.exports = [{ desc: 'should pass component is attribute check', input: { part: { line: 0, rawContent: ["'].join('\n'), params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/index.js ================================================ const bulk = require('bulk-require'); const passExports = bulk(__dirname, '!(index).js'); const entries = Object.keys(passExports); module.exports = entries.reduce((cases, currentEntry) => { if (currentEntry === 'tag-close') { return cases.concat(passExports[currentEntry]); } else { return cases; } }, []); ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/lang-pass.js ================================================ module.exports = [{ desc: 'should pass template lang error', input: { part: { line: 0, rawContent: "", params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/origin-component-skip-rules-pass.js ================================================ module.exports = [{ desc: 'origin component passes emebed rule: excludes check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/platform-specific-tags-pass.js ================================================ module.exports = [{ desc: 'should pass platform specific tags check', input: { part: { line: 0, rawContent: '', platformType: 'wx', params: { lang: 'cml' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/tag-close.js ================================================ module.exports = [{ desc: 'should pass tag-close check', input: { part: { line: 0, rawContent: ["'].join('\n'), params: { lang: '' } }, jsonAst: {} }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/cases/pass/usingcomponent-ref-pass.js ================================================ module.exports = [{ desc: 'should pass usingComponents check', input: { part: { line: 0, rawContent: '', params: { lang: '' } }, jsonAst: { base: { usingComponents: { 'c-geerup': 'path/to/geerup' } } } }, output: { start: 0, messages: [] } }]; ================================================ FILE: packages/chameleon-linter/test/template/linter/template.test.js ================================================ const chai = require('chai'); const path = require('path'); const expect = chai.expect; const templateLinter = require('../../../linters/template'); const config = require('../../../config'); let failCases = require('./cases/fail'); let passCases = require('./cases/pass'); describe('template lint', function() { before(function() { config.init(path.resolve(__dirname, '../docs')); }); describe('fail cases', function() { failCases.forEach(function(caseItem) { it(caseItem.desc, async function() { let results = await templateLinter(caseItem.input.part, caseItem.input.jsonAst); expect(results).to.have.property('messages').to.be.an.instanceOf(Array).to.deep.equal(caseItem.output.messages); }); }); }); describe('pass cases', function() { passCases.forEach(function(caseItem) { it(caseItem.desc, async function() { let results = await templateLinter(caseItem.input.part, caseItem.input.jsonAst); expect(results).to.have.property('messages').to.be.an.instanceOf(Array).to.deep.equal(caseItem.output.messages); }); }); }); }); ================================================ FILE: packages/chameleon-linter/test/utils.test.js ================================================ const utils = require('../utils'); const assert = require('chai').assert; describe('utils', function() { it('toDash', function() { let result = utils.toDash('abcDefGhi'); assert.equal(result, 'abc-def-ghi'); }); }); ================================================ FILE: packages/chameleon-linter/utils.js ================================================ const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); const groupBy = require('lodash.groupby'); const filter = require('lodash.filter'); const map = require('lodash.map'); const cliUtils = require('chameleon-tool-utils'); const config = require('./config'); const Message = require('./classes/message'); let isCmlComponent = (templatePath, usingPath) => { let currentWorkspace = config.getCurrentWorkspace(); let interfaceInfo = cliUtils.findInterfaceFile(currentWorkspace, templatePath, usingPath); let componentInfo = cliUtils.lintHandleComponentUrl(currentWorkspace, templatePath, usingPath); return !!interfaceInfo.filePath || (componentInfo && componentInfo.isCml); } let toSrcPath = (filePath = '') => { return (filePath && path.isAbsolute(filePath)) ? path.relative(config.getCurrentWorkspace(), filePath) : filePath; } /** * 转换成驼峰写法 * * @param {string} variable 变量名称 * @return {string} 变量驼峰命名 */ let toCamelCase = variable => variable.replace(/-(\w)/g, (all, letter) => letter.toUpperCase()); /** * * @param {String} str target string */ const toDash = (str) => { return str.replace(/([A-Z])/g, (match, caps) => { return '-' + caps.toLowerCase(); }); }; /** * 获取全部的部分 * * @param {string} content 内容 * @param {string} platform 平台 * @return {Array} 数组 */ let getCmlFileParts = (filepath, platform) => { let content = fs.readFileSync(filepath, 'utf8'); let parts = cliUtils.splitParts({content}); let result = {}; map(parts, (values, key) => { switch (key) { case 'script': if (values.length) { values.forEach(value => { // interface | json if (value.attrs && value.cmlType) { result[value.cmlType] = value; result[value.cmlType].type = value.cmlType; } else { result[key] = value; } }); } break; default: if (values.length) { result[key] = values[0]; } break; } map(result, (value, type) => { let params = {}; map(result[type].attrs, (attr, key) => { params[toCamelCase(key)] = attr; }); Object.assign(result[type], { params: params, line: result[type].startLine, file: filepath, platformType: platform, rawContent: result[type].tagContent }); }); }); return result; }; /** * 获取所有的片段 * * @param {string} filepath 文件路径 * @return {Objhect} 文件片段表 */ let getCmlParts = filepath => { let parts = {}; let platforms = config.getPlatforms(); let platform; let result = new RegExp('([^/]*?)\.(' + platforms.join('|') + ')\.cml$', 'g').exec(filepath); if (result) { let interfaceFile = filepath.replace(new RegExp('\.(' + platforms.join('|') + ')\.cml$', 'ig'), '.interface'); platform = result[2]; if (fs.existsSync(interfaceFile)) { Object.assign(parts, getCmlFileParts(interfaceFile)); } } Object.assign(parts, getCmlFileParts(filepath, platform)); return parts; }; let getInterfaceParts = filepath => { let _result = { parts: {}, messages: [] }; let lastFilePath = filepath; _retrieveParts(filepath); function _retrieveParts(interfaceFilePath) { // terminate condition if (!fs.existsSync(interfaceFilePath)) { _result.messages.push(new Message({ msg: `The file: "${toSrcPath(interfaceFilePath)}" referenced by file: "${toSrcPath(lastFilePath)}" was not found` })); return; } const content = fs.readFileSync(interfaceFilePath, 'utf8'); const parts = cliUtils.splitParts({content}); // search parts.script array for interface defination and platform specific definations. if (parts.script) { parts.script.forEach(item => { let errMsg = null; let extraPartInfo = { params: {}, line: item.startLine, file: interfaceFilePath, rawContent: item.tagContent, platformType: item.cmlType }; // for interface portion we should keep the origin filepath if (item.cmlType === 'interface') { extraPartInfo.file = filepath; } // check src references for platform definations if (item.cmlType != 'interface' && item.attrs && item.attrs.src) { const targetScriptPath = path.resolve(path.dirname(interfaceFilePath), item.attrs.src); // the referenced source is a js file if (/.js$/.test(item.attrs.src)) { if (!fs.existsSync(targetScriptPath)) { errMsg = new Message({ line: item.line, column: item.tagContent.indexOf(item.attrs.src) + 1, token: item.attrs.src, msg: `The javascript file: "${toSrcPath(targetScriptPath)}" specified with attribute src was not found` }); } else { extraPartInfo.content = extraPartInfo.rawContent = extraPartInfo.tagContent = fs.readFileSync(targetScriptPath, 'utf8'); extraPartInfo.file = targetScriptPath; } } // the referenced source is a cml file if (/.cml$/.test(item.attrs.src)) { if (!fs.existsSync(targetScriptPath)) { errMsg = new Message({ line: item.line, column: item.tagContent.indexOf(item.attrs.src) + 1, token: item.attrs.src, msg: `The cml file: "${toSrcPath(targetScriptPath)}" specified with attribute src was not found` }); } else { const cmlFileContent = fs.readFileSync(targetScriptPath, 'utf8'); const cmlParts = cliUtils.splitParts({content: cmlFileContent}); const scriptPart = cmlParts.script ? cmlParts.script.filter(part => { return part.type === 'script'; }) : null; if (scriptPart && scriptPart.length) { extraPartInfo.content = extraPartInfo.rawContent = extraPartInfo.tagContent = scriptPart[0].content; extraPartInfo.file = targetScriptPath; item.cmlType = 'script'; } else { errMsg = new Message({ line: item.line, column: item.tagContent.indexOf(item.attrs.src) + 1, token: item.attrs.src, msg: `The referenced file: "${toSrcPath(targetScriptPath)}" may not has a script portion` }); } } } } // previous cmlType defination has a higher priority. if (!errMsg && !_result.parts[item.cmlType]) { _result.parts[item.cmlType] = {...item, ...extraPartInfo}; } if (errMsg) { _result.messages.push(errMsg); } }); } // search parts.customBlocks array for include defination which may contains another interface file. let include = null; if (parts.customBlocks) { parts.customBlocks.forEach(item => { if (item.type === 'include') { include = item; } }); } if (include && include.attrs && include.attrs.src) { let newFilePath = path.resolve(path.dirname(interfaceFilePath), include.attrs.src); lastFilePath = interfaceFilePath; return _retrieveParts(newFilePath); } return; } return _result; } /** * 输出出错信息 * * @param {Object} result 出错信息对象 * @param {string} filepath 文件路径 */ let outputWarnings = (result) => { let flag = false; result = filter(Object.values(result), (item) => { if (!item.messages || (item.messages && item.messages.length == 0)) { return false; } flag = true; return true; }); result = groupBy(result, 'file'); for (let key of Object.keys(result)) { if (key !== 'undefined') { console.log(chalk.yellow('[file]') + ': ' + key); } result[key] .sort((a, b) => { return a.start - b.start; }) .forEach((item) => { if (item.type) { console.log(chalk.red(item.type + (item.platform ? (' [' + item.platform + ']') : '') + ' errors:')); } item.messages .sort((preMsg, nextMsg) => { if (preMsg.line == undefined || preMsg.column == undefined || nextMsg.line == undefined || nextMsg.column == undefined) { return 0; } return (preMsg.line - nextMsg.line) * 10000 + (preMsg.column - nextMsg.column); }) .forEach((message) => { if (message.line !== undefined && item.start !== undefined && message.column !== undefined) { console.log('[' + chalk.cyan(message.line + item.start - 1) + ' (line), ' + chalk.cyan(message.column) + ' (column)]' + ' ' + message.msg); } else { console.log(message.msg); } }); }); // 保留!!!需要输出一个空行 console.log(''); } return flag; }; module.exports = { getCmlParts, getInterfaceParts, outputWarnings, toDash, isCmlComponent, toSrcPath }; ================================================ FILE: packages/chameleon-loader/.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, "spread": true, "restParams": true, "objectLiteralComputedProperties": true, "destructuring": 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/chameleon-loader/.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/ ================================================ FILE: packages/chameleon-loader/.npmignore ================================================ .scripts/ test/ gulpfile.js ================================================ FILE: packages/chameleon-loader/.scripts/version.js ================================================ /** * 用于钉钉推送版本信息 */ module.exports = { } ================================================ FILE: packages/chameleon-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/chameleon-loader/gulpfile.js ================================================ const gulp = require('gulp'); const uglify = require('gulp-uglify-es').default; const through = require('through2'); const path = require('path'); const devPath = path.join(__dirname,'../chameleon-cli/node_modules/chameleon-loader/src') const distPathMap = { build: { dist: 'dist', interface: 'dist/interface-check' }, dev: { dist: devPath, interface: path.join(devPath,'interface-check') } } let EntryTask = gulp.env._[0]; gulp.task('before', function () { gulp.src([ 'lib/**/**.js', '!lib/interface-check/**/**.js' ]) .pipe(through.obj(function (file, encode, cb) { var result = file.contents.toString() result = result.replace(/@didi\/chameleon\-loader\/lib\//g,'chameleon-loader/src/'); // // 再次转为Buffer对象,并赋值给文件内容 file.contents = new Buffer(result) // 以下是例行公事 this.push(file) cb() })) .pipe(uglify({ parse: { bare_returns: true, }, mangle: { toplevel: true }, compress: { drop_console: true, drop_debugger: true } })) .pipe(gulp.dest(distPathMap[EntryTask].dist)); }); gulp.task('build', ['before'],function () { gulp.src('lib/interface-check/**/**.js') .pipe(gulp.dest(distPathMap[EntryTask].interface)); }); gulp.task('dev', ["build"], function() { gulp.watch('lib/**/**.js',['build']); }) ================================================ FILE: packages/chameleon-loader/package.json ================================================ { "name": "chameleon-loader", "version": "1.0.8", "description": "chameleon cml文件处理loader", "main": "src/index.js", "directories": { "lib": "lib", "test": "test" }, "scripts": { "test": "mocha --recursive --reporter spec", "cover": "istanbul cover --report lcov node_modules/mocha/bin/_mocha -- -R spec --recursive", "eslint:fix": "eslint --ext .js lib --fix", "eslint": "eslint ./lib" }, "dependencies": { "@babel/parser": "7.1.2", "@babel/types": "^7.3.4", "babel-generator": "6.26.1", "babel-traverse": "6.26.0", "chameleon-template-parse": "1.0.8", "chameleon-tool-utils": "1.0.8", "consolidate": "0.14.0", "de-indent": "1.0.2", "fs-extra": "^7.0.1", "hash-sum": "1.0.2", "he": "1.1.0", "loader-utils": "1.1.0", "lru-cache": "4.1.1", "mvvm-interface-parser": "1.0.8", "resolve": "1.4.0", "runtime-check": "1.0.8", "source-map": "0.6.1", "vue-style-loader": "4.0.1" }, "devDependencies": { "autoprefixer": "^7.2.5", "babel-core": "^6.25.0", "babel-loader": "^7.0.0", "babel-preset-env": "^1.6.0", "chai": "^4.1.0", "coffee-loader": "^0.7.2", "coffee-script": "^1.12.7", "conventional-changelog-cli": "^1.3.22", "coveralls": "^2.11.9", "css-loader": "^0.28.4", "eslint": "^3.19.0", "eslint-plugin-vue-libs": "^1.2.0", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^1.1.5", "gulp": "^3.9.1", "gulp-uglify-es": "^1.0.4", "istanbul": "^0.4.5", "js-yaml": "^3.9.1", "lint-staged": "^4.0.2", "marked": "^0.3.12", "memory-fs": "^0.4.1", "mkdirp": "^0.5.1", "mocha": "^4.0.1", "node-libs-browser": "^2.0.0", "normalize-newline": "^3.0.0", "null-loader": "^0.1.1", "pug": "^2.0.0-rc.2", "raw-loader": "^0.5.1", "skeleton-loader": "1.1.3", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "sugarss": "^1.0.0", "through2": "^3.0.0", "url-loader": "^0.6.2", "vue": "^2.5.0", "vue-server-renderer": "^2.5.0", "webpack": "^3.0.0", "webpack-merge": "^4.1.1", "yorkie": "^1.0.3" }, "author": "Chameleon-Team", "license": "Apache", "mail": "ChameleonCore@didiglobal.com", "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-loader/src/cml-compile/handle-platform-css.js ================================================ const postcss = require('postcss'); // 原生组件引用的样式 @import './xxx' 不带平台样式后缀,less会默认添加less导致无法识别需要人工处理 const handleCssSuffix = postcss.plugin('handleCssSuffix', function(options) { let {ext} = options; return function(css, result) { css.walkAtRules('import', function (rule) { let params = rule.params;// ./xxx 引用的样式文件路径 // 注意这里取到的值是带引号的,比如 "./xxx" './xxx' `./xxx` params = params.replace(/"|'|`/g, ''); if (!params.endsWith(ext)) { rule.params = `"${params}${ext}"` } }); }; }) module.exports = function(source, options = {}) { return postcss([handleCssSuffix(options)]).process(source).css; } ================================================ FILE: packages/chameleon-loader/src/cml-compile/json-handle.js ================================================ const cmlUtils = require('chameleon-tool-utils'); module.exports = function(loaderContext, jsonObject, cmlType, componentDeps) { var jsonPath = loaderContext.resourcePath.replace(/(\.cml|\.wx\.cml|\.alipay\.cml|\.baidu\.cml|\.qq\.cml|\.tt\.cml)$/, '.json') var context = (loaderContext._compiler && loaderContext._compiler.context) || loaderContext.options.context || process.cwd() if (jsonObject.usingComponents) { let components = jsonObject.usingComponents; Object.keys(components).forEach(key => { let comPath = components[key]; let splitInfo = comPath.split('?'); comPath = splitInfo[0]; let {filePath, refUrl} = cmlUtils.handleComponentUrl(context, loaderContext.resourcePath, comPath, cmlType); if (filePath) { componentDeps.push(filePath); components[key] = refUrl; } else { // plugin开头的小程序插件不做处理 if (components[key].indexOf('plugin://') !== 0) { delete components[key]; cmlUtils.log.error(`can't find component:${refUrl} in ${loaderContext.resourcePath}`); } } }) } cmlUtils.addNpmComponents(jsonObject, jsonPath, cmlType, context); return jsonObject; } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/app.js ================================================ import _cml_ from 'chameleon-runtime'; module.exports = function(){ _cml_.createApp(exports.default).getOptions(); } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/common/util.js ================================================ /* eslint-disable */ /** * 原型上的方法放到对象上 * @param {Object} obj 待添加属性对象 * @param {Object} proto 差异方法 * @return {Object} 修改后值 */ export function copyProtoProperty(obj = {}) { let EXPORT_OBJ = obj; let EXPORT_PROTO = EXPORT_OBJ.__proto__; if (EXPORT_PROTO.constructor !== Object) { Object.getOwnPropertyNames(EXPORT_PROTO).forEach(key => { if (!/constructor|prototype|length/ig.test(key)) { // 原型上有自身没有的属性 放到自身上 if (!EXPORT_OBJ.hasOwnProperty(key)) { EXPORT_OBJ[key] = EXPORT_PROTO[key] } } }) } return EXPORT_OBJ; } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/component.js ================================================ import _cml_ from 'chameleon-runtime'; module.exports = function(){ _cml_.createComponent(exports.default).getOptions(); } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/index.js ================================================ const fs = require('fs'); const path = require('path'); const _ = module.exports = {} ; _.getMiniAppRunTimeSnippet = function (platform, type) { return fs.readFileSync(path.join(__dirname, `./${type}.js`)) } _.getVueRunTimeSnippet = function (platform, type) { return fs.readFileSync(path.join(__dirname, `./web/${type}.js`)) } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/page.js ================================================ import _cml_ from 'chameleon-runtime'; module.exports = function(){ _cml_.createPage(exports.default).getOptions(); } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/web/app.js ================================================ import _cml_ from 'chameleon-runtime'; exports.default = _cml_.createApp(exports.default).getOptions() ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/web/component.js ================================================ import _cml_ from 'chameleon-runtime'; exports.default = _cml_.createComponent(exports.default).getOptions() ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/web/page.js ================================================ import _cml_ from 'chameleon-runtime'; exports.default = _cml_.createPage(exports.default).getOptions() ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/web/util.js ================================================ /* eslint-disable */ /** * 对象属性 `data` 的 映射 * @param {[type]} obj [description] * @return {[type]} [description] */ export function genConfig(obj) { Object.getOwnPropertyNames(obj).forEach(function(key) { if (key === 'data') { var _temp = obj['data'] obj['data'] = function() { return { ..._temp } } return obj } }) return obj } /** * 添加处理动态style的方法 * @param {*} obj */ // export function addStyleHandle(obj) { // obj.methods = obj.methods || {} // obj.methods['__parseStyle'] = function(content) { // return content // } // } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/weex/util.js ================================================ /* eslint-disable */ /** * 对象属性 `data` 的 映射 * @param {[type]} obj [description] * @return {[type]} [description] */ export function genConfig(obj) { Object.getOwnPropertyNames(obj).forEach(function(key) { if (key === 'data') { var _temp = obj['data'] obj['data'] = function() { return { ..._temp } } return obj } }) return obj } /** * 添加处理动态style的方法 * @param {*} obj */ // export function addStyleHandle(obj) { // obj.methods = obj.methods || {} // obj.methods[weexStyle.methodKey] = function(content) { // return weexStyle.weexStaticStyle(content) // } // } ================================================ FILE: packages/chameleon-loader/src/cml-compile/runtime/wx/util.js ================================================ // const wxStyle = require('chameleon-loader/lib/cml-compile/wx-style-handle.js') function type(n) { return Object.prototype.toString.call(n).slice(8, -1) } function middleware(obj, map, lifecycle) { for (let key of Object.keys(map)) { if (obj.hasOwnProperty(key)) { obj['_' + key] = obj[key] map['_' + key] = map[key] delete obj[key] delete map[key] } } } /** * 生命周期映射 * @param {Object} obj 待添加属性对象 * @param {Object} map 映射表 * @param {Object} lifecycle 生命周期序列 确保顺序遍历 * @return {Object} 修改后值 */ export function lifecycleHandler(obj, map, lifecycle) { // 将生命周期 键名 处理成 ['_' + key] lifecycle = lifecycle.map(key => '_' + key) middleware(obj, map) lifecycle.forEach(function(key) { var mapVal = map[key] var objVal = obj[key] if (obj.hasOwnProperty(key)) { if (obj.hasOwnProperty(mapVal)) { if (type(obj[mapVal]) !== 'Array') { obj[mapVal] = [obj[mapVal], objVal] } else { obj[mapVal].push(objVal) } } else { obj[mapVal] = [objVal] } delete obj[key] } }) return obj } /** * 对象键名重定义 * @param {Object} obj 对象 * @param {String} oldKey 原键名 * @param {String} newKey 新键名 * @return {Object} 新对象 */ export function rename(obj, oldKey, newKey) { Object.getOwnPropertyNames(obj).forEach(function(key) { if (key === oldKey) { obj[newKey] = obj[key] delete obj[key] return obj } }) return obj } /** * 处理组件props属性 * @param {Object} obj 组件options * @return {[type]} [description] */ export function propsHandler(obj) { if (obj.props) { Object.getOwnPropertyNames(obj.props).forEach(function(name) { let prop = obj.props[name] if (type(prop) === 'Object' && prop.hasOwnProperty('default')) { rename(prop, 'default', 'value') } }) rename(obj, 'props', 'properties') } return obj } /** * 添加处理动态style的方法 * @param {*} obj */ // export function addStyleHandle(obj) { // obj.methods = obj.methods || {} // obj.methods[wxStyle.methodKey] = function(content) { // return wxStyle.wxStaticStyle(content) // } // } ================================================ FILE: packages/chameleon-loader/src/cml-compile/wxml-selector.js ================================================ /* 这个文件用来处理原生小程序组件*/ const loaderUtils = require('loader-utils'); const fs = require('fs'); const cmlUtils = require('chameleon-tool-utils'); const path = require('path'); const handlePlatformCss = require('./handle-platform-css.js'); module.exports = function(content) { const self = this;// eslint-disable-line const resource = this.resourcePath; const extName = path.extname(resource); const cssExt = { '.wxml': '.wxss', '.axml': '.acss', '.swan': '.css', '.qml': '.qss', '.ttml': '.ttss' } const styles = cssExt[extName]; const query = loaderUtils.getOptions(this) || {} const type = query.type; let targetFilePath = ''; let output = ''; let extMap = { script: '.js', styles } // targetFilePath = resource.replace(/\.wxml/, extMap[type]); targetFilePath = resource.replace(new RegExp(`${extName}$`), extMap[type]); if (!cmlUtils.isFile(targetFilePath)) { throw new Error(`can't find ${targetFilePath}`) } else { self.addDependency(targetFilePath); output = fs.readFileSync(targetFilePath, {encoding: 'utf-8'}) } if (type === 'script') { return `${output}\n; module.exports = function(){\n }` } if (output) { output = handlePlatformCss(output, {filePath: this.resourcePath, ext: styles}) } return output; } ================================================ FILE: packages/chameleon-loader/src/helpers.js ================================================ const querystring = require('querystring') const loaderUtils = require('loader-utils') const normalize = require('./utils/normalize') const selectorPath = normalize.lib('selector') const wxmlSelectorPath = normalize.lib('cml-compile/wxml-selector') // dep loaders const styleLoaderPath = normalize.dep('vue-style-loader') // check whether default js loader exists 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 ( options, moduleId, isProduction, hasScoped, hasComment, hasFunctionalTemplate, needCssSourceMap, loaderContext ) { const JSLoader = (loaderContext._compiler.options.module.rules || []).find((rule) => (rule.test instanceof RegExp && rule.test.test('name.js'))) const JSLoaderConfig = (JSLoader.use || []).find((item) => item.loader === 'babel-loader') options.loaders && (options.loaders.js = JSLoaderConfig) 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 defaultLoaders = { css: styleLoaderPath + '!' + 'css-loader' + cssLoaderOptions, js: 'babel-loader' } return { defaultLoaders, loaders: Object.assign({}, defaultLoaders, options.loaders), preLoaders: options.preLoaders || {}, postLoaders: options.postLoaders || {} } } module.exports = function createHelpers ( loaderContext, options, moduleId, parts, isProduction, hasScoped, hasComment, hasFunctionalTemplate, needCssSourceMap, fileType ) { // console.log(fileType) const rawRequest = getRawRequest(loaderContext, options.excludedPreLoaders) const { defaultLoaders, loaders, preLoaders, postLoaders } = resolveLoaders( options, moduleId, isProduction, hasScoped, hasComment, hasFunctionalTemplate, needCssSourceMap, loaderContext ) function getRequire (type, part, index, scoped) { return 'require(' + getRequestString(type, part, index, scoped) + ')' } function getWxmlRequest(type) { return 'require(' + getWxmlRequestString(type) + ')' } function getWxmlRequestString(type) { const part = {} const index = 0; const scoped = false; let loaderString = '!!' + // get loader string for pre-processors getLoaderString(type, part, index, scoped); if (options.afterWxSelector) { loaderString += // 微信端在selector 拼接差异化代码后做replace stringifyLoaders(options.afterWxSelector) + '!' ; } loaderString += // select the corresponding part from the vue file `${wxmlSelectorPath}?type=${type}!` + // the url to the actual vue file, including remaining requests rawRequest; return loaderUtils.stringifyRequest( loaderContext, loaderString ) } 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) { let loaderString = '!!' + // get loader string for pre-processors getLoaderString(type, part, index, scoped); if (options.afterWxSelector) { loaderString += // 微信端在selector 拼接差异化代码后做replace stringifyLoaders(options.afterWxSelector) + '!' ; } loaderString += // select the corresponding part from the vue file getSelectorString(type, index || 0) + // the url to the actual vue file, including remaining requests rawRequest; return loaderUtils.stringifyRequest( loaderContext, loaderString ) } 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 ================================================ FILE: packages/chameleon-loader/test/src/parser.test.js ================================================ const parser = require('../../src/parser.js'); const expect = require('chai').expect; const fs = require('fs'); const path = require('path'); let source = fs.readFileSync(path.resolve(__dirname,'./parser.cml'),'utf-8'); describe('parser.js',function(){ it('test parser',function(){ let result = parser(source); expect(result.template.length).to.equal(1); expect(result.script.length).to.equal(2); expect(result.style.length).to.equal(1); }) }) ================================================ FILE: packages/chameleon-loader/test/src/project/src/components/coma.cml ================================================ ================================================ FILE: packages/chameleon-loader/test/src/project/src/pages/pagea.cml ================================================ ================================================ FILE: packages/chameleon-loader/test/src/utils.test/normalize.test.js ================================================ const normalize = require('../../../src/utils/normalize.js'); const expect = require('chai').expect; const fs = require('fs'); const path = require('path'); describe('normalize.js',function(){ it('test dep function:process.env.VUE_LOADER_TEST=true',function(){ process.env.VUE_LOADER_TEST = true; let dep = 'test-dep' let result = normalize.dep(dep); expect(result).to.be.equal(dep); }); it('test dep function:process.env.VUE_LOADER_TEST=fasle',function(){ process.env.VUE_LOADER_TEST = false; let dep = 'test-dep' let result = normalize.dep(dep); expect(result).to.be.equal(dep); }); }) ================================================ FILE: packages/chameleon-loader/test/src/utils.test/prehandle.test.js ================================================ const prehandle = require('../../../src/utils/prehandle.js'); const expect = require('chai').expect; let content1 = ` ` let content2 = ` ` let self = { resourcePath: 'User/yyl/1.cml' } describe('prehandle.js', function() { it('injectWeexBaseStyle', function() { let result1 = prehandle.injectWeexBaseStyle(content1, self); console.log(result1) expect(!!~result1.indexOf(`@import 'chameleon-runtime/src/platform/weex/style/index.css';`)).to.be.equal(true); }); it('webAddStyleScope', function() { let result1 = prehandle.webAddStyleScope(content1, self); console.log(result1) expect(!!~result1.indexOf(`scoped`)).to.be.equal(true); let result2 = prehandle.webAddStyleScope(content2, self); console.log(result2) expect(result2 === content2).to.be.equal(true); }); }) ================================================ FILE: packages/chameleon-loader/test/src/utils.test/try-require.test.js ================================================ const try_require = require('../../../src/utils/try-require.js'); const expect = require('chai').expect; const fs = require('fs'); const path = require('path'); describe('try_require.js',function(){ it('test try require',function(){ let result = !!try_require('@babel/parser'); expect(result).to.be.ok }) }) ================================================ FILE: packages/chameleon-miniapp-target/.eslintrc ================================================ { // 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写 "globals": { "cml": false }, // 环境定义了预定义的全局变量。 "env": { //环境定义了预定义的全局变量。更多在官网查看 "browser": true, "node": true, "commonjs": true, "amd": true, "es6": true, "mocha": true }, // JavaScript 语言选项 "parserOptions": { // ECMAScript 版本 "ecmaVersion": 6, "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/chameleon-miniapp-target/.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/chameleon-miniapp-target/.npmignore ================================================ .scripts/ test/ gulpfile.js ================================================ FILE: packages/chameleon-miniapp-target/gulpfile.js ================================================ const gulp = require('gulp'); const uglify = require('gulp-uglify-es').default; gulp.task('build', function () { gulp.src('src/**/**.js') .pipe(uglify({ parse: { bare_returns: true, }, mangle: { toplevel: true }, compress: { drop_console: true, drop_debugger: true } })) .pipe(gulp.dest('dist/')); }); ================================================ FILE: packages/chameleon-miniapp-target/package.json ================================================ { "name": "chameleon-miniapp-target", "version": "1.0.8", "description": "chameleon 小程序webpack的target", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\"" }, "author": "Chameleon-Team", "license": "Apache", "devDependencies": { "gulp": "^3.9.1", "gulp-uglify-es": "^1.0.4", "webpack": "^3.12.0" }, "dependencies": { "chameleon-tool-utils": "1.0.8" }, "mail": "ChameleonCore@didiglobal.com", "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-miniapp-target/src/JsonpChunkTemplatePlugin.js ================================================ "use strict"; const ConcatSource = require("webpack-sources").ConcatSource; const cmlUtils = require('chameleon-tool-utils'); class JsonpChunkTemplatePlugin { apply(chunkTemplate) { chunkTemplate.plugin("render", function(modules, chunk) { const jsonpFunction = this.outputOptions.jsonpFunction; const source = new ConcatSource(); const chunkNameLength = cmlUtils.handleWinPath(chunk.name).split('/').length; // 所有chunk都在static/js下,求该chunk相对manifest.js的路径 const manifestPath = [ 'manifest.js' ] if (chunkNameLength === 1) { manifestPath.unshift('./'); } else { let prefix = ''; // 如果是3 ../../ for (let i = 1; i < chunkNameLength; i++) { prefix += '../' } manifestPath.unshift(prefix); } source.add(`var __CML__GLOBAL = require("${manifestPath.join('')}");\n`); source.add(`__CML__GLOBAL.${jsonpFunction}(${JSON.stringify(chunk.ids)},`); source.add(modules); const entries = [chunk.entryModule].filter(Boolean).map(m => m.id); if(entries.length > 0) { source.add(`,${JSON.stringify(entries)}`); } source.add(")\n"); if(chunk.name !== 'common'){ source.add(`module.exports = __CML__GLOBAL.__CMLCOMPONNETS__['${chunk.name}']`) } return source; }); chunkTemplate.plugin("hash", function(hash) { hash.update("JsonpChunkTemplatePlugin"); hash.update("3"); hash.update(`${this.outputOptions.jsonpFunction}`); hash.update(`${this.outputOptions.library}`); }); } } module.exports = JsonpChunkTemplatePlugin; ================================================ FILE: packages/chameleon-miniapp-target/src/JsonpHotUpdateChunkTemplatePlugin.js ================================================ "use strict"; const ConcatSource = require("webpack-sources").ConcatSource; class JsonpHotUpdateChunkTemplatePlugin { apply(hotUpdateChunkTemplate) { hotUpdateChunkTemplate.plugin("render", function(modulesSource, modules, removedModules, hash, id) { const source = new ConcatSource(); source.add(`${this.outputOptions.hotUpdateFunction}(${JSON.stringify(id)},`); source.add(modulesSource); source.add(")"); return source; }); hotUpdateChunkTemplate.plugin("hash", function(hash) { hash.update("JsonpHotUpdateChunkTemplatePlugin"); hash.update("3"); hash.update(`${this.outputOptions.hotUpdateFunction}`); hash.update(`${this.outputOptions.library}`); }); } } module.exports = JsonpHotUpdateChunkTemplatePlugin; ================================================ FILE: packages/chameleon-miniapp-target/src/JsonpMainTemplatePlugin.js ================================================ "use strict"; const Template = require("webpack/lib/Template"); const ConcatSource = require("webpack-sources").ConcatSource; class JsonpMainTemplatePlugin { apply(mainTemplate) { mainTemplate.plugin("local-vars", function(source, chunk) { if(chunk.chunks.length > 0) { return this.asString([ source, "", "// objects to store loaded and loading chunks", "var installedChunks = {", this.indent( chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n") ), "};" ]); } return source; }); mainTemplate.plugin("jsonp-script", function(_, chunk, hash) { const chunkFilename = this.outputOptions.chunkFilename; const chunkMaps = chunk.getChunkMaps(); const crossOriginLoading = this.outputOptions.crossOriginLoading; const chunkLoadTimeout = this.outputOptions.chunkLoadTimeout; const scriptSrcPath = this.applyPluginsWaterfall("asset-path", JSON.stringify(chunkFilename), { hash: `" + ${this.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`, chunk: { id: "\" + chunkId + \"", hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, hashWithLength(length) { const shortChunkHashMap = Object.create(null); Object.keys(chunkMaps.hash).forEach(chunkId => { if(typeof chunkMaps.hash[chunkId] === "string") shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length); }); return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`; }, name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "` } }); return this.asString([ "var script = document.createElement('script');", "script.type = 'text/javascript';", "script.charset = 'utf-8';", "script.async = true;", `script.timeout = ${chunkLoadTimeout};`, crossOriginLoading ? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)};` : "", `if (${this.requireFn}.nc) {`, this.indent(`script.setAttribute("nonce", ${this.requireFn}.nc);`), "}", `script.src = ${this.requireFn}.p + ${scriptSrcPath};`, `var timeout = setTimeout(onScriptComplete, ${chunkLoadTimeout});`, "script.onerror = script.onload = onScriptComplete;", "function onScriptComplete() {", this.indent([ "// avoid mem leaks in IE.", "script.onerror = script.onload = null;", "clearTimeout(timeout);", "var chunk = installedChunks[chunkId];", "if(chunk !== 0) {", this.indent([ "if(chunk) {", this.indent("chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));"), "}", "installedChunks[chunkId] = undefined;" ]), "}" ]), "};", ]); }); mainTemplate.plugin("require-ensure", function(_, chunk, hash) { return this.asString([ "var installedChunkData = installedChunks[chunkId];", "if(installedChunkData === 0) {", this.indent([ "return new Promise(function(resolve) { resolve(); });" ]), "}", "", "// a Promise means \"currently loading\".", "if(installedChunkData) {", this.indent([ "return installedChunkData[2];" ]), "}", "", "// setup Promise in chunk cache", "var promise = new Promise(function(resolve, reject) {", this.indent([ "installedChunkData = installedChunks[chunkId] = [resolve, reject];" ]), "});", "installedChunkData[2] = promise;", "", "// start chunk loading", "var head = document.getElementsByTagName('head')[0];", this.applyPluginsWaterfall("jsonp-script", "", chunk, hash), "head.appendChild(script);", "", "return promise;" ]); }); mainTemplate.plugin("require-extensions", function(source, chunk) { if(chunk.chunks.length === 0) return source; return this.asString([ source, "", "// on error function for async loading", `${this.requireFn}.oe = function(err) { console.error(err); throw err; };` ]); }); mainTemplate.plugin("bootstrap", function(source, chunk, hash) { if(chunk.chunks.length > 0) { var jsonpFunction = this.outputOptions.jsonpFunction; return this.asString([ source, "", "// install a JSONP callback for chunk loading", `var parentJsonpFunction = __CML__GLOBAL[${JSON.stringify(jsonpFunction)}];`, `__CML__GLOBAL[${JSON.stringify(jsonpFunction)}] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {`, this.indent([ "// add \"moreModules\" to the modules object,", "// then flag all \"chunkIds\" as loaded and fire callback", "var moduleId, chunkId, i = 0, resolves = [], result;", "for(;i < chunkIds.length; i++) {", this.indent([ "chunkId = chunkIds[i];", "if(installedChunks[chunkId]) {", this.indent("resolves.push(installedChunks[chunkId][0]);"), "}", "installedChunks[chunkId] = 0;" ]), "}", "for(moduleId in moreModules) {", this.indent([ "if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {", this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), "}" ]), "}", "if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);", "while(resolves.length) {", this.indent("resolves.shift()();"), "}", this.entryPointInChildren(chunk) ? [ "if(executeModules) {", this.indent([ "for(i=0; i < executeModules.length; i++) {", this.indent(`result = ${this.requireFn}(${this.requireFn}.s = executeModules[i]);`), "}" ]), "}", "return result;", ] : "" ]), "};" ]); } return source; }); mainTemplate.plugin("hot-bootstrap", function(source, chunk, hash) { const hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename; const hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename; const hotUpdateFunction = this.outputOptions.hotUpdateFunction; const currentHotUpdateChunkFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateChunkFilename), { hash: `" + ${this.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "`, chunk: { id: "\" + chunkId + \"" } }); const currentHotUpdateMainFilename = this.applyPluginsWaterfall("asset-path", JSON.stringify(hotUpdateMainFilename), { hash: `" + ${this.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${this.renderCurrentHashCode(hash, length)} + "` }); const runtimeSource = Template.getFunctionContent(require("./JsonpMainTemplate.runtime.js")) .replace(/\/\/\$semicolon/g, ";") .replace(/\$require\$/g, this.requireFn) .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename) .replace(/\$hash\$/g, JSON.stringify(hash)); return `${source} function hotDisposeChunk(chunkId) { delete installedChunks[chunkId]; } var parentHotUpdateCallback = global.parentHotUpdateCallback = this[${JSON.stringify(hotUpdateFunction)}]; this[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`; }); mainTemplate.plugin("hash", function(hash) { hash.update("jsonp"); hash.update("4"); hash.update(`${this.outputOptions.filename}`); hash.update(`${this.outputOptions.chunkFilename}`); hash.update(`${this.outputOptions.jsonpFunction}`); hash.update(`${this.outputOptions.hotUpdateFunction}`); }); mainTemplate.plugin("render", (beforeMainSource, chunk, hash, moduleTemplate, dependencyTemplates) => { //beforeMainSource 是每部的MainTemplate render之后的代码 // __CMLCOMPONNETS__ 是用于存放每个组件的执行函数 const source = new ConcatSource(); source.add(`/******/ var __CML__GLOBAL = {};\n`); source.add(`/******/ __CML__GLOBAL.__CMLCOMPONNETS__ = {};\n`); source.add(beforeMainSource); source.add(`\r\n /******/ module.exports = __CML__GLOBAL;`); return source; }); } } module.exports = JsonpMainTemplatePlugin; ================================================ FILE: packages/chameleon-miniapp-target/src/JsonpTemplatePlugin.js ================================================ "use strict"; const JsonpMainTemplatePlugin = require("./JsonpMainTemplatePlugin"); const JsonpChunkTemplatePlugin = require("./JsonpChunkTemplatePlugin"); const JsonpHotUpdateChunkTemplatePlugin = require("./JsonpHotUpdateChunkTemplatePlugin"); class JsonpTemplatePlugin { apply(compiler) { compiler.plugin("this-compilation", (compilation) => { compilation.mainTemplate.apply(new JsonpMainTemplatePlugin()); compilation.chunkTemplate.apply(new JsonpChunkTemplatePlugin()); compilation.hotUpdateChunkTemplate.apply(new JsonpHotUpdateChunkTemplatePlugin()); }); } } module.exports = JsonpTemplatePlugin; ================================================ FILE: packages/chameleon-miniapp-target/src/index.js ================================================ const JsonpTemplatePlugin = require("./JsonpTemplatePlugin"); const NodeSourcePlugin = require("webpack/lib/node/NodeSourcePlugin"); const FunctionModulePlugin = require("webpack/lib/FunctionModulePlugin"); const LoaderTargetPlugin = require("webpack/lib/LoaderTargetPlugin"); module.exports = function (compiler) { const { options } = compiler compiler.apply( new JsonpTemplatePlugin(options.output), new FunctionModulePlugin(options.output), new NodeSourcePlugin(options.node), new LoaderTargetPlugin(options.target) ); }; ================================================ FILE: packages/chameleon-mixins/.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/chameleon-mixins/.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/ ================================================ FILE: packages/chameleon-mixins/.npmignore ================================================ .scripts ================================================ FILE: packages/chameleon-mixins/alipay-mixins.js ================================================ const commonMixins = require('./wx-alipay-common-mixins.js'); var _ = module.exports = commonMixins.deepClone(commonMixins); const utils = require('./utils.js'); commonMixins.merge(_.mixins.methods, { [_.eventEmitName]: function(eventKey, detail) { let dataset = {}; let propKeys = Object.keys((this.props || {})); (propKeys || []).forEach((propKey) => { if (propKey.indexOf('data-') === 0) { let dataKey = propKey.slice(5);// 得到dataset中应该对应的key值 if (dataKey) { dataset[dataKey] = this.props[propKey]; } } }) // let modelkey = this.props['data-modelkey']; // let eventKeyProps = this.props["data-event" + eventKey]; function titleLize (word) { // 将开头字母转化为大写 return word.replace(/^\w/, function (match) { return match.toUpperCase(); }) } // 这里对于用户自定义事件,会将首字母大写,但是对于原生事件 touchstart 仅仅大写是不够的,还需要将 touchstart ==> TouchStart eventKey = utils.handleCompEventType(eventKey); let callback = this.props['on' + titleLize(eventKey)]; if (callback && _.isType(callback, 'Function')) { callback({ type: eventKey, detail, currentTarget: { dataset } }) } } }); ================================================ FILE: packages/chameleon-mixins/common.js ================================================ const _ = module.exports = {}; const utils = require('./utils.js') _.eventProxyName = '_cmlEventProxy'; _.modelEventProxyName = '_cmlModelEventProxy';// c-model v-model的事件代理 _.inlineStatementEventProxy = '_cmlInline';// 内联语句的事件代理 _.eventEmitName = '$cmlEmit'; // 触发事件的方法 _.styleParseName = '$cmlStyle'; // 提供各端处理style属性的方法 weex中处理成对象,wx中处理成字符串,web中不处理 _.styleProxyName = '_cmlStyleProxy'; // 提供代理 weex和web端处理style _.mergeStyleName = '$cmlMergeStyle'; _.animationProxy = '_animationCb'; _.weexClassProxy = '_weexClassProxy'; _.merge = function(target, fromObj) { Object.keys(fromObj).forEach(key => { target[key] = fromObj[key] }) } _.isType = function (obj, type) { return Object.prototype.toString.call(obj).slice(8, -1) === type } _.mergeStyle = function (...args) { // 可以接受字符串或者对象 function styleToObj(str) { let obj = {}; str.split(';').filter(item => !!item.trim()) .forEach(item => { let {key, value} = utils.getStyleKeyValue(item); key = key.replace(/\s+/, '') value = value.replace(/\s+/, '') obj[key] = value }) return obj; } let arr = []; args.forEach(arg => { if (typeof arg === 'string') { arr.push(styleToObj(arg)) } else if (Object.prototype.toString.call(arg) === '[object Object]') { arr.push(arg); } }) let resultObj = Object.assign(...arr) let resultStr = '' Object.keys(resultObj).forEach(key => { resultStr += `${key}:${resultObj[key]};` }) return resultStr; } _.trim = function (value) { return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }; _.isReactive = function(value) { let reg = /(?:^'([^']*?)'$)/; return _.trim(value).match(reg); } ================================================ FILE: packages/chameleon-mixins/miniapp-utils/px2cpx.js ================================================ /* eslint-disable */ const _ = module.exports = {}; const platform = process.env.platform; let viewportWidth ; _.px2cpx = function(px) { function getViewportSize() { if (platform === 'wx') { const { windowWidth } = wx.getSystemInfoSync(); return windowWidth; } if (platform === 'baidu') { const { windowWidth } = swan.getSystemInfoSync(); return windowWidth; } if (platform === 'alipay') { const { windowWidth } = my.getSystemInfoSync(); return windowWidth; } if (platform === 'qq') { const { windowWidth } = qq.getSystemInfoSync(); return windowWidth; } if (platform === 'tt') { const { windowWidth } = tt.getSystemInfoSync(); return windowWidth; } } viewportWidth = viewportWidth || getViewportSize(); const cpx = +(750 / viewportWidth * px).toFixed(3); return cpx; } ================================================ FILE: packages/chameleon-mixins/package.json ================================================ { "name": "chameleon-mixins", "version": "1.0.8", "description": "chameleon-mixins", "main": "index.js", "scripts": { "test": "mocha --recursive --reporter spec", "eslint:fix": "eslint web-mixins.js weex-mixins.js wx-mixins.js common.js --fix", "eslint": "eslint web-mixins.js weex-mixins.js wx-mixins.js common.js " }, "author": "Chameleon-Team", "license": "Apache", "devDependencies": { "chai": "^4.2.0", "chameleon-css-loader": "1.0.8", "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/chameleon-mixins/test/common.test.js ================================================ const common = require('../common.js'); const {expect} = require('chai'); describe('common.js', function() { it('test merge source to target', function() { let target = {name: 'jim', age: 12}; let source = {name: 'jhon', address: 'China'}; let result = common.merge(target, source); expect(target).to.be.deep.equal({ name: 'jhon', age: 12, address: 'China' }) }); it('test isType function', function() { let str = 'this is string'; let bool = true; let obj = {}; let arr = []; let date = new Date(); let promise = new Promise(() => {}); let reg = /this is reg/g; let fun = () => {}; expect(common.isType(str, 'String')).to.be.ok; expect(common.isType(bool, 'Boolean')).to.be.ok; expect(common.isType(obj, 'Object')).to.be.ok; expect(common.isType(arr, 'Array')).to.be.ok; expect(common.isType(date, 'Date')).to.be.ok; expect(common.isType(promise, 'Promise')).to.be.ok; expect(common.isType(reg, 'RegExp')).to.be.ok; expect(common.isType(fun, 'Function')).to.be.ok; expect(common.isType(str, 'String')).to.be.ok; }); it('test mergeStyle function', function() { let cssString = 'background-color:red; width:100px; '; let cssObj1 = {height: '200px;', 'font-size': '30px'}; let cssObj2 = {color: 'red'}; let mergeResult = common.mergeStyle(cssString, cssObj1, cssObj2); expect(mergeResult).to.be.equal(`background-color:red;width:100px;height:200px;;font-size:30px;color:red;`); }); it('judge if the arguments is reactive', function() { expect(common.isReactive(`'index'`)).to.be.an('array') }); it('test trim function', function() { expect(common.trim(' name ')).to.be.equal('name'); }) }) ================================================ FILE: packages/chameleon-mixins/test/utils.test.js ================================================ const utils = require('../utils.js'); const {expect} = require('chai'); describe('utils.js', function() { it('test getStyleKeyValue', function() { let source = `background-url:(http:www.didi.chameleon.png)` let result = utils.getStyleKeyValue(source); expect(result).to.include.keys('key'); expect(result).to.include.keys('value'); }); it('test handleEventType', function() { expect(utils.handleEventType('touchStart')).to.equal('touchstart') expect(utils.handleEventType('touchMove')).to.equal('touchmove') expect(utils.handleEventType('touchEnd')).to.equal('touchend') expect(utils.handleEventType('tap')).to.equal('tap') }); it('test handleCompEventType', function() { expect(utils.handleCompEventType('touchstart')).to.equal('touchStart') expect(utils.handleCompEventType('touchmove')).to.equal('touchMove') expect(utils.handleCompEventType('touchend')).to.equal('touchEnd') expect(utils.handleCompEventType('tap')).to.equal('tap') }); }) ================================================ FILE: packages/chameleon-mixins/test/web-mixins.test.js ================================================ const mixins = require('../web-mixins.js').mixins.methods; const {expect} = require('chai'); let eventEmittter = require('events') let e = { type: 'touchstart', target: { }, stopPropagation: function() {}, timeStamp: 3795662, currentTarget: { dataset: { eventtouchstart: ['handleTouchStart', '$event', 1] } }, touches: [{ identifier: 'identifier', pageX: 'pageX', pageY: 'pageY', screenX: 'screenX', screenY: 'screenY' }], changedTouches: [{ identifier: 'identifier', pageX: 'pageX', pageY: 'pageY', screenX: 'screenX', screenY: 'screenY' }] } describe('web-mixins.js', function() { it('test _cmlInline', function() { global.Event = eventEmittter let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlInline.call(thisArg, 'handleTouchStart', true, e)).to.be.not.ok }); it('test _cmlModelEventProxy', function() { let thisArg = { key: 'modelKey' } global.window = {innerWidth: 750, innerHeight: 1260 } // px2cpx用到了window.innerWidth expect(mixins._cmlModelEventProxy.call(thisArg, e, 'key')).to.be.not.ok }); it('test _cmlEventProxy', function() { let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlEventProxy.call(thisArg, e, 'handleTouchStart', true)).to.be.not.ok }); it('test $cmlEmit', function() { let thisArg = { $emit: function() { }, $__checkCmlEmit__: function() { } } expect(mixins.$cmlEmit.call(thisArg, 'handleTouchStart', {detail: 'detail'})).to.be.not.ok }); it('test styleProxyName function:aimed to transform cssStyle object to cssStyle string', function() { expect(mixins.$cmlStyle('width:100px;')).to.be.equal('width:100px;') expect(mixins.$cmlStyle({width: '100px'})).to.be.equal('width:100px;') }); it('test $cmlMergeStyle function', function() { let cssString = 'background-color:red; width:100px; '; let cssObj1 = {height: '200px;', 'font-size': '30px'}; let cssObj2 = {color: 'red'}; let mergeResult = mixins.$cmlMergeStyle(cssString, cssObj1, cssObj2); expect(mergeResult).to.be.equal(`background-color:red;width:100px;height:200px;;font-size:30px;color:red;`); }); }) ================================================ FILE: packages/chameleon-mixins/test/weex-mixins.test.js ================================================ const mixins = require('../weex-mixins.js').mixins.methods; const {expect} = require('chai'); describe('weex-mixins.js', function() { it('test _cmlInline', function() { let e = { type: 'touchstart', target: { }, stopPropagation: function() {}, timestamp: 3795662, currentTarget: { dataset: { eventtouchstart: ['handleTouchStart', '$event', 1] } }, touches: [{ identifier: 'identifier', pageX: 'pageX', pageY: 'pageY', screenX: 'screenX', screenY: 'screenY' }], changedTouches: [{ identifier: 'identifier', pageX: 'pageX', pageY: 'pageY', screenX: 'screenX', screenY: 'screenY' }] } let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlInline.call(thisArg, 'handleTouchStart', true, 1, e)).to.be.not.ok }); it('test _cmlModelEventProxy', function() { let e = { type: 'touchstart', target: { }, stopPropagation: function() {}, timestamp: 3795662, currentTarget: { dataset: { eventtouchstart: ['handleTouchStart', '$event', 1] } } } let thisArg = { key: 'modelKey' } expect(mixins._cmlModelEventProxy.call(thisArg, e, 'key')).to.be.not.ok }); it('test _cmlEventProxy', function() { let e = { type: 'touchstart', target: { }, stopPropagation: function() {}, timestamp: 3795662, currentTarget: { dataset: { eventtouchstart: ['handleTouchStart', '$event', 1] } } } let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlEventProxy.call(thisArg, e, 'handleTouchStart', true)).to.be.not.ok }); it('test $cmlEmit', function() { let thisArg = { $emit: function() { }, $__checkCmlEmit__: function() { } } expect(mixins.$cmlEmit.call(thisArg, 'handleTouchStart', {detail: 'detail'})).to.be.not.ok }); it('test styleProxyName function:aimed to transform cssStyle string to cssStyle Object', function() { expect(mixins._cmlStyleProxy('width:75px; ;height:50px; ')).to.be.deep.equal({ width: '75px', height: '50px' }); expect(mixins._cmlStyleProxy({ width: '75px', height: '50px' })).to.be.deep.equal({ width: '75px', height: '50px' }); }); it('test $cmlMergeStyle function', function() { let cssString = 'background-color:red; width:100px; '; let cssObj1 = {height: '200px;', 'font-size': '30px'}; let cssObj2 = {color: 'red'}; let mergeResult = mixins.$cmlMergeStyle(cssString, cssObj1, cssObj2); expect(mergeResult).to.be.equal(`background-color:red;width:100px;height:200px;;font-size:30px;color:red;`); }); it('test weexClassProxy array function', function() { let result = mixins._weexClassProxy(['str1', 'str2']); expect(result).to.include('str1') expect(result).to.include('str2') }); it('test weexClassProxy array function', function() { let result = mixins._weexClassProxy({str1: 'str1', str2: 'str2'}); expect(result).to.include('str1') expect(result).to.include('str2') }); }) ================================================ FILE: packages/chameleon-mixins/test/wx-mixins.test.js ================================================ const mixins = require('../wx-mixins.js').mixins.methods; const {expect} = require('chai'); describe('wx-mixins.js', function() { it('test $cmlEmit', function() { let thisArg = { triggerEvent: function() { }, $__checkCmlEmit__: function() { } } expect(mixins.$cmlEmit.call(thisArg, 'handleTouchStart', {detail: 'detail'})).to.be.not.ok }); it('test _cmlInline', function() { let e = { type: 'touchstart', currentTarget: { dataset: { eventtouchstart: ['handleTouchStart', '$event', 1] } } } let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlInline.call(thisArg, e)).to.be.not.ok }); it('test _cmlModelEventProxy', function() { let e = { type: 'touchstart', currentTarget: { dataset: { modelkey: 'key' } }, detail: { value: "detailValue" } } let thisArg = { key: 'modelKey' } expect(mixins._cmlModelEventProxy.call(thisArg, e)).to.be.not.ok }); it('test _cmlEventProxy', function() { let e = { type: 'touchstart', currentTarget: { dataset: { eventtouchstart: ['handleTouchStart'] } } } let thisArg = { handleTouchStart: function() { } } expect(mixins._cmlEventProxy.call(thisArg, e)).to.be.not.ok }); it('test styleProxyName function:aimed to transfrom px to rpx', function() { expect(mixins.$cmlStyle('width:75cpx;height:50cpx;')).to.be.equal(`width:75rpx;height:50rpx`); expect(mixins.$cmlStyle({ width: '75cpx', height: '50cpx' })).to.be.equal(`width:75rpx;height:50rpx`); }); it('test $cmlMergeStyle function', function() { let cssString = 'background-color:red; width:100px; '; let cssObj1 = {height: '200px;', 'font-size': '30px'}; let cssObj2 = {color: 'red'}; let mergeResult = mixins.$cmlMergeStyle(cssString, cssObj1, cssObj2); expect(mergeResult).to.be.equal(`background-color:red;width:100px;height:200px;;font-size:30px;color:red;`); }); // _animationCb it('test _animationCb', function() { let e = { animationValue: 'animationValue' } let thisArg = { animationValue: { cbs: [function() {}], index: 0 } } expect(mixins._animationCb.call(thisArg, e)).to.be.not.ok }); it('test _animationCb', function() { let e = { animationValue: 'animationValue' } let thisArg = { animationValue: { index: 1 } } expect(mixins._animationCb.call(thisArg, e)).to.be.not.ok }); it('test _animationCb', function() { let e = { animationValue: 'animationValue' } let thisArg = { animationValue: { cbs: [function() {}] } } expect(mixins._animationCb.call(thisArg, e)).to.be.not.ok }); }) ================================================ FILE: packages/chameleon-mixins/utils.js ================================================ const _ = module.exports = {}; _.getStyleKeyValue = function(declaration) { let colonIndex = declaration.indexOf(':'); let key = declaration.slice(0, colonIndex); let value = declaration.slice(colonIndex + 1); return { key, value } } // 支付宝中的e.type="touchStart" _.handleEventType = function(eventType) { let aliEventMap = { touchStart: "touchstart", touchEnd: "touchend", touchMove: "touchmove" } if (Object.keys(aliEventMap).includes(eventType)) { return aliEventMap[eventType] } else { return eventType } } // 对于组件上绑定的touchstart事件,在编译之后会处理成 onTouchStart="handleStart",所以需要改为对应的大写 _.handleCompEventType = function(eventType) { let aliEventMap = { touchstart: 'touchStart', touchend: 'touchEnd', touchmove: 'touchMove' } if (Object.keys(aliEventMap).includes(eventType)) { return aliEventMap[eventType] } else { return eventType } } ================================================ FILE: packages/chameleon-mixins/web-mixins.js ================================================ const common = require('./common.js'); const webStyleHandle = require('chameleon-css-loader/proxy/proxyWeb.js'); const {px2cpx} = require('./web-utils/px2cpx.js'); const _ = module.exports = {}; common.merge(_, common); _.mixins = { methods: { //支持事件传参 [_.inlineStatementEventProxy](...args){ let _cml_event_lmc; // ...args 的参数是用户传入的,可能为任意值,防止冲突;(a,'item',e); args = args.reduce((result,arg) => { if(arg instanceof Event){ _cml_event_lmc = arg; result.push(getNewEvent(arg)) }else{ result.push(arg) } return result; },[]); let originFuncName = args[0]; let isStopBubble = args[1]; if(isStopBubble && _cml_event_lmc && typeof _cml_event_lmc.stopPropagation === 'function'){ _cml_event_lmc.stopPropagation(); } if(this[originFuncName] && _.isType(this[originFuncName], 'Function')){ this[originFuncName](...args.slice(2)) }else{ console.log(`can not find function ${originFuncName}`) } }, //代理 事件,可以使用v-model [_.modelEventProxyName](e,modelKey){ let newEvent = getNewEvent(e); this[modelKey] = newEvent.detail.value; }, [_.eventProxyName](e, originFuncName,isStopBubble) { //调用原始事件 if(isStopBubble && typeof e.stopPropagation === 'function'){ e.stopPropagation(); } if(this[originFuncName] && _.isType(this[originFuncName], 'Function')) { //获取新的事件对象 let newEvent = getNewEvent(e); this[originFuncName](newEvent) } else { console.log(`can not find function ${originFuncName}`) } }, [_.eventEmitName](eventKey, detail){ //传递的参数内容包装成detail参数 this.$emit(eventKey, { type: eventKey, detail, stopPropagation:function(){} }) if(this['$__checkCmlEmit__']) { this['$__checkCmlEmit__'](eventKey, detail); } }, [_.styleParseName](content) { //web端不需要处理直接返回 let res = ''; if(_.isType(content, 'String')) { res = content } else if(_.isType(content, 'Object')) { Object.keys(content).forEach(key=>{ res +=`${key}:${content[key]};` }) } return res; }, [_.styleProxyName](content,options) { return webStyleHandle(content,options); }, [_.mergeStyleName](...args) { return _.mergeStyle(...args); } } } function getNewEvent(e) { let newEvent = {} let {type, timeStamp, target, currentTarget, touches, changedTouches, detail = {} } = e; newEvent._originEvent = e; if(type) { type = type.replace(/^weex\$/, ''); type = type === 'click' ? 'tap' : type; newEvent.type = type; } if(timeStamp) { newEvent.timeStamp = parseInt(timeStamp); } if(target) { newEvent.target = { id: target.id, // offsetLeft: target.offsetLeft, // offsetTop: target.offsetTop, dataset: target.dataset } } if(currentTarget) { newEvent.currentTarget = { id: currentTarget.id, // offsetLeft: currentTarget.offsetLeft, // offsetTop: currentTarget.offsetTop, dataset: currentTarget.dataset } } if(touches) { newEvent.touches = []; for(let i=0;i { return weexEventKeys.includes(flagKey); }) } let _cml_event_lmc; // ...args 的参数是用户传入的,可能为任意值,防止冲突;(a,'item',e); args = args.reduce((result, arg) => { if (_is__WEEX__EVENT(arg)) { _cml_event_lmc = arg; result.push(getNewEvent(arg)) } else { result.push(arg) } return result; }, []); let originFuncName = args[0]; let isStopBubble = args[1]; if(isStopBubble && _cml_event_lmc && typeof _cml_event_lmc.stopPropagation === 'function'){ _cml_event_lmc.stopPropagation(); } if (this[originFuncName] && _.isType(this[originFuncName], 'Function')) { this[originFuncName](...args.slice(2)) } else { console.log(`can not find function ${originFuncName}`) } }, [_.modelEventProxyName](e, modelKey) { let newEvent = getNewEvent(e); this[modelKey] = newEvent.detail.value; }, [_.eventProxyName](e, originFuncName,isStopBubble) { if(isStopBubble && typeof e.stopPropagation === 'function'){ e.stopPropagation(); } //调用原始事件 if (this[originFuncName] && _.isType(this[originFuncName], 'Function')) { //获取新的事件对象 let newEvent = getNewEvent(e); this[originFuncName](newEvent) } else { console.log(`can not find function ${originFuncName}`) } }, [_.eventEmitName](eventKey, detail) { //传递的参数内容包装成detail参数 this.$emit(eventKey, { type: eventKey, detail }) if (this['$__checkCmlEmit__']) { this['$__checkCmlEmit__'](eventKey, detail); } }, //weex端的处理放到自动包装的代理函数中 [_.styleParseName](content) { return content; }, //动态的style ,编译时会自动包装的代理函数 [_.styleProxyName](content) { let res = ''; if (_.isType(content, 'String')) { res = content } else if (_.isType(content, 'Object')) { Object.keys(content).forEach(key => { res += `${key}:${content[key]};` }) } //先统一转成字符串,处理后再转成对象 res = weexStyleHandle.parse(res) var obj = {}; res.split(';').filter(styleitem => { return !!styleitem.trim(); }).forEach(item => { let { key, value } = utils.getStyleKeyValue(item); key = key.replace(/\s/g, '') value = value.replace(/\s/g, '') obj[key] = value; }) return obj; }, [_.mergeStyleName](...args) { return _.mergeStyle(...args); }, // 只提供weex端动态class的代理 [_.weexClassProxy](value) { if ((typeof value === 'string')) { return value.split(' ').filter((item) => !!item.trim()); } else if (Object.prototype.toString.call(value) === '[object Array]') { return value } else if (Object.prototype.toString.call(value) === '[object Object]') { return Object.keys(value).filter((key) => value[key]) } else { throw new Error(`please check if the value of class is right at ${JSON.stringify(value)}`) } } } } function getNewEvent(e) { let newEvent = {} let { type, timestamp, target, currentTarget, touches, changedTouches, value, detail = {} } = e; newEvent._originEvent = e; if (type) { type = type.replace(/^weex\$/, ''); type = type === 'click' ? 'tap' : type; newEvent.type = type; } if (timestamp) { newEvent.timeStamp = parseInt(e.timestamp); } if (target) { newEvent.target = getTarget(target) } if (currentTarget) { newEvent.currentTarget = getTarget(currentTarget) } function getTarget(originTarget) { let { attr } = originTarget; let id; let dataset = {}; if (attr) { Object.keys(attr).forEach(key => { let originKey = key; if (key === 'id') { id = attr[key] } else { if (key.indexOf('data') === 0) { key = key.slice(4); //weex中会把data-name="yyl" 变成 dataName:yyl data后的第一个字母大写 key = key[0].toLowerCase() + key.slice(1); dataset[key] = attr[originKey] } } }) } return { id, dataset } } if (touches) { newEvent.touches = []; for (let i = 0; i < touches.length; i++) { let touch = touches[i]; let ret = {} ret.identifier = touch.identifier; ret.pageX = parseInt(touch.pageX); ret.pageY = parseInt(touch.pageY); ret.clientX = parseInt(touch.screenX); ret.clientY = parseInt(touch.screenY); newEvent.touches.push(ret); } } if (changedTouches) { newEvent.changedTouches = []; for (let i = 0; i < changedTouches.length; i++) { let touch = changedTouches[i]; let ret = {} ret.identifier = touch.identifier; ret.pageX = parseInt(touch.pageX); ret.pageY = parseInt(touch.pageY); ret.clientX = parseInt(touch.screenX); ret.clientY = parseInt(touch.screenY); newEvent.changedTouches.push(ret); } } if (_.isType(detail, 'Object')) { newEvent.detail = detail } else { newEvent.detail = {} } if (value) { newEvent.detail.value = value; } //特殊事件处理 if (type === 'loadmore') { newEvent.detail.direction = 'bottom'; } if (type === 'scroll') { newEvent.detail = { ...newEvent.detail, scrollHeight: e.contentSize ? e.contentSize.height : 0, scrollWidth: e.contentSize ? e.contentSize.width : 0, scrollLeft: e.contentOffset ? Math.abs(e.contentOffset.x) : 0, scrollTop: e.contentOffset ? Math.abs(e.contentOffset.y) : 0, // 坐标轴变换 deltaX: 0, deltaY: 0 } } return newEvent; } ================================================ FILE: packages/chameleon-mixins/wx-alipay-common-mixins.js ================================================ const common = require('./common.js'); const wxStyleHandle = require('chameleon-css-loader/proxy/proxyMiniapp.js') const utils = require('./utils.js'); const {px2cpx} = require('./miniapp-utils/px2cpx.js'); const deepClone = function(obj) { if (obj.toString().slice(8, -1) !== "Object") { return obj; } let res = {}; Object.keys(obj).forEach(key => { res[key] = deepClone(obj[key]); }) return res; } var _ = module.exports = { deepClone }; common.merge(_, common); _.mixins = { methods: { // 支持事件传参 [_.inlineStatementEventProxy](e) { let { dataset } = e.currentTarget; // 支付宝的e.type = 'touchStart',需要改为小写,否则找不到函数 e.type = utils.handleEventType(e.type); let eventKey = e.type.toLowerCase(); let originFuncName = dataset && dataset[`event${eventKey}`] && dataset[`event${eventKey}`][0]; let inlineArgs = dataset && dataset[`event${eventKey}`] && dataset[`event${eventKey}`].slice(1); let argsArr = []; // 由于百度对于 data-arg="" 在dataset.arg = true 值和微信端不一样所以需要重新处理下这部分逻辑 if (inlineArgs) { argsArr = inlineArgs.reduce((result, arg, index) => { if (arg === "$event") { let newEvent = getNewEvent(e); result.push(newEvent); } else { result.push(arg) } return result; }, []); } if (originFuncName && this[originFuncName] && _.isType(this[originFuncName], 'Function')) { this[originFuncName](...argsArr) } else { console.log(`can not find method ${originFuncName}`) } }, [_.modelEventProxyName](e) { let dataset = e.currentTarget && e.currentTarget.dataset let modelKey = dataset && dataset.modelkey if (modelKey) { this[modelKey] = e.detail.value; } }, [_.eventProxyName](e) { let { dataset } = e.currentTarget; // 支付宝的e.type = 'touchStart',需要改为小写,否则找不到函数 e.type = utils.handleEventType(e.type); let eventKey = e.type.toLowerCase(); let originFuncName = dataset && dataset[`event${eventKey}`] && dataset[`event${eventKey}`][0]; if (originFuncName && this[originFuncName] && _.isType(this[originFuncName], 'Function')) { let newEvent = getNewEvent(e); this[originFuncName](newEvent) } else { console.log(`can not find method ${originFuncName}`) } }, [_.styleParseName](content) { let res = '' if (_.isType(content, 'Object')) { Object.keys(content).forEach(key => { res += `${key}:${content[key]};` }) } else if (_.isType(content, 'String')) { res = content; } return wxStyleHandle(res); }, [_.mergeStyleName](...args) { return _.mergeStyle(...args); }, [_.animationProxy](...args) { let animationValue = args[0]; // animationValue:{cbs:{0:cb0,1:cb1,length:2},index} let animation = this[animationValue];// 引用值 if (!animation) { return ; } const { cbs, index } = animation; // 配合 解决百度端动画bug if (cbs === undefined || index === undefined) {return ;} let cb = cbs[index]; if (cb && typeof cb === 'function') { cb(); } delete animation.index; animation.index = index + 1; } } } function getNewEvent(e) { let newEvent = {}; ['type', 'timeStamp', 'target', 'currentTarget', 'detail', 'touches', 'changedTouches'].forEach((key) => { if (e[key]) { if (~['target', 'currentTarget'].indexOf(key)) { let newTarget = {} newTarget = { id: e[key].id, dataset: e[key].dataset } newEvent[key] = newTarget } else if (~['touches', 'changedTouches'].indexOf(key)) { if (key == 'touches') { let touches = e[key]; newEvent.touches = []; for (let i = 0;i < touches.length;i++) { let touch = touches[i]; let ret = {} ret.identifier = touch.identifier; ret.pageX = px2cpx(parseInt(touch.pageX, 10)); ret.pageY = px2cpx(parseInt(touch.pageY, 10)); ret.clientX = px2cpx(parseInt(touch.clientX, 10)); ret.clientY = px2cpx(parseInt(touch.clientY, 10)); newEvent.touches.push(ret); } } if (key == 'changedTouches') { let changedTouches = e[key] newEvent.changedTouches = []; for (let i = 0;i < changedTouches.length;i++) { let touch = changedTouches[i]; let ret = {} ret.identifier = touch.identifier; ret.pageX = px2cpx(parseInt(touch.pageX, 10)); ret.pageY = px2cpx(parseInt(touch.pageY, 10)); ret.clientX = px2cpx(parseInt(touch.clientX, 10)); ret.clientY = px2cpx(parseInt(touch.clientY, 10)); newEvent.changedTouches.push(ret); } } } else { newEvent[key] = e[key] } } }) newEvent._originEvent = e; return newEvent; } ================================================ FILE: packages/chameleon-mixins/wx-mixins.js ================================================ const commonMixins = require('./wx-alipay-common-mixins.js'); var _ = module.exports = commonMixins.deepClone(commonMixins); commonMixins.merge(_.mixins.methods, { [_.eventEmitName]: function(eventKey, detail) { this.triggerEvent(eventKey, detail); if (this.$__checkCmlEmit__) { this.$__checkCmlEmit__(eventKey, detail); } } }); ================================================ FILE: packages/chameleon-template-parse/.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/chameleon-template-parse/.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/ # old-test/ ================================================ FILE: packages/chameleon-template-parse/.npmignore ================================================ # Editor directories and files .git .DS_Store .idea *.suo *.ntvs* *.njsproj *.sln .vscode .script # 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 node_modules/ 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 # /////////////////////////// # odl-test old-test/ ================================================ FILE: packages/chameleon-template-parse/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/chameleon-template-parse/gulpfile.js ================================================ const gulp = require('gulp'); const uglify = require('gulp-uglify-es')["default"]; gulp.task('build', function () { gulp.src('src/**/**.js') .pipe(uglify({ parse: { bare_returns: true }, mangle: { toplevel: true }, compress: { drop_console: true, drop_debugger: true } })) .pipe(gulp.dest('dist/')); }); ================================================ FILE: packages/chameleon-template-parse/index.html ================================================ Document
================================================ FILE: packages/chameleon-template-parse/index.js ================================================ let compileTemplate = require('./src/index.js') module.exports = compileTemplate; ================================================ FILE: packages/chameleon-template-parse/package.json ================================================ { "name": "chameleon-template-parse", "version": "1.0.8", "description": "", "main": "index.js", "scripts": { "eslint": "eslint ./src", "lint": "eslint --ext .js src --fix", "dev": "webpack-dev-server", "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/generator": "^7.1.2", "@babel/parser": "^7.3.4", "@babel/plugin-syntax-jsx": "^7.0.0", "@babel/traverse": "^7.1.0", "@babel/types": "^7.1.2", "babylon": "^6.18.0", "hash-sum": "^1.0.2", "tapable": "^1.1.0" }, "devDependencies": { "chai": "^4.2.0", "chameleon-css-loader": "1.0.8", "chameleon-mixins": "1.0.8", "chameleon-tool-utils": "1.0.8", "clean-webpack-plugin": "^0.1.19", "coveralls": "^2.11.9", "eslint": "^5.9.0", "gulp": "^3.9.1", "gulp-uglify-es": "^1.0.4", "html-webpack-plugin": "^3.2.0", "istanbul": "^0.4.5", "mocha": "^5.2.0", "uglifyjs-webpack-plugin": "^2.0.1", "webpack": "^4.20.2", "webpack-cli": "^3.1.1", "webpack-dev-server": "^3.1.9" }, "mail": "ChameleonCore@didiglobal.com", "gitHead": "5ddcde4330774710f7646559446e008f7785ce00" } ================================================ FILE: packages/chameleon-template-parse/src/common/cml-map.js ================================================ exports.tagMap = { targetTagMap: { 'cover-view': { wx: 'cover-view', web: 'div', weex: 'div', alipay: 'cover-view', baidu: 'cover-view', qq: 'cover-view', tt: 'cover-view' }, 'view': { wx: 'view', web: 'div', weex: 'div', alipay: 'view', baidu: 'view', qq: 'view', tt: 'view' }, 'text': { wx: 'text', web: 'span', weex: 'text', alipay: 'text', baidu: 'text', qq: 'text', tt: 'text' }, 'image': { wx: 'image', web: 'img', weex: 'image', alipay: 'image', baidu: 'image', qq: 'image', tt: 'image' }, 'input': { wx: 'input', web: 'input', weex: 'input', alipay: 'input', baidu: 'input', qq: 'input', tt: 'input' }, 'button': { wx: 'button', web: 'div', weex: 'div', alipay: 'button', baidu: 'button', qq: 'button', tt: 'button' }, 'cell': { wx: 'view', web: 'cell', weex: 'cell', alipay: 'view', baidu: 'view', qq: 'view', tt: 'view' }, 'slider-item': { wx: 'swiper-item', web: 'div', weex: 'div', alipay: 'swiper-item', baidu: 'swiper-item', qq: 'swiper-item', tt: 'swiper-item' } }, afterTagMap: { // 在最后处理的标签,因为template标签在jsdom中不识别 'block': { wx: 'block', web: 'template', weex: 'template', alipay: 'block', baidu: 'block', qq: 'block', tt: 'block' } }, diffTagMap: { wx: ['cml-wx'], web: ['cml-web', 'cml-web-weex'], weex: ['cml-weex', 'cml-web-weex'], alipay: ['cml-ali'], baidu: ['cml-baidu'], qq: ['cml-qq'], tt: ['cml-tt'] }, wxTagMap: { 'carousel': 'swiper', 'carousel-item': 'swiper-item' } } exports.conditionMap = { 'c-if': { web: 'v-if', weex: 'v-if', wx: 'wx:if', alipay: 'a:if', baidu: 's-if', qq: 'qq:if', tt: 'tt:if' }, 'c-else-if': { web: 'v-else-if', weex: 'v-else-if', wx: 'wx:elif', alipay: 'a:elif', baidu: 's-elif', qq: 'qq:elif', tt: 'tt:elif' }, 'c-else': { web: 'v-else', weex: 'v-else', wx: 'wx:else', alipay: 'a:else', baidu: 's-else', qq: 'qq:else', tt: 'tt:else' } } exports.interationMap = { 'c-for': { wx: 'wx:for', alipay: 'a:for', baidu: 's-for', qq: 'qq:for', tt: 'tt:for' }, 'c-key': { wx: 'wx:key', alipay: 'a:key', baidu: 's-key', // 百度文档没有提到这个,黑人问号脸 qq: 'qq:key', tt: 'tt:key' }, 'c-for-item': { wx: 'wx:for-item', alipay: 'a:for-item', baidu: 's-for-item', qq: 'qq:for-item', tt: 'tt:for-item' }, 'c-for-index': { wx: 'wx:for-index', alipay: 'a:for-index', baidu: 's-for-index', qq: 'qq:for-index', tt: 'tt:for-index' } } exports.eventMap = { 'touchstart': 'touchStart', 'touchmove': 'touchMove', 'touchend': 'touchEnd', 'touchcancel': 'touchCancel', 'longtap': 'longTap' } exports.conditionMapVue2Wx = { 'v-if': { wx: 'wx:if', alipay: 'a:if', baidu: 's-if', qq: 'qq:if', tt: 'tt:if' }, 'v-else-if': { wx: 'wx:elif', alipay: 'a:elif', baidu: 's-elif', qq: 'qq:elif', tt: 'tt:elif' }, 'v-else': { wx: 'wx:else', alipay: 'a:else', baidu: 's-else', qq: 'qq:else', tt: 'tt:else' } } ================================================ FILE: packages/chameleon-template-parse/src/common/process-template.js ================================================ const babylon = require('babylon'); const t = require('@babel/types'); const traverse = require('@babel/traverse')['default']; const generate = require('@babel/generator')['default']; const { tagMap } = require('../common/cml-map.js') const utils = require('./utils'); exports.startCallback = function(matchStart, type, options) { let usingComponents = options.usingComponents || []; let buildInComponents = options.buildInComponents || {}; if (type === 'alipay') { let isComponent = usingComponents.find((comp) => comp.tagName === matchStart.tagName) || Object.keys(buildInComponents).includes(matchStart.tagName); let leftAttrsOnComponent = matchStart.attrs;// 遗留在组件上的属性,默认值是所有属性,如果是组件,那么还需要过滤 if (isComponent) { // 如果是组件,那么需要将组件的c-if c-else去掉 let filtersAttrs = ['c-if', 'c-else', 'c-else-if', 'v-if', 'v-else', 'v-else-if'] leftAttrsOnComponent = matchStart.attrs.filter((attr) => !filtersAttrs.includes(attr[1])) } let attrString = (leftAttrsOnComponent || []).reduce((result, item) => result = result + (item[0] || ''), '') return attrString; } } /* @description:因为阿里的组件上的样式会被直接忽略掉,所以编译层要做一层标签的包裹处理; 注意只处理alipay端的组件,其他的不作处理 */ exports.preParseAliComponent = function(source, type, options) { if (type === 'alipay') { let usingComponents = options.usingComponents || []; let buildInComponents = options.buildInComponents || {}; let exceptTags = ['carousel-item', 'c-tab-item', 'checkbox', 'radio'];// 用于包括哪些组件标签不用被view标签包裹 let callbacks = {startCallback: exports.startCallback}; let htmlArr = exports.preParseHTMLtoArray(source, type, options, callbacks); let newHtmlArr = []; htmlArr.forEach((item) => { let isExpectTags = exceptTags.includes(item.tagName) if (item.type === 'tagContent') { // 标签内置直接push内容 newHtmlArr.push(item.content); } if (item.type === 'tagEnd') { // 结束标签的话,先将该标签的内容push,然后判断是否是组件 newHtmlArr.push(item.content); let isComponent = usingComponents.find((comp) => comp.tagName === item.tagName) || Object.keys(buildInComponents).includes(item.tagName); if (isComponent && !isExpectTags) { newHtmlArr.push(''); } } if (item.type === 'tagStart') { // 先 push view标签,然后再push组件标签 let isComponent = usingComponents.find((comp) => comp.tagName === item.tagName) || Object.keys(buildInComponents).includes(item.tagName); let inheritNodes = (item.attrs || []).filter((attr) => { let inheritAttrsFromComp = ['c-if', 'c-else', 'c-else-if', 'v-if', 'v-else', 'v-else-if', 'class', 'style', 'v-bind:style', 'v-bind:class', ':style', ':class', 'c-show', 'v-show', 'id']; // eslint-disable-next-line let inheritEvent = ['c-bind:click', 'c-bind:tap', 'c-bind:touchstart', 'c-bind:touchmove', 'c-bind:touchend', 'c-bind:touchcancel', 'c-catch:click', 'c-catch:tap', 'c-catch:touchstart', 'c-catch:touchmove', 'c-catch:touchend', 'c-catch:touchcancel']; let isInherit = inheritAttrsFromComp.includes(attr[1]) || inheritEvent.includes(attr[1]) || /^data-/.test(attr[1]) return isInherit; }); let inheritString = inheritNodes.reduce((result, styleClassNode) => result = result + (styleClassNode[0] || ''), ''); if (isComponent && !isExpectTags) { // 如果是组件需要从组件继承一些属性过来 if (!item.isunary) { // 如果不是一元标签,那么只在该标签前面push一个view newHtmlArr.push(``); newHtmlArr.push(item.content); } if (item.isunary) { // 如果是一元标签,那么在该标签前后都要push view newHtmlArr.push(``); newHtmlArr.push(item.content); newHtmlArr.push('') } } else { // 不是内置组件直接push newHtmlArr.push(item.content) } } }); return newHtmlArr.join('') } return source; } /* @description:提供一个将模板转化为数组的方法,以后各个端如果对模板的处理在jsx中做不了的话,可以通过处理这个数组进行解决 解析的数组元素分为三种类型 tagStart tagEnd 和 tagContent,其中tagStart有可能是自闭标签 @params:html模板 @return:html模板对应的字符串 */ exports.preParseHTMLtoArray = function(html, type, options, 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}[^>]*>`); // eslint-disable-next-line 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 = !!utils.trim(end[1] || '');// 标记是否是一元标签 advance(end[0].length) let attrString = startCallback(matchStart, type, options) || ''; 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; } } } // 注意,所有要再次使用babylon解析html模板的,必须再次将模板转化为符合jsx语法 exports.preParseDiffPlatformTag = function(htmlContent, type) { let activeTags = tagMap.diffTagMap[type] || []; let deadTags = [] Object.keys(tagMap.diffTagMap).forEach(key => { if (key !== type) { deadTags = deadTags.concat(tagMap.diffTagMap[key]) } }) activeTags.forEach(tag => { htmlContent = exports.activeTagHandler(tag, htmlContent); }) deadTags.forEach(tag => { htmlContent = exports.deadTagHandler(tag, htmlContent); }) return htmlContent } exports.deadTagHandler = function(tag, content) { var contentReg = new RegExp(`<\\s*${tag}[\\s\\S]*?>[\\s\\S]*?<\\s*\\/\\s*${tag}[\\s\\S]*?>`, 'ig') content = content.replace(contentReg, '') return content } exports.activeTagHandler = function (tag, content) { let startreg = new RegExp(`<\\s*${tag}[\\s\\S]*?>`, 'ig') let endreg = new RegExp(`<\\s*\\/\\s*${tag}[\\s\\S]*?>`, 'ig') content = content.replace(startreg, '') content = content.replace(endreg, '') return content; } // 模板前置处理器 // 预处理:属性 :name="sth" ==> v-bind:name="sth",因为jsx识别不了 :name="sth" exports.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 */ exports.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', ''); return `c-catch:${$1}=`; } else { return `c-bind:${$1}=` } }); return content; } // 处理 {{}} 里面的 > < ==> exports.preParseGtLt = function(content) { let reg = /{{([\s\S]*?)}}/g; return content.replace(reg, function(match) { return exports._operationGtLt(match); }) } // 预处理 标签内的 {{item.id}} 这种语法jsx无法识别,转化为 _cml{item.id}lmc_ exports.preParseMustache = function (content) { let reg = />([\s\S]*?)<[a-zA-Z\/\-_]+?/g; return content.replace(reg, function (match, key) { return exports._operationMustache(match); }) } exports.preDisappearAnnotation = function (content) { let annotionReg = //g; return content.replace(annotionReg, function (match) { return ''; }) } // 将模板预处理符合jsx解析规则,比如 : {{}} 等 exports.preParseTemplateToSatisfactoryJSX = function(source, callbacks) { // 预处理html模板中的注释,jsx不支持,这个需要优先处理,防止解析 < > 的时候出现问题; callbacks.forEach((callback) => { source = exports[callback](source); }) return source; } exports.preParseAnimation = function(source, type) { // 这个只在小程序端增加callback; // if (type === 'wx' || type === 'alipay' || type === 'baidu' || type === 'qq') { let miniAppType = ['wx', 'alipay', 'baidu', 'qq', 'tt']; if (miniAppType.includes(type)) { let callbacks = ['preDisappearAnnotation', 'preParseGtLt', 'preParseBindAttr', 'preParseVueEvent', 'preParseMustache', 'postParseLtGt'] source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (t.isJSXAttribute(node) && (node.name.name === 'c-animation' || node.name.name === 'v-animation')) { let value = utils.trimCurly(node.value.value).trim(); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('c-bind:transitionend'), t.stringLiteral(`_animationCb('${value}',$event)`))) } } }); // 这里注意,每次经过babel之后,中文都需要转义过来; return exports.postParseUnicode(generate(ast).code); } return source; } // 语法检查:这个不参与真正的模板编译; // vue语法不能写c-bind {{}} c-show c-if c-model c-text c-animation c-for // cml语法不能写 @ : v-show v-if v-model v-text v-animation v-for exports.preParseEventSyntax = function (content) { 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 `v-on:${$1}=`; } else { $1 = $1 === 'click' ? 'tap' : $1; return `v-on:${$1}=` } }); return content; } // 仅仅对跨端组件进行语法校验:不能是 .web.cml .weex.cml .alipay.cml .wx.cml .baidu.cml结尾的 exports.preCheckTemplateSyntax = function(source, type, options) { let {lang, filePath} = options; // 多态组件不进行语法校验 let polymorphicCompSuffix = `.${type}.cml`; let crossPlatformSuffix = '.cml'; let ispolymorphicComp = filePath.endsWith(polymorphicCompSuffix); // 跨端组件肯定不能是 .web.cml .weex.cml .alipay.cml .wx.cml .baidu.cml结尾的 let iscrossPlatform = !ispolymorphicComp && filePath.endsWith(crossPlatformSuffix); let errorInfo if (lang === 'vue' && iscrossPlatform) { try { errorInfo = exports.preCheckTemplateSyntaxForVue(source, type, options) } catch (e) { errorInfo = 'vue syntax error ' } } if (lang === 'cml' && iscrossPlatform) { try { errorInfo = exports.preCheckTemplateSyntaxForCml(source, type, options) } catch (e) { errorInfo = 'cml syntax error ' } } return errorInfo } exports.preCheckTemplateSyntaxForVue = function(source, type, options) { let {lang} = options; if (lang === 'vue') { let callbacks = ['preDisappearAnnotation', 'preParseEventSyntax', 'preParseGtLt', 'preParseBindAttr', 'preParseMustache', 'postParseLtGt'] source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); let errorInfo = ''; let directiveError = []; let twoWayBindError; let eventBindingError; let disabledDirective = ['c-if', 'c-else-if', 'c-else', 'c-show', 'c-text', 'c-model', 'c-animation', 'c-for'] const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (directiveError.length <= disabledDirective.length && t.isJSXAttribute(node) && disabledDirective.includes(node.name.name)) { errorInfo += `${node.name.name} can't be used with vue syntax ;` !directiveError.includes(node.name.name) && directiveError.push(directiveError) } if (!twoWayBindError && t.isJSXAttribute(node) && node.value && utils.isMustacheReactive(node.value.value)) { errorInfo += '
can not be used with vue syntax,please use
或者
' twoWayBindError = true; } if (!eventBindingError && t.isJSXNamespacedName(node.name) && node.name.namespace.name === 'c-bind') { errorInfo += "with vue syntax you can not use 'c-bind' to get event binded , please use @ or v-on;" eventBindingError = true } } }); return errorInfo } } exports.preCheckTemplateSyntaxForCml = function(source, type, options) { let {lang} = options; if (lang === 'cml') { let callbacks = ['preDisappearAnnotation', 'preParseEventSyntax', 'preParseGtLt', 'preParseBindAttr', 'preParseMustache', 'postParseLtGt'] source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); let errorInfo = ''; let directiveError = []; let twoWayBindError; let eventBindingError; let disabledDirective = ['v-if', 'v-else-if', 'v-else', 'v-show', 'v-text', 'v-model', 'v-animation', 'v-for'] const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (directiveError.length <= disabledDirective.length && t.isJSXAttribute(node) && disabledDirective.includes(node.name.name)) { errorInfo += `${node.name.name} can't be used with cml syntax ;` !directiveError.includes(node.name.name) && directiveError.push(directiveError) } if (!twoWayBindError && t.isJSXNamespacedName(node.name) && node.name.namespace.name === 'v-bind') { errorInfo += '
或者
can not be used with cml syntax,please use
' twoWayBindError = true; } if (!eventBindingError && t.isJSXNamespacedName(node.name) && node.name.namespace.name === 'v-on') { errorInfo += "with cml syntax you can not use @ or v-on to get event binded , please use 'c-bind';" eventBindingError = true; } } }); return errorInfo } } // 模板后置处理器 exports.postParseMustache = function (content) { let reg = />([\s\S]*?)<[a-zA-Z\/\-_]+?/g; return content.replace(reg, function (match, key) { return exports._deOperationMustache(match); }) } exports.postParseLtGt = function(content) { let reg = /{{([\s\S]*?)}}/g; return content.replace(reg, function(match) { return exports._deOperationGtLt(match); }) } exports.postParseUnicode = function(content) { let reg = /\\u/g; return unescape(content.replace(reg, '%u')); } /** * 校验 template 模板下如果有 cml 标签,则必须是第一层,且在第一层不能有其他标签; */ exports.checkTemplateChildren = function(path) { let node = path.node; let children = node.children || []; let jsxElements = children.filter((child) => t.isJSXElement(child)); let hasCMLTag = jsxElements.some((ele) => ele.openingElement.name.name === 'cml'); let hasOtherTag = jsxElements.some((ele) => ele.openingElement.name.name !== 'cml'); return {hasCMLTag, hasOtherTag, jsxElements}; } /* 校验 cml 标签的父元素必须是 template; */ exports.checkCMLParent = function(path) { let node = path.node; if (node.openingElement.name.name === 'cml') { let parentNode = path.parentPath && path.parentPath.node; if (parentNode && parentNode.openingElement && parentNode.openingElement.name.name !== 'template') { throw new Error('模板多态标签 cml 只允许在 template 标签的第一层'); } } } /** * 获取 符合多态模板结构的 template 标签下对应平台的cml标签里的内容; * @params:jsxElements template 节点下所有的 元素节点标签 * @params:type 当前平台对应的 type,wx baidu alipay 等 */ exports.getCurrentPlatformCML = function(jsxElements, type) { let currentCML = jsxElements.find((ele) => { let typeAttr = ele.openingElement.attributes.find((attr) => attr.name.name = 'type'); if (!typeAttr) { throw new Error('cml 标签必须有 type 属性,标识用于哪端的代码') } let typeValue = typeAttr && typeAttr.value && typeAttr.value.value; return typeValue.includes(type) }); // 有对应平台的 type 找到之后直接return 这个节点 if (currentCML) { return currentCML; } let baseCML = jsxElements.find((ele) => { let typeAttr = ele.openingElement.attributes.find((attr) => attr.name.name = 'type'); if (!typeAttr) { throw new Error('cml 标签必须有 type 属性,标识用于哪端的代码') } let typeValue = typeAttr && typeAttr.value && typeAttr.value.value; return typeValue.includes('base') }); if (!currentCML && baseCML) { return baseCML; } } /* @source: 源 template 文件 @type:要编译的平台 */ exports.postParseOriginTag = function(source, type) { let callbacks = ['preDisappearAnnotation', 'preParseGtLt', 'preParseBindAttr', 'preParseMustache', 'postParseLtGt']; source = exports.postParseUnicode(source); source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (t.isJSXElement(node) && (node.openingElement.name && typeof node.openingElement.name.name === 'string')) { if (node.openingElement.name.name.indexOf('origin-') === 0) { let currentTag = node.openingElement.name.name; let targetTag = currentTag.replace('origin-', '') node.openingElement.name.name = targetTag; node.closingElement && (node.closingElement.name.name = targetTag); } } } }); // 这里注意,每次经过babel之后,中文都需要转义过来; return exports.postParseUnicode(generate(ast).code); } /* 提供给 chameleon-loader 用于删除多态模板多其他端的不用的代码 @params:source 模板内容 @params:type 当前要编译的平台,用于截取多态模板 @params:options needTranJSX 需要转化为jsx可以解析的模板;needDelTemplate 需要删除template节点 */ exports.preParseMultiTemplate = function(source, type, options = {}) { try { if (options.needTranJSX) { // 当调用这个方法之前没有事先转义jsx,那么就需要转义一下 let callbacks = ['preDisappearAnnotation', 'preParseGtLt', 'preParseBindAttr', 'preParseVueEvent', 'preParseMustache', 'postParseLtGt']; source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); } let isEmptyTemplate = false; const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (t.isJSXElement(node) && (node.openingElement.name && typeof node.openingElement.name.name === 'string' && node.openingElement.name.name === 'template')) { path.stop();// 不要在进行子节点的遍历,因为这个只需要处理template let {hasCMLTag, hasOtherTag, jsxElements} = exports.checkTemplateChildren(path); if (hasCMLTag && hasOtherTag) { throw new Error('多态模板里只允许在template标签下的一级标签是cml'); } if (hasCMLTag && !hasOtherTag) {// 符合多态模板的结构格式 let currentPlatformCML = exports.getCurrentPlatformCML(jsxElements, type); if (currentPlatformCML) { currentPlatformCML.openingElement.name.name = 'view'; // 这里要处理自闭和标签,没有closingElement,所以做个判断; currentPlatformCML.closingElement && (currentPlatformCML.closingElement.name.name = 'view'); node.children = [currentPlatformCML]; if (options.needDelTemplate) { // 将template节点替换成找到的cml type 节点; path.replaceWith(currentPlatformCML) } } else { // 如果没有写对应平台的 cml type='xxx' 或者 cml type='base',那么报错 throw new Error('没有对应平台的模板或者基础模板') } } else { // 不是多态模板 // 注意要考虑空模板的情况 if (options.needDelTemplate && jsxElements.length === 1) { // 将template节点替换成找到的cml type 节点; path.replaceWith((jsxElements[0])); } else { isEmptyTemplate = true; } } } } }); // 这里注意,每次经过babel之后,中文都需要转义过来; if (isEmptyTemplate) { return ''; } source = exports.postParseUnicode(generate(ast).code); if (/;$/.test(source)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的; source = source.slice(0, -1); } return source; } catch (e) { console.log('preParseMultiTemplate', e) } } // cli仓库使用 exports.analyzeTemplate = function(source, options) { try { let callbacks = ['preDisappearAnnotation', 'preParseGtLt', 'preParseBindAttr', 'preParseVueEvent', 'preParseMustache', 'postParseLtGt'];// //这些预处理是为了让jsx可以处理 if (!source) { return options; } source = exports.preParseTemplateToSatisfactoryJSX(source, callbacks); source = exports.preParseMultiTemplate(source, options.cmlType, {needDelTemplate: true}) const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { 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'} if (targetTag) { (!options.usedBuildInTagMap) && (options.usedBuildInTagMap = {}); options.usedBuildInTagMap[currentTag] = targetTag; } } } }); return options; } catch (e) { console.log('analyzeTemplate', e) } } // 模块内置方法 // 这里主要处理1 >{{}}< 双花括号之间的 ==> _cml{}lmc_ ,因为jsx无法识别 {{}} // 2 同时将 {{}}内的 _cml_gt_lmc_ _cml_lt_lmc_ 复原 < > exports._operationMustache = function (content) { let mustacheReg = /{{([\s\S]*?)}}/g return content.replace(mustacheReg, function (match, key) { key = exports._deOperationGtLt(key); return `_cml{${key}}lmc_` }) } exports._deOperationMustache = function (content) { let deMustacheReg = /_cml{([\s\S]*?)}lmc_/g; return content.replace(deMustacheReg, function (match, key) { return `{{${key}}}` }) } exports._operationGtLt = function(content) { let gtltReg = />|') { return '_cml_gt_lmc_' } if (match === '<') { return '_cml_lt_lmc_' } return match; }) } exports._deOperationGtLt = function(content) { let deGtLtReg = /_cml_gt_lmc_|_cml_lt_lmc_/g; return content.replace(deGtLtReg, function(match) { if (match === '_cml_gt_lmc_') { return '>'; } if (match === '_cml_lt_lmc_') { return '<'; } return match; }) } exports.transformNativeEvent = function(source) { let reg = /__CML_NATIVE_EVENTS__/g; return source.replace(reg, '.native'); } ================================================ FILE: packages/chameleon-template-parse/src/common/utils.js ================================================ // --inspect-brk const t = require('@babel/types'); const _ = module.exports = {}; const utils = _; const babylon = require('babylon'); const traverse = require('@babel/traverse')["default"]; const generate = require('@babel/generator')["default"]; const weexMixins = require('chameleon-mixins/weex-mixins.js'); _.trimCurly = (str) => str.replace(/(?:{{)|(?:}})/ig, ''); _.getModelKey = function(modelKey) { modelKey = _.trimCurly(modelKey); modelKey = modelKey.trim(); return modelKey; } // 驼峰化单词 _.camelize = function(str) { return str.replace(/[-_\s]+(.)/g, function(match, key) { return key ? key.toUpperCase() : ''; }) } // 中划线化单词 _.dasherise = function(str) { return str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-') .toLowerCase(); } _.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*\)\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 } } _.titleLize = function (word) { return word.replace(/^\w/, function (match) { return match.toUpperCase(); }) } // ast遍历相关 _.getSiblingPaths = function (path) { let container = path.container; let siblingPaths = []; for (let i = 0; i < container.length; i++) { siblingPaths.push(path.getSibling(i)); } return siblingPaths; } /* 获取某个jsxElement 上的某个具体属性的值* @params: path:代表JSXElement的path值 return 该JSXElement上所有的属性集合 */ _.getJSXElementAttrKeyValue = function (path) { let attributes = path.node.openingElement.attributes || []; return attributes.reduce((attrMap, attr) => { let key = attr.name.name; let value = attr.value.value; if (typeof key === 'string') { attrMap[key] = value } return attrMap; }, {}); } _.trim = function (value) { return value.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }; _.isInlineStatementFn = function (statament) { let reg = /\(([\s\S]*?)\)/; return statament.match(reg); } // 判断函数参数值在微信中是否是响应式的属性,'index' 代表字符串 'index'+1 代表表达式; _.isReactive = function (value) { let reg = /(?:^'([^']*?)'$)/; return _.trim(value).match(reg); } _.doublequot2singlequot = function (value) { return value.replace(/["]/g, "'"); } _.isMustacheReactive = function (value) { let reg = /(?=.*[{]{2})(?=.*[}]{2})/; return reg.test(value); } _.isOnlySpaceContent = function(value) { let reg = /[^\s]+/; return !reg.test(value); } _.getReactiveValue = function(nodeValue) { let vLength = nodeValue && nodeValue.length; const newValue = []; if (nodeValue && _.isMustacheReactive(nodeValue)) { for (let idx = 0; idx < vLength; idx++) { if (nodeValue[idx] === '{' && idx < vLength - 1 && nodeValue[idx + 1] === '{') { newValue.push("'+("); idx++; } else if (nodeValue[idx] === '}' && idx < vLength - 1 && nodeValue[idx + 1] === '}') { newValue.push(")+'"); idx++; } else { newValue.push(nodeValue[idx]); } } if (newValue.length != 0) { newValue.push("'"); newValue.unshift("'"); const nl = newValue.length; if (nl > 2 && newValue[nl - 1] === "'" && newValue[nl - 2] === ")+'") { newValue.splice(-2, 2); newValue.push(')'); } if (newValue.length > 2 && newValue[0] === "'" && newValue[1] === "'+(") { newValue.splice(0, 2); newValue.unshift('('); } nodeValue = newValue.join(''); } } return nodeValue; }; // 从 let value = `a b {{true ? 'cls1':'cls2'}} c d {{true ? 'cls1':'cls2'}} `;获取 `a b c d` _.getStaticValueFromMixinValue = function(value) { let reg = /[{]{2}[^{}]*?[}]{2}/g; return value.replace(reg, ' '); } /** * @params: * {{'width{'}} {{'width+++'}} cpx:300 cpx:'300cpx' @returnvalue: 将非变量cpx单位转化为rpx; 转化策略: 1 首先将非 {{}} 内的cpx全部转化为rpx,因为非{{}}内的肯定都是cpx这个单位; 2 转化 {{}} 内的cpx 单位,而不能转化变量cpx, */ _.transformWxDynamicStyleCpxToRpx = function(value) { let reg = /[{]{2}([^{}]*?)[}]{2}/g; value = _.transformNotInMustacheCpxToRpx(value); value = value.replace(reg, (match, statement) => `{{${_.transformMustacheCpxToRpx(statement)}}}`); value = _.doublequot2singlequot(value) return value; } _.transformNotInMustacheCpxToRpx = function(value) { let isNotMustacheCpxToRpxReg = /([^{}]+)?(\{\{[^{}]+\}\})?/g; let temp = ''; value.replace(isNotMustacheCpxToRpxReg, (match, $1, $2, $3) => { if ($1) { temp += $1.replace(/cpx/g, 'rpx'); } if ($2) { temp += $2; } }); return temp } _.transformMustacheCpxToRpx = function(source) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { let node = path.node; if (t.isStringLiteral(node)) { if (node.value.includes('cpx')) { node.value = node.value.replace(/cpx/g, `rpx`); } } } }) let result = generate(ast).code; if (/;$/.test(result)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的; result = result.slice(0, -1); } return result } _.handleVUEClassNodes = function (options) { let { type } = options; _[`${type}VUEClassNodes`](options); } _.webVUEClassNodes = function (options) { let { classNodes, attributes, extraClass } = options; if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else if (classNodes.length === 1) { // 可能是动态class或者静态class classNodes.forEach((itemNode) => { if (t.isJSXNamespacedName(itemNode.name)) { // 动态的 extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else {// 静态的 itemNode.value.value = `${itemNode.value.value} ${extraClass}` } }) } else if (classNodes.length === 2) { classNodes.forEach((itemNode) => { if (t.isJSXNamespacedName(itemNode.name)) { // 动态的 } else { // 静态的 itemNode.value.value = `${itemNode.value.value} ${extraClass}` } }) } } _.weexVUEClassNodes = function (options) { let { classNodes, attributes, extraClass } = options; if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else if (classNodes.length === 1) { // 可能是动态class或者静态class classNodes.forEach((itemNode) => { if (t.isJSXNamespacedName(itemNode.name)) { // 动态的 extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))); let newClassNodeValue = utils.trimCurly(itemNode.value.value); itemNode.value.value = `${weexMixins.weexClassProxy}((${newClassNodeValue}))` } else {// 静态的 itemNode.value.value = ` ${extraClass} ${itemNode.value.value}` } }) } else if (classNodes.length === 2) { classNodes.forEach((itemNode) => { if (t.isJSXNamespacedName(itemNode.name)) { // 动态的 // itemNode.value.value = `[(${itemNode.value.value})]`; let newClassNodeValue = utils.trimCurly(itemNode.value.value); itemNode.value.value = `${weexMixins.weexClassProxy}((${newClassNodeValue}))` } else { // 静态的 itemNode.value.value = ` ${extraClass} ${itemNode.value.value}` } }) } } _.miniappVUEClassNodes = function (options) { let { classNodes, attributes, extraClass } = options; if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else if (classNodes.length === 1) { // 可能是动态class或者静态class classNodes.forEach((itemNode) => { // itemNode.value.value = `${itemNode.value.value} ${extraClass}` if (t.isJSXNamespacedName(itemNode.name)) { let newValue = `{{${itemNode.value.value}}} ${extraClass}`; let classNodeIndex = attributes.indexOf(itemNode) if (classNodeIndex !== -1) { attributes.splice(classNodeIndex, 1) } attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(newValue))); } else { itemNode.value.value = `${itemNode.value.value} ${extraClass}` } }) } else if (classNodes.length === 2) { let reactiveClassNode = classNodes.find((item) => t.isJSXNamespacedName(item.name)); let staticClassNode = classNodes.find((item) => t.isJSXIdentifier(item.name)); let reactiveClassNodeValue = reactiveClassNode && reactiveClassNode.value.value; let staticClassNodeValue = staticClassNode && staticClassNode.value.value; let newValue = `{{${reactiveClassNodeValue}}} ${staticClassNodeValue} ${extraClass}` attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(newValue))) classNodes.forEach((classNode) => { let classNodeIndex = attributes.indexOf(classNode); if (classNodeIndex !== -1) { attributes.splice(classNodeIndex, 1) } }) } } // 转换 $event参数 _.getInlineStatementArgs = function(argsStr) { // argsStr:"1,'index'+1,$event,'item',index+1,item" const result = argsStr.split(',').reduce((result, current, index) => { // if (current === '$event') { if (/\s*?\$event\s*?/.test(current)) { result.push("'$event'"); } else { result.push(current) } return result }, []); return result.join();// "1,'index'+1,'$event','item',index+1,item" } _.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; } // 判断是否是原生组件 _.isNativeComp = function(tagName, options) { let usedComponentInfo = (options.usingComponents || []).find((item) => item.tagName === tagName) let isNative = usedComponentInfo && usedComponentInfo.isNative; return isNative } // 判断是否是组件,不包括第三方原生组件 // {button: "cml-buildin-button"}, _.isNotNativeComponent = function(tagName, options) { let usingComponents = options.usingComponents || []; let buildInComponents = options.buildInComponents || {}; let isComponent = usingComponents.find((comp) => ((comp.tagName === tagName) && !comp.isNative) ) || Object.values(buildInComponents).includes(tagName); return isComponent } ================================================ FILE: packages/chameleon-template-parse/src/compile-template-cml.js ================================================ const babylon = require('babylon'); const traverse = require('@babel/traverse')['default']; const generate = require('@babel/generator')['default']; // traverse path的时候解析path const parseTemplate = require('./parser/index.js'); // 对于模板的预处理 - 后置处理 - 等正则的一些替换; const processTemplate = require('./common/process-template.js') // 目前事件的处理有两处:第一,c-bind,第二c-model,两者互相不影响;借鉴于此,需要新增处理事件支持传参的形式,而此时就需要处理c-bind; exports.compileTemplateForCml = function (source, type, options) { // source // 预处理html模板中的注释,jsx不支持,这个需要优先处理,防止解析 < > 的时候出现问题; source = processTemplate.preDisappearAnnotation(source); // 预处理:< > ==> _cml_lt_lmc_ _cml_gt_lmc_,这么做的目的为了防止 preParseMustache 解析 > < 中间的内容报错,所以需要将 > < 先转化 gt lt的形式,等 preParseMustache 解析完毕之后即可将其转化回来; source = processTemplate.preParseGtLt(source); source = processTemplate.preParseDiffPlatformTag(source, type); // 预处理:属性 jsx不支持 :name="sth" ==> v-bind:name="sth" source = processTemplate.preParseBindAttr(source); // 预处理vue事件 @click="handleClick" ==> c-bind:click="handleClick" source = processTemplate.preParseVueEvent(source); // 预处理 标签内的 {{item.id}} 这种语法jsx无法识别,转化为 _cml{item.id}lmc_ source = processTemplate.preParseMustache(source); // 预处理:解析_cml_lt_lmc_ ==> <; _cml_gt_lmc_ ==> > source = processTemplate.postParseLtGt(source); // 预处理c-animation 标签,给这个标签增加一个 c-bind:transitionend = "_animationCb(value,$event)",注意这个必须在所有预处理的最后,因为前面的预处理兼容了jsx的语法; source = processTemplate.preParseAnimation(source, type); source = processTemplate.preParseAliComponent(source, type, options); if (type === 'web') { source = compileWebTemplate(source, type, options).code; } if (type === 'weex') { source = compileWeexTemplate(source, type, options).code; } if (type === 'wx') { source = compileWxTemplate(source, type, options).code; } if (type === 'qq') { source = compileQqTemplate(source, type, options).code; } if (type === 'alipay') { source = compileAliPayTemplate(source, type, options).code; } if (type === 'baidu') { source = compileBaiduTemplate(source, type, options).code; } if (type === 'tt') { source = compileTtTemplate(source, type, options).code; } // 后置处理,解析origin-tag ==> tag source = processTemplate.postParseOriginTag(source, type) // 后置处理:解析_cml{str}lmc_ ==> {{str}} source = processTemplate.postParseMustache(source) // 后置处理:用于处理 \u ,便于解析unicode 中文 source = processTemplate.postParseUnicode(source); // 后置处理,所有的 __CML_NATIVE_EVENTS__ ==> .native source = processTemplate.transformNativeEvent(source) return { source, usedBuildInTagMap: options.usedBuildInTagMap } } function compileWebTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); parseTemplate.parseTagForSlider(path, type, options); parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options) // 处理 c-bind c-catch parseTemplate.parseIterationStatement(path, type, options);// 处理 c-for // 解析c-model ==> v-bind:value="modelValue" v-on:input="_cmlModelEventProxy($event,modelKey)" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseStyleStatement(path, type, options); // wx alipay 的 {{}}语法中转换成vue属性表达式 所有的响应式属性的处理成vue识别的; propname="a{{b}}" -> :propname = "'a'+(b)" parseTemplate.parseAttributeStatement(path, type, options); // 最后处理模板中的标签 block-->template parseTemplate.afterParseTag(path, type, options); } }) return generate(ast); } function compileWeexTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 这个最优先; parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options) parseTemplate.parseIterationStatement(path, type, options); // 解析c-model ==> v-bind:value="modelValue" v-on:input="_cmlModelEventProxy($event,modelKey)" parseTemplate.parseDirectiveStatement(path, type, options); // style的处理判断是否是动态和静态的依赖{{}} 需要在parseAttributeStatement之前执行,parseAttributeStatement会处理掉{{}} parseTemplate.parseStyleStatement(path, type, options) // {{}}中转换成vue属性表达式 class="a{{b}}" -> :class = "'a'+(b)" parseTemplate.parseAttributeStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); } }) return generate(ast); } function compileWxTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持ref parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; // 微信端需要特殊处理支持 component parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options); // 解析c-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseIterationStatement(path, type, options); // parseTemplate.parseStyleStatement(path, type, options); // parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileQqTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持ref parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; // 微信端需要特殊处理支持 component parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options); // 解析c-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseIterationStatement(path, type, options); // parseTemplate.parseStyleStatement(path, type, options); // parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileAliPayTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持ref parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; // 微信端需要特殊处理支持 component parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options); // 解析c-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseIterationStatement(path, type, options); // parseTemplate.parseStyleStatement(path, type, options); // 用于支持 parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileBaiduTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持ref parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; // 微信端需要特殊处理支持 component parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options); // 解析c-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseIterationStatement(path, type, options); // parseTemplate.parseStyleStatement(path, type, options); // parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileTtTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持ref parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; // 微信端需要特殊处理支持 component parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseConditionalStatement(path, type, options);// 替换c-if c-else parseTemplate.parseEventListener(path, type, options); // 解析c-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseIterationStatement(path, type, options); // parseTemplate.parseStyleStatement(path, type, options); // parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } ================================================ FILE: packages/chameleon-template-parse/src/compile-template-vue.js ================================================ const babylon = require('babylon'); const traverse = require('@babel/traverse')['default']; const generate = require('@babel/generator')['default']; // traverse path的时候解析path const parseTemplate = require('./parser/index.js'); // 对于模板的预处理 - 后置处理 - 等正则的一些替换; const processTemplate = require('./common/process-template.js') // 目前事件的处理有两处:第一,c-bind,第二c-model,两者互相不影响;借鉴于此,需要新增处理事件支持传参的形式,而此时就需要处理c-bind; exports.compileTemplateForVue = function (source, type, options) { // source // 预处理html模板中的注释,将其删除;这个需要优先处理,防止解析 < > 的时候出现问题; source = processTemplate.preDisappearAnnotation(source); // 预处理:< > ==> _cml<&lmc_ _cml>&lmc_,这么做的目的为了防止 preParseMustache 解析 > < 中间的内容报错,所以需要将 > < 先转化 gt lt的形式,等 preParseMustache 解析完毕之后即可将其转化回来; source = processTemplate.preParseGtLt(source); source = processTemplate.preParseDiffPlatformTag(source, type); // 预处理:属性 :name="sth" ==> v-bind:name="sth" jsx无法解析 :name="sth" source = processTemplate.preParseBindAttr(source); // 预处理vue事件 @click="handleClick" ==> c-bind:click="handleClick" source = processTemplate.preParseVueEvent(source); // 预处理 标签内的 {{item.id}} 这种语法jsx无法识别,转化为 _cml{item.id}lmc_ source = processTemplate.preParseMustache(source); // 后置处理:解析_cml_lt_lmc_ ==> < _cml_gt_lmc_ ==> > source = processTemplate.postParseLtGt(source); // 预处理c-animation 标签,给这个标签增加一个 c-bind:transitionend = "_animationCb(value,$event)",注意这个必须在所有预处理的最后,因为前面的预处理兼容了jsx的语法; source = processTemplate.preParseAnimation(source, type); source = processTemplate.preParseAliComponent(source, type, options); if (type === 'web') { source = compileWebTemplate(source, type, options).code; } if (type === 'weex') { source = compileWeexTemplate(source, type, options).code; } if (type === 'wx') { source = compileWxTemplate(source, type, options).code; } if (type === 'qq') { source = compileQqTemplate(source, type, options).code; } if (type === 'alipay') { source = compileAliPayTemplate(source, type, options).code; } if (type === 'baidu') { source = compileBaiduTemplate(source, type, options).code; } if (type === 'tt') { source = compileTtTemplate(source, type, options).code; } // 后置处理,解析origin-tag ==> tag source = processTemplate.postParseOriginTag(source, type) // 后置处理:解析_cml{str}lmc_ ==> {{str}} source = processTemplate.postParseMustache(source) // 后置处理:用于处理 \u ,便于解析unicode 中文 source = processTemplate.postParseUnicode(source); // 后置处理,所有的 __CML_NATIVE_EVENTS__ ==> .native source = processTemplate.transformNativeEvent(source) return { source, usedBuildInTagMap: options.usedBuildInTagMap } } function compileWebTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.parseEventListener(path, type, options) // 解析c-model ==> v-bind:value="modelValue" v-on:input="_cmlModelEventProxy($event,modelKey)" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseStyleStatement(path, type, options) // wx alipay 的 {{}}语法中转换成vue属性表达式 class="a{{b}}" -> :class = "'a'+(b)" parseTemplate.parseAttributeStatement(path, type, options); // 最后处理模板中的标签 block-->template parseTemplate.afterParseTag(path, type, options); } }) return generate(ast); } function compileWeexTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 这个最优先; parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.parseEventListener(path, type, options) // 解析v-model ==> v-bind:value="modelValue" v-on:input="_cmlModelEventProxy($event,modelKey)" parseTemplate.parseDirectiveStatement(path, type, options); // style的处理判断是否是动态和静态的依赖{{}} 需要在parseAttributeStatement之前执行,parseAttributeStatement会处理掉{{}} parseTemplate.parseStyleStatement(path, type, options) // {{}}中转换成vue属性表达式 class="a{{b}}" -> :class = "'a'+(b)" parseTemplate.parseAttributeStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); } }) return generate(ast); } function compileWxTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持 ref; parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseEventListener(path, type, options); // 解析v-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); // parseTemplate.parseIterationStatement(path,type,options); parseTemplate.parseStyleStatement(path, type, options); // 用于支持 v-bind:name="sth" ==> name="{{sth}}" v-for v-if parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileQqTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持 ref; parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseEventListener(path, type, options); // 解析v-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); // parseTemplate.parseIterationStatement(path,type,options); parseTemplate.parseStyleStatement(path, type, options); // 用于支持 v-bind:name="sth" ==> name="{{sth}}" v-for v-if parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileAliPayTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持 ref; parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseEventListener(path, type, options); // 解析v-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseStyleStatement(path, type, options); // 用于支持 v-bind:name="sth" ==> name="{{sth}}" v-for v-if parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileBaiduTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持 ref; parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseEventListener(path, type, options); // 解析v-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseStyleStatement(path, type, options); // 用于支持 v-bind:name="sth" ==> name="{{sth}}" v-for v-if parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } function compileTtTemplate(source, type, options) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { parseTemplate.parseClassStatement(path, type, options); // 微信端支持安震 slider parseTemplate.parseTagForSlider(path, type, options); // 微信端支持 ref; parseTemplate.parseRefStatement(path, type, options) parseTemplate.parseBuildTag(path, type, options) // 解析内置标签; parseTemplate.parseTag(path, type, options);// 替换标签; parseTemplate.parseAnimationStatement(path, type, options); parseTemplate.afterParseTag(path, type, options); parseTemplate.parseEventListener(path, type, options); // 解析v-model ==> value="{{modelValue}}" bindinput="_cmlModelEventProxy($event) data-modelkey="modelKey" parseTemplate.parseDirectiveStatement(path, type, options); parseTemplate.parseStyleStatement(path, type, options); // 用于支持 v-bind:name="sth" ==> name="{{sth}}" v-for v-if parseTemplate.parseVue2WxStatement(path, type, options); } }) return generate(ast); } ================================================ FILE: packages/chameleon-template-parse/src/index.js ================================================ const { compileTemplateForCml } = require('./compile-template-cml'); const { compileTemplateForVue } = require('./compile-template-vue'); const {analyzeTemplate, preParseMultiTemplate} = require('./common/process-template.js'); module.exports = function(source, type, options = {lang: 'cml'}) { if (!source) { return {source, usedBuildInTagMap: {}}; } let compileTemplateMap = { 'cml': compileTemplateForCml, 'vue': compileTemplateForVue }; let result = compileTemplateMap[options.lang](source, type, options); if (/;$/.test(result.source)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的; result.source = result.source.slice(0, -1); } return result; } module.exports.analyzeTemplate = analyzeTemplate; module.exports.preParseMultiTemplate = preParseMultiTemplate; ================================================ FILE: packages/chameleon-template-parse/src/parser/index.js ================================================ const t = require('@babel/types'); const {parseCondition} = require('./parse-condition.js'); const {parseEvent} = require('./parse-event.js'); const {parseInteration} = require('./parse-interation.js'); const {parseAttribute} = require('./parse-attribute.js'); const {parseStyle} = require('./parse-style.js'); const {parseVue2Wx} = require('./parse-vue2wx.js'); const {parseAnimationTag} = require('./parse-animation-tag.js'); const {parseDirective} = require('./parse-directive.js'); const {parseClass} = require('./parse-class.js'); const {parseRef} = require('./parse-ref.js'); const { tagMap } = require('../common/cml-map.js') /** * 解析原则: * 1 只处理需要处理的,不需要处理的,不满足条件则不做任何操作; * 2 为了保证解析过程的正确性,即只处理需要处理的节点,按照 节点类型 --> 平台类型 --> 这样的流程进行判断; * * */ // web wx weex exports.parseTag = function (path, type) { let node = path.node; if (t.isJSXElement(node)) { let currentTag = node.openingElement.name.name; let targetTag = tagMap.targetTagMap[currentTag] && tagMap.targetTagMap[currentTag][type]; if (targetTag && currentTag !== targetTag) { node.openingElement.name.name = targetTag; // 这里要处理自闭和标签,没有closingElement,所以做个判断; node.closingElement && (node.closingElement.name.name = targetTag); } } } exports.afterParseTag = function (path, type) { let node = path.node; if (t.isJSXElement(node)) { let currentTag = node.openingElement.name.name; let targetTag = tagMap.afterTagMap[currentTag] && tagMap.afterTagMap[currentTag][type]; if (targetTag && currentTag !== targetTag) { node.openingElement.name.name = targetTag; node.closingElement && (node.closingElement.name.name = targetTag); } } } exports.parseBuildTag = function (path, type, 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) { // 如果是用户的内置组件,这里不做任何处理,直接返回 } 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; } } } } // 配合安震,解析c-slider; exports.parseTagForSlider = function(path, type, options) { let node = path.node; if ((['wx', 'qq', 'baidu', 'alipay', 'tt'].includes(type)) && t.isJSXElement(node)) { let currentTag = node.openingElement.name.name; let targetTag = tagMap.wxTagMap[currentTag]; if (targetTag && currentTag !== targetTag) { node.openingElement.name.name = targetTag; node.closingElement && (node.closingElement.name.name = targetTag); } } } exports.parseRefStatement = function parseRefStatement(path, type, options) { let node = path.node; let lang = options.lang; // cml语法下只解析 ref节点,不解析 :ref节点 if (lang === 'cml' && t.isJSXAttribute(node) && node.name.name === 'ref') { parseRef.call({path, type, node, options}); } else if (lang === 'vue' && t.isJSXAttribute(node) && (node.name.name === 'ref' || node.name.name.name === 'ref')) { parseRef.call({path, type, node, options}); } } // web weex wx ...只处理cml语法 c-if c-else-if c-else exports.parseConditionalStatement = function parseConditionalStatement(path, type, options) { let node = path.node; if (t.isJSXAttribute(node) && (node.name.name === 'c-if' || node.name.name === 'c-else-if' || node.name.name === 'c-else' )) { parseCondition.call({path, type, node, options}) } } // web weex wx 只处理cml语法 c-bind c-catch exports.parseEventListener = function parseEventListener(path, type, options) { let node = path.node; // 对于 JSXNamespaceName节点,仅仅需要处理 c-catch c-bind的情况,其他的不要进行处理 if (t.isJSXNamespacedName(node) && (node.namespace.name === 'c-catch' || node.namespace.name === 'c-bind')) { parseEvent.call({path, type, node, options}) } } // 只支持数组,小程序不支持对象的for循环; // web weex wx 只处理cml语法 c-for exports.parseIterationStatement = function parseIterationStatement(path, type, options) { let node = path.node; if (t.isJSXAttribute(node) && node.name.name === 'c-for') { parseInteration.call({path, node, type, options}); } } // 处理所有的属性,将其转化为计算属性 {{}}中转换成vue属性表达式 class="a+{{b}}" -> :class = "'a'+(b)" // web weex wx exports.parseAttributeStatement = function parseAttributeStatement(path, type) { let node = path.node; // 将 {{}}语法转化为动态值得时候,不处理class和style,这个方法是将wx中的props={{}}的语法转化为 v-bind:props="sth"的形式; if (t.isJSXAttribute(node) && node.name.name !== 'class' && node.name.name !== 'style' && node.name.name.name !== 'class' && node.name.name.name !== 'style' ) { parseAttribute.call({path, node, type}); } }; exports.parseStyleStatement = function parseStyleStatement(path, type, options) { let node = path.node; let lang = options.lang; // node.name.name === 'style' (代表静态style) == node.name.name.name === 'style' (代表动态style) // cml语法下,不要解析 :style ,因为有可能是原生组件或者原生标签 if (lang === 'cml' && t.isJSXAttribute(node) && node.name.name === 'style') { parseStyle.call({path, node, type, options}); } else if (lang === 'vue' && t.isJSXAttribute(node) && (node.name.name === 'style' || node.name.name.name === 'style')) { parseStyle.call({path, node, type, options}); } } exports.parseClassStatement = function parseClassStatement(path, type, options) { let node = path.node; if (t.isJSXElement(node)) { parseClass.call({path, node, type, options}); } } exports.parseAnimationStatement = function parseAnimationStatement(path, type) { let node = path.node; // 由于微信端组件的名的标签会被渲染为一个单独的标签,所以需要这个hack; if (t.isJSXAttribute(node) && node.name.name === 'c-animation') { parseAnimationTag.call({path, node, type}); } } exports.parseVue2WxStatement = function parseVue2WxStatement(path, type, options) { let node = path.node; node && parseVue2Wx.call({path, node, type, options}); } exports.parseDirectiveStatement = function parseDirectiveStatement(path, type, options) { let node = path.node; node && parseDirective.call({path, node, type, options}) } ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-animation-tag.js ================================================ // import * as t from "@babel/types"; const { SyncHook } = require('tapable'); const utils = require('../common/utils'); let parseAnimationTag = new SyncHook(['args']) parseAnimationTag.tap('wx', (args) => { let { node, type } = args; if (type === 'web' || type === 'weex') { node.name.name = 'v-animation'; node.value && (node.value.value = utils.trimCurly(node.value.value)); } let miniAppType = ['wx', 'baidu', 'alipay', 'qq', 'tt'] if (miniAppType.includes(type)) { node.name.name = 'animation'; if (type === 'alipay') { node.value && (node.value.value = `{{(${utils.trimCurly(node.value.value)}).actions}}`) } } }) module.exports.parseAnimationTag = parseAnimationTag; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-attribute.js ================================================ const { SyncHook } = require("tapable"); const utils = require('../common/utils'); let parseAttribute = new SyncHook(['args']) parseAttribute.tap('web-weex', (args) => { let { node, type } = args; if (type === 'web' || type === 'weex') { let nodeName = node.name && node.name.name; let nodeValue = node.value && node.value.value; let vLength = nodeValue && nodeValue.length; const newValue = []; if (nodeValue && utils.isMustacheReactive(nodeValue)) { for (let idx = 0; idx < vLength; idx++) { if (nodeValue[idx] === '{' && idx < vLength - 1 && nodeValue[idx + 1] === '{') { newValue.push("'+("); idx++; } else if (nodeValue[idx] === '}' && idx < vLength - 1 && nodeValue[idx + 1] === '}') { newValue.push(")+'"); idx++; } else { newValue.push(nodeValue[idx]); } } if (newValue.length != 0) { newValue.push("'"); newValue.unshift("'"); const nl = newValue.length; if (nl > 2 && newValue[nl - 1] === "'" && newValue[nl - 2] === ")+'") { newValue.splice(-2, 2); newValue.push(')'); } if (newValue.length > 2 && newValue[0] === "'" && newValue[1] === "'+(") { newValue.splice(0, 2); newValue.unshift('('); } nodeValue = newValue.join(''); } if (nodeName[0] === ':') { node.value.value = nodeValue; } else { node.name.name = `:${nodeName}`; node.value.value = nodeValue; } } } }); exports.parseAttribute = parseAttribute; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-class.js ================================================ const { SyncHook } = require('tapable'); const utils = require('../common/utils'); const t = require('@babel/types'); const weexMixins = require('chameleon-mixins/weex-mixins.js') // weex: 不支持fafafa。也就注定不能这么写多个class ,但是可以 class="cls1 cls2 cls3" let parseClass = new SyncHook(['args']); const hash = require('hash-sum'); // weex对于动态样式的处理 简直 amazing // cml语法:支持的写法如下:class="cls1 cls2" class="{{true ? 'cls1 cls2':'cls3 cls4'}}" /** *test class1 test class1 test class1 */ parseClass.tap('web-cml', (args) => { let { node, type, options: {lang, isInjectBaseStyle} } = args; if (lang === 'cml' && type === 'web') { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' ); let extraClass = ''; if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; } if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else if (classNodes.length === 1) { classNodes.forEach((itemNode) => { const dealedClassNodeValue = `${itemNode.value.value} ${extraClass}`; if (utils.isMustacheReactive(itemNode.value.value)) { // 包括动态class const newClassNodeValue = utils.getReactiveValue(dealedClassNodeValue); itemNode.name.name = `v-bind:${itemNode.name.name}` itemNode.value.value = `(${newClassNodeValue})` } else { // 静态class itemNode.value.value = dealedClassNodeValue } }) } else { throw new Error('Only allow one class node in element\'s attribute with cml syntax'); } } }) parseClass.tap('weex-cml', (args) => { let { node, type, options: {lang, isInjectBaseStyle} } = args; if (lang === 'cml' && type === 'weex') { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' ); let extraClass = ''; if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; } if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) } else if (classNodes.length === 1) { classNodes.forEach((itemNode) => { if (utils.isMustacheReactive(itemNode.value.value)) { // 动态的 const dealedClassNodeValue = `${extraClass} ${itemNode.value.value}` ; itemNode.name.name = `:${itemNode.name.name}` const newDynamicClassNodeValue = utils.getReactiveValue(dealedClassNodeValue); itemNode.value.value = `${weexMixins.weexClassProxy}(${newDynamicClassNodeValue})` } else { itemNode.name.name = `:${itemNode.name.name}` const newStaticClassNodeValue = `${extraClass} ${itemNode.value.value}` itemNode.value.value = `${weexMixins.weexClassProxy}('${newStaticClassNodeValue}')` } }) } else { throw new Error('Only allow one class node in element\'s attribute with cml syntax'); } } }) parseClass.tap('wx-alipay-baidu-cml', (args) => { let { node, type, options: { media, lang, filePath, usingComponents, isInjectBaseStyle } } = args; // type === 'wx' || type === 'alipay' || type === 'baidu' if (lang === 'cml' && (['wx', 'qq', 'baidu', 'alipay', 'tt'].includes(type))) { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' ); let isUsingComponents = (usingComponents || []).find((comp) => comp.tagName === tagName); let extraClass = ''; if (['wx', 'qq', 'baidu', 'tt'].includes(type)) { if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; if (isUsingComponents) { extraClass = ` cml-view cml-${tagName}`; } } } if (type === 'alipay') { let randomClassName = hash(filePath); if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; extraClass = `${extraClass} cml-${randomClassName}` } else { extraClass = `${extraClass} cml-${randomClassName}` // 不插入全局样式的时候也要插入样式隔离 } } let chameleonConfig = cml.config.get()[type][media]; const hasMiniAppCustomDataClass = chameleonConfig.hasMiniAppCustomDataClass; if (classNodes.length === 0) { extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral(extraClass))) // 添加 data-class 方便在小程序中的时间回调 event 中拿到class值 hasMiniAppCustomDataClass && extraClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('data-class'), t.stringLiteral(extraClass.trim()))) } else if (classNodes.length === 1) { const classNode = classNodes[0]; const dealedClassNodeValue = `${classNode.value.value} ${extraClass}` classNode.value.value = dealedClassNodeValue; // 添加 data-class 方便在小程序中的时间回调 event 中拿到class值 hasMiniAppCustomDataClass && attributes.push(t.jsxAttribute(t.jsxIdentifier('data-class'), t.stringLiteral(dealedClassNodeValue))) } else { throw new Error('Only allow one class node in element\'s attribute with cml syntax'); } } }) // vue语法:class='cls1 cls2' :class="true ? 'cls1 cls2 cls3' : 'cls4 cls5 cls6'" parseClass.tap('web-vue', (args) => { let { node, type, options: {lang, isInjectBaseStyle} } = args; if (lang === 'vue' && type === 'web') { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' || attr.name.name.name === 'class' ); let extraClass = ''; if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; } utils.handleVUEClassNodes({classNodes, attributes, extraClass, lang, type}) } }) parseClass.tap('weex-vue', (args) => { let { node, type, options: {lang, isInjectBaseStyle} } = args; if (lang === 'vue' && type === 'weex') { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' || attr.name.name.name === 'class' ); let extraClass = ''; if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; } utils.handleVUEClassNodes({classNodes, attributes, extraClass, lang, type}) } }) parseClass.tap('wx-alipay-baidu-vue', (args) => { let { node, type, options: {lang, filePath, usingComponents, isInjectBaseStyle} } = args; // (type === 'wx' || type === 'alipay' || type === 'baidu') if (lang === 'vue' && (['wx', 'qq', 'baidu', 'alipay', 'tt'].includes(type))) { let tagName = node.openingElement.name.name; let attributes = node.openingElement.attributes; let classNodes = attributes.filter((attr) => // 如果没有符合条件的classNodes则返回一个空数组 attr.name.name === 'class' || attr.name.name.name === 'class' ); let isUsingComponents = (usingComponents || []).find((comp) => comp.tagName === tagName); let extraClass = ''; if (['wx', 'qq', 'baidu', 'tt'].includes(type)) { if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; if (isUsingComponents) { extraClass = ` cml-view cml-${tagName}`; } } } if (type === 'alipay') { let randomClassName = hash(filePath); if (isInjectBaseStyle) { extraClass = ` cml-base cml-${tagName}`; extraClass = `${extraClass} cml-${randomClassName}` } else { extraClass = `${extraClass} cml-${randomClassName}` // 不插入全局样式的时候也要插入样式隔离 } } utils.handleVUEClassNodes({classNodes, attributes, extraClass, lang, type: 'miniapp'}) } }) module.exports.parseClass = parseClass; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-condition.js ================================================ // import * as t from "@babel/types"; const t = require('@babel/types'); const { SyncHook } = require('tapable'); const { conditionMap } = require('../common/cml-map.js'); const utils = require('../common/utils'); let parseCondition = new SyncHook(['args']) parseCondition.tap('web-weex', (args) => { let { node, type, options: {lang}} = args; if (lang === 'cml' && (type === 'web' || type === 'weex')) { let currentCondition = node.name.name; let targetCondition = conditionMap[currentCondition] && conditionMap[currentCondition][type]; if (targetCondition && currentCondition !== targetCondition) { node.name.name = targetCondition; node.value && t.isStringLiteral(node.value) && (node.value.value = utils.trimCurly(node.value.value)); } } }) parseCondition.tap('wx-alipay-qq-tt', (args) => { let { node, type, options: {lang}} = args; let miniAppType = ['wx', 'alipay', 'qq', 'tt']; if (lang === 'cml' && miniAppType.includes(type)) { let currentCondition = node.name.name; let targetCondition = conditionMap[currentCondition] && conditionMap[currentCondition][type]; if (targetCondition && currentCondition !== targetCondition) { node.name.name = targetCondition; } } }); parseCondition.tap('baidu', (args) => { let { node, type, options: {lang}} = args; if (lang === 'cml' && type === 'baidu') { let currentCondition = node.name.name; let targetCondition = conditionMap[currentCondition] && conditionMap[currentCondition][type]; if (targetCondition && currentCondition !== targetCondition) { node.name.name = targetCondition; // 百度端比较特殊是 s-if="xxx" node.value && t.isStringLiteral(node.value) && (node.value.value = utils.trimCurly(node.value.value)); } } }) module.exports.parseCondition = parseCondition; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-directive.js ================================================ const t = require('@babel/types') const weexMixins = require('chameleon-mixins/weex-mixins.js') const { SyncHook } = require('tapable'); const utils = require('../common/utils'); const eventProxy = require('chameleon-mixins/web-mixins.js'); const wxEventProxy = require('chameleon-mixins/wx-mixins.js'); let parseDirective = new SyncHook(['args']) // cml语法 parseDirective.tap('web-weex-cml', (args) => { let { path, node, type, options: {lang}} = args; if (lang === 'cml' && (type === 'web' || type === 'weex')) { // 以下开始处理指令; // v-model c-model // web端因为是自定义组件触发的 input事件的参数不对,所以不能直接用vue的v-model if (t.isJSXAttribute(node) && node.name.name === 'c-model') { let modelKey = utils.getModelKey(node.value.value); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('v-bind:value'), t.stringLiteral(modelKey))) path.insertAfter(t.jsxAttribute(t.jsxIdentifier('v-on:input'), t.stringLiteral(`${eventProxy.modelEventProxyName}($event,'${modelKey}')`))); path.remove();// 删除c-model属性节点; } // v-text c-text if (t.isJSXAttribute(node) && node.name.name === 'c-text') { let textValue = node.value.value; let JSXElemenetPath = path.find((pth) => pth.isJSXElement()); JSXElemenetPath && (JSXElemenetPath.node.children = [t.jsxText(textValue)]); path.remove();// 删除c-text; } // c-show if (t.isJSXElement(node)) { let attributes = node.openingElement.attributes; let showDirectiveNode = attributes.find((attr, i) => { if (attr.name.name === 'c-show') { attributes.splice(i, 1); } return attr.name.name === 'c-show'; }); let styleNode = attributes.find((attr) => attr.name.name === 'style' || attr.name.name.name === 'style') if (!showDirectiveNode) { return ; } if (styleNode) { throw new Error('The style attribute can\'t be used in the element that has attributes with c-show '); } let elementShow = utils.trimCurly(showDirectiveNode.value.value); let styleNodeValue = `display:{{${elementShow}?'':'none'}};{{${elementShow}?'':'height:0px;width:0px;overflow:hidden'}}` if (type === 'weex') { attributes.push(t.jsxAttribute(t.jsxIdentifier('style'), t.stringLiteral(styleNodeValue))) } else if (type === 'web') { attributes.push(t.jsxAttribute(t.jsxIdentifier('v-show'), t.stringLiteral(elementShow))) } } } }); parseDirective.tap('wx-baidu-qq-cml', (args) => { let { path, node, type, options: {lang} } = args; // type === 'wx' || type === 'baidu' || type === 'alipay' if (lang === 'cml' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { // c-model if (t.isJSXAttribute(node) && node.name.name === 'c-model') { let modelKey = utils.getModelKey(node.value.value); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('value'), t.stringLiteral(node.value.value))) if (type === 'alipay') { path.insertAfter(t.jsxAttribute(t.jsxIdentifier('onInput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('data-eventinput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))); } else { path.insertAfter(t.jsxAttribute(t.jsxIdentifier('bindinput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))); } path.replaceWith(t.jsxAttribute(t.jsxIdentifier('data-modelkey'), t.stringLiteral(`${modelKey}`))) } // c-text if (t.isJSXAttribute(node) && node.name.name === 'c-text') { let textValue = node.value.value; let JSXElemenetPath = path.find((pth) => pth.isJSXElement()); JSXElemenetPath && (JSXElemenetPath.node.children = [t.jsxText(textValue)]); path.remove();// 删除c-text; } // c-show if (t.isJSXElement(node)) { let attributes = node.openingElement.attributes; let showDirectiveNode = attributes.find((attr, i) => { if (attr.name.name === 'c-show') { attributes.splice(i, 1); } return attr.name.name === 'c-show'; }); let styleNode = attributes.find((attr) => attr.name.name === 'style' || attr.name.name.name === 'style') if (!showDirectiveNode) { return ; } if (styleNode) { throw new Error('The style attribute can\'t be used in the element that has attributes with c-show '); } let elementShow = utils.trimCurly(showDirectiveNode.value.value); let styleNodeValue = `display:{{${elementShow}?'':'none'}};{{${elementShow}?'':'height:0px;width:0px;overflow:hidden'}}` attributes.push(t.jsxAttribute(t.jsxIdentifier('style'), t.stringLiteral(styleNodeValue))) } } }) // vue语法 parseDirective.tap('web-weex-vue', (args) => { let { path, node, type, options: {lang}} = args; if (lang === 'vue' && (type === 'web' || type === 'weex')) { // 以下开始处理指令; // v-model if (t.isJSXAttribute(node) && node.name.name === 'v-model') { let modelKey = utils.getModelKey(node.value.value); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('v-bind:value'), t.stringLiteral(modelKey))) path.insertAfter(t.jsxAttribute(t.jsxIdentifier('v-on:input'), t.stringLiteral(`${eventProxy.modelEventProxyName}($event,'${modelKey}')`))); path.remove();// 删除c-model属性节点; } // v-text if (t.isJSXAttribute(node) && node.name.name === 'v-text') { let textValue = node.value.value; let JSXElemenetPath = path.find((pth) => pth.isJSXElement()); JSXElemenetPath && (JSXElemenetPath.node.children = [t.jsxText(`{{${textValue}}}`)]); path.remove();// 删除c-text; } // v-show if (t.isJSXElement(node)) { let attributes = node.openingElement.attributes; let showDirectiveNode = attributes.find((attr, i) => { if (attr.name.name === 'v-show') { attributes.splice(i, 1); } return attr.name.name === 'v-show'; }); let styleNode = attributes.find((attr) => attr.name.name === 'style' || attr.name.name.name === 'style') if (!showDirectiveNode) { return ; } if (styleNode) { throw new Error('The style attribute can\'t be used in the element that has attributes with v-show '); } let elementShow = utils.trimCurly(showDirectiveNode.value.value); let styleNodeValue = `display:{{${elementShow}?'':'none'}};{{${elementShow}?'':'height:0px;width:0px;overflow:hidden'}}`; if (type === 'weex' && styleNodeValue.indexOf('_cmlStyleProxy') === -1) { styleNodeValue = `${weexMixins.styleProxyName}(${utils.getReactiveValue(styleNodeValue)})` } if (type === 'weex') { attributes.push(t.jsxAttribute(t.jsxIdentifier(':style'), t.stringLiteral(styleNodeValue))) } else if (type === 'web') { attributes.push(t.jsxAttribute(t.jsxIdentifier('v-show'), t.stringLiteral(elementShow))) } } } }) parseDirective.tap('wx-vue', (args) => { let { path, node, type, options: {lang} } = args; if (lang === 'vue' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { if (t.isJSXAttribute(node) && node.name.name === 'v-model') { let modelKey = utils.getModelKey(node.value.value); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('value'), t.stringLiteral(`{{${node.value.value}}}`))) if (type === 'alipay') { path.insertAfter(t.jsxAttribute(t.jsxIdentifier('onInput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))); path.insertAfter(t.jsxAttribute(t.jsxIdentifier('data-eventinput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))); } else { path.insertAfter(t.jsxAttribute(t.jsxIdentifier('bindinput'), t.stringLiteral(`${wxEventProxy.modelEventProxyName}`))) } path.replaceWith(t.jsxAttribute(t.jsxIdentifier('data-modelkey'), t.stringLiteral(`${modelKey}`))) } // v-text c-text if (t.isJSXAttribute(node) && node.name.name === 'v-text') { let textValue = node.value.value; let JSXElemenetPath = path.find((pth) => pth.isJSXElement()); JSXElemenetPath && (JSXElemenetPath.node.children = [t.jsxText(`{{${textValue}}}`)]); path.remove();// 删除v-text; } if (t.isJSXElement(node)) { let attributes = node.openingElement.attributes; let showDirectiveNode = attributes.find((attr, i) => { if (attr.name.name === 'v-show') { attributes.splice(i, 1); } return attr.name.name === 'v-show'; }); let styleNode = attributes.find((attr) => attr.name.name === 'style' || attr.name.name.name === 'style') if (!showDirectiveNode) { return ; } if (styleNode) { throw new Error('The style attribute can\'t be used in the element that has attributes with v-show '); } let elementShow = utils.trimCurly(showDirectiveNode.value.value); let styleNodeValue = `display:{{${elementShow}?'':'none'}};{{${elementShow}?'':'height:0px;width:0px;overflow:hidden'}}`; attributes.push(t.jsxAttribute(t.jsxIdentifier('style'), t.stringLiteral(styleNodeValue))) } } }) module.exports.parseDirective = parseDirective; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-event.js ================================================ const t = require('@babel/types') const { SyncHook } = require('tapable'); const { eventMap } = require('../common/cml-map.js'); const utils = require('../common/utils'); const eventProxy = require('chameleon-mixins/web-mixins.js'); const wxEventProxy = require('chameleon-mixins/wx-mixins.js'); let parseEvent = new SyncHook(['args']) parseEvent.tap('web-weex', (args) => { let { path, node, type, options} = args; if (type === 'web' || type === 'weex') { let container = path.container; let value = container.value; let isStopBubble = false;// 默认都是冒泡 if (node.namespace.name === 'c-catch') { isStopBubble = true; } else { isStopBubble = false; } node.namespace.name = 'v-on'; // ====这里作用是阻止对 origin-tag标签的事件进行代理 let jsxElementNodePath = path.findParent((path) => t.isJSXElement(path.node)); let jsxElementNode = jsxElementNodePath.node; let tagName = jsxElementNode.openingElement.name.name; let isOriginOrNative = utils.isOriginTagOrNativeComp(tagName, options); let isNotNativeComp = utils.isNotNativeComponent(tagName, options); let originEvents = ['tap', 'click', 'touchstart', 'touchmove', 'touchend', 'touchcancel']; if (type === 'web') { if (isNotNativeComp || tagName === 'component') { // cml组件上的tap和click都处理成tap.native click.native;这是为了和小程序端保持一致;对于第三方组件或者标签上的click或者tap则不再处理; originEvents.includes(node.name.name) && (node.name.name = `${node.name.name}__CML_NATIVE_EVENTS__`); } // if (isNotNativeComp || tagName === 'component') { // cml组件上的tap和click都处理成tap.native click.native;这是为了和小程序端保持一致; // // node.name.name === 'tap' && (node.name.name = 'click'); // originEvents.includes(node.name.name) && (node.name.name = `${node.name.name}__CML_NATIVE_EVENTS__`); // } else if (isNativeComp) { // 对于引用的第三方组件则不处理 // // native组件不处理名字 // } else { // 普通标签都处理成tap // let isOriginTag = tagName.indexOf('origin-') === 0; // if (!isOriginTag) { // 如果是原生 origin- 开头的标签,那么click不要处理成tap // // node.name.name === 'click' && (node.name.name = 'tap'); // } // } } if (type === 'weex') { // weex端 还是原来的逻辑 node.name.name === 'tap' && (node.name.name = 'click'); if (isNotNativeComp || tagName === 'component') { originEvents.includes(node.name.name) && (node.name.name = `${node.name.name}__CML_NATIVE_EVENTS__`); } } if (isOriginOrNative) { return // 原生标签和原生组件直接不解析 } // ====这里作用是阻止对 origin-tag标签的事件进行代理 // 这里代理事件的时候,需要区分是内联事件还是单纯的事件名,c-bind:click="makeCountUp()" c-bind:click="handleClick(item,1,'1', 'index')";和单纯的事件名 c-bind:click="makeCountUp" let handler = value.value && utils.trim(value.value); let match = utils.isInlineStatementFn(handler); if (!match) { // 不是内联函数执行语句 handler => "handleClick" value.value = `${eventProxy.eventProxyName}($event,'${handler}',${isStopBubble})`; } else { // handler ==> handleClick() handleClick(item,1,2) .. // 如果是 handleClick() handleClick(item,name,1,2,"item","1",'2',true,"true")两种情况 let index = handler.indexOf('('); index > 0 && (handler = utils.trim(handler.slice(0, index))); if (!utils.trim(match[1])) { // 对应handleClick( ) 中的括号中的值; value.value = `${eventProxy.inlineStatementEventProxy}('${handler}',${isStopBubble})` } else { // handleClick(item,name,1,2,"item","1",'2',true,"true") let args = match && utils.doublequot2singlequot(match[1]); value.value = `${eventProxy.inlineStatementEventProxy}('${handler}',${isStopBubble},${args})` } } } }) parseEvent.tap('wx-baidu-qq-tt', (args) => { let { path, node, type, options} = args; let miniAppType = ['wx', 'baidu', 'qq', 'tt']; if (miniAppType.includes(type)) { let container = path.container; let value = container.value; let parentPath = path.parentPath; let name = node.name.name === 'click' ? 'tap' : node.name.name; let eventKey = name.toLowerCase(); let wxName = node.name.name === 'click' ? 'tap' : node.name.name; let handler = value.value && utils.trim(value.value); let match = utils.isInlineStatementFn(handler); if (node.namespace.name === 'c-bind') { wxName = `bind${wxName}` } else if (node.namespace.name === 'c-catch') { wxName = `catch${wxName}` } path.replaceWith(t.jsxIdentifier(wxName)); // ====这里作用是阻止对 origin-tag标签的事件进行代理 let jsxElementNodePath = path.findParent((path) => t.isJSXElement(path.node)); let jsxElementNode = jsxElementNodePath.node; let tagName = jsxElementNode.openingElement.name.name if (utils.isOriginTagOrNativeComp(tagName, options)) { return // 原生标签和原生组件直接不解析 } // ====这里作用是阻止对 origin-tag标签的事件进行代理 if (!match) { parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}']}}`))) value.value = `${wxEventProxy.eventProxyName}`; } else { let index = handler.indexOf('('); index > 0 && (handler = utils.trim(handler.slice(0, index))); value.value = `${eventProxy.inlineStatementEventProxy}`; let args = match && utils.doublequot2singlequot(match[1]).trim(); if (args) { // 内联函数传参 let inlineArgs = utils.getInlineStatementArgs(args); parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}',${inlineArgs}]}}`))) } else { // 内联函数不传参 parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}']}}`))) } } } }) parseEvent.tap('alipay', (args) => { // 这里注意和wx的不同,wx bindtap="tapName" alipay onTap="tapName" on后面必须是大写字母; let { path, node, type, options} = args; if (type === 'alipay') { let container = path.container; let value = container.value; let parentPath = path.parentPath; let name = node.name && (node.name.name === 'click' ? 'tap' : node.name.name); let eventKey = name.toLowerCase(); // alipay需要将事件名称转化成大写; let aliName = utils.titleLize(eventMap[name] || name); let handler = value.value && utils.trim(value.value); let match = utils.isInlineStatementFn(handler); if (node.namespace.name === 'c-bind') { aliName = `on${aliName}` } else if (node.namespace.name === 'c-catch') { aliName = `catch${aliName}` } path.replaceWith(t.jsxIdentifier(aliName)); // ====这里作用是阻止对 origin-tag标签的事件进行代理 let jsxElementNodePath = path.findParent((path) => t.isJSXElement(path.node)); let jsxElementNode = jsxElementNodePath.node; let tagName = jsxElementNode.openingElement.name.name if (utils.isOriginTagOrNativeComp(tagName, options)) { return // 原生标签和原生组件直接不解析 } // ====这里作用是阻止对 origin-tag标签的事件进行代理 if (!match) { parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}']}}`))) value.value = `${wxEventProxy.eventProxyName}`; } else { let index = handler.indexOf('('); index > 0 && (handler = utils.trim(handler.slice(0, index))); value.value = `${eventProxy.inlineStatementEventProxy}`; let args = match && utils.doublequot2singlequot(match[1]).trim(); if (args) { // 内联函数传参 let inlineArgs = utils.getInlineStatementArgs(args); parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}',${inlineArgs}]}}`))) } else { // 内联函数不传参 parentPath.insertAfter(t.jsxAttribute(t.jsxIdentifier(`data-event${eventKey}`), t.stringLiteral(`{{['${handler}']}}`))) } } } }); module.exports.parseEvent = parseEvent; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-interation.js ================================================ const t = require('@babel/types') const { SyncHook } = require('tapable'); const { interationMap } = require('../common/cml-map.js'); const utils = require('../common/utils'); let parseInteration = new SyncHook(['args']) parseInteration.tap('web-weex', (args) => { let { path, node, type, options: {lang} } = args; if (lang === 'cml' && (type === 'web' || type === 'weex')) { let container = path.container; let forBody = utils.trimCurly(node.value.value); let forIdx = 'index'; let forItem = 'item'; let forKey = ''; let siblingPaths = utils.getSiblingPaths(path); for (let item of container) { if (item.name.name === 'c-for-index') { forIdx = (item.value && item.value.value) || 'index'; } if (item.name.name === 'c-for-item') { forItem = (item.value && item.value.value) || 'item'; } if (item.name.name === 'c-key') { // 这个 for-key的限制是来自于微信端; forKey = item.value && item.value.value; if (forKey && forKey === '*this') { forKey = forIdx; } else { forKey = forItem + '.' + forKey; } } } node.name.name = 'v-for'; node.value.value = `(${forItem}, ${forIdx}) in ${forBody}`; // 移除 c-for-index c-for-item c-key这些jsxAttribute siblingPaths.forEach((siblingPath) => { if (siblingPath.node.name.name === 'c-for-index' || siblingPath.node.name.name === 'c-for-item' || siblingPath.node.name.name === 'c-key' ) { // 如果相邻元素有c-key才会插入一个 :key的JSXAttributer; if (siblingPath.node.name.name === 'c-key') { path.insertAfter(t.jsxAttribute(t.jsxIdentifier(':key'), t.stringLiteral(forKey))); } siblingPath.remove(); } }) } }) parseInteration.tap('wx-alipay-qq-tt', (args) => { let { path, node, type, options: {lang} } = args; let miniAppType = ['wx', 'alipay', 'qq', 'tt']; if (lang === 'cml' && miniAppType.includes(type)) { let name = node.name.name; node.name.name = interationMap[name][type]; let siblingPaths = utils.getSiblingPaths(path); siblingPaths.forEach((siblingPath) => { let siblingPathName = siblingPath.node.name.name; if (siblingPathName === 'c-for-index') { siblingPath.node.name.name = interationMap[siblingPathName][type]; } if (siblingPathName === 'c-for-item') { siblingPath.node.name.name = interationMap[siblingPathName][type]; } if (siblingPathName === 'c-key') { siblingPath.node.name.name = interationMap[siblingPathName][type]; } }) } }); parseInteration.tap('baidu', (args) => { let { path, node, type, options: {lang} } = args; if (lang === 'cml' && type === 'baidu') { let name = node.name.name; node.name.name = interationMap[name][type]; node.value.value = utils.trimCurly(node.value.value); let siblingPaths = utils.getSiblingPaths(path); siblingPaths.forEach((siblingPath) => { let siblingPathName = siblingPath.node.name.name; if (siblingPathName === 'c-for-index') { siblingPath.node.name.name = interationMap[siblingPathName][type]; siblingPath.node.value.value = utils.trimCurly(siblingPath.node.value.value) } if (siblingPathName === 'c-for-item') { siblingPath.node.name.name = interationMap[siblingPathName][type]; siblingPath.node.value.value = utils.trimCurly(siblingPath.node.value.value) } if (siblingPathName === 'c-key') { siblingPath.node.name.name = interationMap[siblingPathName][type]; siblingPath.node.value.value = utils.trimCurly(siblingPath.node.value.value) } }) } }) module.exports.parseInteration = parseInteration; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-ref.js ================================================ // import * as t from "@babel/types"; const t = require('@babel/types'); const { SyncHook } = require('tapable'); let parseRef = new SyncHook(['args']) parseRef.tap('wx-cml', (args) => { let { path, type, options: { lang } } = args; if (lang === 'cml' && (['wx', 'qq', 'baidu', 'alipay', 'tt'].includes(type))) { let parentPath = path.parentPath; let attributes = parentPath.node.attributes; let idNode = attributes.find((attr) => attr.name.name === 'id'); let refNode = attributes.find((attr) => attr.name.name === 'ref'); let classNode = attributes.find((attr) => attr.name.name === 'class'); if (idNode) { idNode.value.value = refNode.value.value; } else { attributes.push(t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(refNode.value.value))) } if (!classNode) { // 不存在class节点 attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral('_cml_ref_lmc_'))) } else { classNode.value.value = `${classNode.value.value} _cml_ref_lmc_` } path.remove(); } }); parseRef.tap('wx-vue', (args) => { let { path, type, options: { lang } } = args; if (lang === 'vue' && (['wx', 'qq', 'baidu', 'alipay', 'tt'].includes(type))) { let parentPath = path.parentPath; let attributes = parentPath.node.attributes; let idNode = attributes.find((attr) => attr.name.name === 'id'); let refNode = attributes.find((attr) => (attr.name.name === 'ref' || attr.name.name.name === 'ref')); let isDynamicRef = t.isJSXNamespacedName(refNode.name); let refNodeValue = isDynamicRef ? `{{${refNode.value.value}}}` : refNode.value.value let classNode = attributes.find((attr) => attr.name.name === 'class'); if (idNode) { idNode.value.value = refNodeValue; } else { attributes.push(t.jsxAttribute(t.jsxIdentifier('id'), t.stringLiteral(refNodeValue))) } if (!classNode) { // 不存在class节点 attributes.push(t.jsxAttribute(t.jsxIdentifier('class'), t.stringLiteral('_cml_ref_lmc_'))) } else { classNode.value.value = `${classNode.value.value} _cml_ref_lmc_` } path.remove(); } }) module.exports.parseRef = parseRef; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-style.js ================================================ // --inspect-brk const t = require('@babel/types') const { SyncHook } = require('tapable'); const utils = require('../common/utils'); const weexMixins = require('chameleon-mixins/weex-mixins.js') const webMixins = require('chameleon-mixins/web-mixins.js') const webStaticStyleHandle = require('chameleon-css-loader/proxy/proxyWeb.js'); const wxStaticStyleHandle = require('chameleon-css-loader/proxy/proxyMiniapp.js'); const weexStaticStyleHandle = require('chameleon-css-loader/transform/weex.js') let parseStyle = new SyncHook(['args']) // cml语法下只能一个style属性 parseStyle.tap('web-cml', (args) => { let { node, type, options: { lang, cmss } } = args; if (!cmss) { throw new Error('please ensure that if you configed the cmss in chameleon.config.js'); } if (lang === 'cml' && type === 'web') { let styleNode = node; let cmssString = utils.doublequot2singlequot(JSON.stringify(cmss)); // 动态的style,包装成代理函数; if (styleNode && styleNode.value && utils.isMustacheReactive(styleNode.value.value)) { styleNode.value.value = utils.getReactiveValue(styleNode.value.value); styleNode.value.value = `${webMixins.styleProxyName}((${styleNode.value.value}),${cmssString})` styleNode.name.name = `:${styleNode.name.name}`; } else { // 静态的 styleNode.value.value = webStaticStyleHandle(styleNode.value.value, cmssString); } } }); parseStyle.tap('weex-cml', (args) => { let { node, type, options: { lang } } = args; if (lang === 'cml' && type === 'weex') { let styleNode = node if (styleNode && styleNode.value && utils.isMustacheReactive(styleNode.value.value)) { // weex动态style styleNode.value.value = utils.getReactiveValue(styleNode.value.value); styleNode.value.value = `${weexMixins.styleProxyName}((${styleNode.value.value}))` styleNode.name.name = `:${styleNode.name.name}`; } else { // weex静态style styleNode.value && (styleNode.value.value = weexStaticStyleHandle.parse(styleNode.value.value)); } } }); parseStyle.tap('wx-alipay-baidu-cml', (args) => { let { node, type, options: { lang } } = args; if (lang === 'cml' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { let styleNode = node if (styleNode && styleNode.value && utils.isMustacheReactive(styleNode.value.value)) {// 动态的style cpx转化成rpx styleNode.value && (styleNode.value.value = utils.transformWxDynamicStyleCpxToRpx(styleNode.value.value)); } else { // 静态的style styleNode.value && (styleNode.value.value = wxStaticStyleHandle(styleNode.value.value)); } } }) parseStyle.tap('web-vue', (args) => { let { node, type, options: { lang, cmss } } = args; if (!cmss) { throw new Error('please ensure that if you configed the cmss in chameleon.config.js'); } if (lang === 'vue' && type === 'web') { // web端处理动态style; let styleNode = node; let cmssString = utils.doublequot2singlequot(JSON.stringify(cmss)); if (t.isJSXNamespacedName(node.name)) { styleNode.value.value = `${webMixins.styleProxyName}((${styleNode.value.value}),${cmssString})` } else { // 静态 styleNode.value.value = webStaticStyleHandle(styleNode.value.value, cmssString); } } }); parseStyle.tap('weex-vue', (args) => { let { node, type, options: { lang } } = args; if (lang === 'vue' && type === 'weex') { let styleNode = node; if (styleNode) { if (t.isJSXNamespacedName(styleNode.name)) { styleNode.value.value = `${weexMixins.styleProxyName}((${styleNode.value.value}))` } else { // 静态的 // styleNode.value && (styleNode.value.value = styleHandle(styleNode.value.value)); styleNode.value && (styleNode.value.value = weexStaticStyleHandle.parse(styleNode.value.value)); } } } }); parseStyle.tap('miniapp-vue', (args) => { let { path, node, type, options: { lang } } = args; if (lang === 'vue' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { let styleNode = node; if (styleNode) { let newStyleNodeValue; if (t.isJSXNamespacedName(styleNode.name)) { try { newStyleNodeValue = utils.transformWxDynamicStyleCpxToRpx(`{{${styleNode.value.value}}}`); path.replaceWith(t.jsxAttribute(t.jsxIdentifier('style'), t.stringLiteral(newStyleNodeValue))); } catch (err) { throw new Error(`${type} platform with ${lang} syntax,the attributes style is not correct`); } } else if (styleNode.value && !utils.isMustacheReactive(styleNode.value.value)) { // 静态的 styleNode.value && (styleNode.value.value = wxStaticStyleHandle(styleNode.value.value)); } } } }) module.exports.parseStyle = parseStyle; ================================================ FILE: packages/chameleon-template-parse/src/parser/parse-vue2wx.js ================================================ const t = require('@babel/types') const { SyncHook } = require('tapable'); const { interationMap, conditionMapVue2Wx } = require('../common/cml-map.js'); const utils = require('../common/utils'); let parseVue2Wx = new SyncHook(['args']) // 注意点,如果 parseVue2Wx 前面处理某个path将其path.remove()删除了,那么这个path走到了这里就会报错 parseVue2Wx.tap('vue2wx-condition,vue2alipay-condition', (args) => { let { node, type, options: {lang}} = args; if (lang === 'vue' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { if (t.isJSXAttribute(node) && (node.name.name === 'v-if' || node.name.name === 'v-else-if' || node.name.name === 'v-else' )) { let currentCondition = node.name.name; let targetCondition = conditionMapVue2Wx[currentCondition][type]; if (targetCondition && currentCondition !== targetCondition) { node.name.name = targetCondition; if (type === 'baidu') { // 不做处理 } else { node.value && t.isStringLiteral(node.value) && (node.value.value = `{{${node.value.value}}}`); } } } } }) parseVue2Wx.tap('vue2wx-v-bind,vue2alipay-v-bind', (args) => { let { node, type, options: {lang}} = args; if (lang === 'vue' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { // 注意这个node节点仍然是 JSXAttribute节点; let bindAttrName = node.name; if (t.isJSXNamespacedName(bindAttrName) && bindAttrName.namespace.name === 'v-bind' && bindAttrName.name.name !== 'key' && bindAttrName.name.name !== 'class') { // key属性不要处理;class属性不要处理; let finalBindAttrName = bindAttrName.name && bindAttrName.name.name; if (finalBindAttrName) { node.name = t.jsxIdentifier(finalBindAttrName); node.value.value = `{{${node.value.value}}}` } } } }); parseVue2Wx.tap('vue2wx-v-for', (args) => { let { path, node, type, options: {lang}} = args; if (lang === 'vue' && (['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type))) { if (t.isJSXAttribute(node) && node.name.name === 'v-for') { let siblingPaths = utils.getSiblingPaths(path); let value = node.value && node.value.value; let {item, list, index} = utils.analysisFor(value); siblingPaths.forEach((siblingPath) => { let siblingPathNode = siblingPath.node; let keyAttrName = siblingPathNode.name; let keyValue = siblingPathNode.value && siblingPathNode.value.value; if (t.isJSXNamespacedName(keyAttrName) && keyAttrName.name.name === 'key') { if (keyValue === item) { keyValue = '*this' } else { let reg = new RegExp(`${item}\\.`, 'g'); keyValue = keyValue.replace(reg, ''); } if (keyValue) { // siblingPathNode.name = t.jsxIdentifier('wx:key'); siblingPathNode.name = t.jsxIdentifier(interationMap['c-key'][type]); siblingPathNode.value.value = `${keyValue}` } } }) if (type === 'baidu') { path.insertAfter(t.jsxAttribute(t.jsxIdentifier(interationMap['c-for'][type]), t.stringLiteral(`${list}`))); } else { path.insertAfter(t.jsxAttribute(t.jsxIdentifier(interationMap['c-for'][type]), t.stringLiteral(`{{${list}}}`))); } path.insertAfter(t.jsxAttribute(t.jsxIdentifier(interationMap['c-for-index'][type]), t.stringLiteral(`${index}`))); path.insertAfter(t.jsxAttribute(t.jsxIdentifier(interationMap['c-for-item'][type]), t.stringLiteral(`${item}`))); path.remove(); } } }); parseVue2Wx.tap('component-is', (args) => { let {path, node, type, options} = args; let lang = options.lang; let conditionMap = { wx: 'wx:if', alipay: 'a:if', baidu: 's-if', qq: 'qq:if', tt: 'tt:if' } let usingComponents = (options.usingComponents || []).map(item => item.tagName) if ((['wx', 'baidu', 'alipay', 'qq', 'tt'].includes(type)) && t.isJSXElement(node)) { let currentTag = node.openingElement.name.name; let jsxElementChildren = node.children || []; if (currentTag === 'component') { let attributes = utils.getJSXElementAttrKeyValue(path); let shrinkcomponents = attributes.shrinkcomponents; if (shrinkcomponents) { usingComponents = shrinkcomponents.split(',').reduce((result, comp) => { comp = utils.trim(comp); if (comp) { result.push(comp); } return result; }, []) } let currentComp; (path.node.openingElement.attributes || []).forEach((attr) => { let attrName = attr.name if (lang === 'vue' && t.isJSXNamespacedName(attrName) && attrName.name.name === 'is') { currentComp = attr.value.value; } if (lang === 'cml' && t.isJSXIdentifier(attrName) && attrName.name === 'is') { currentComp = utils.trimCurly(attr.value.value); } }) if (currentComp && usingComponents) { let elementAttributes = path.node.openingElement.attributes || []; usingComponents.forEach((comp) => { elementAttributes = JSON.parse(JSON.stringify(elementAttributes)); // eslint-disable-next-line let openTag = t.jsxOpeningElement(t.jsxIdentifier(comp), [t.jsxAttribute(t.jsxIdentifier(`${conditionMap[type]}`), t.stringLiteral(`{{${currentComp} === '${comp}'}}`))].concat(elementAttributes)); let closeTag = t.jsxClosingElement(t.jsxIdentifier(comp)) let insertNode = t.jsxElement(openTag, closeTag, jsxElementChildren, false); path.insertAfter(insertNode); }) } path.remove();// 无论如何都要移除 component这个元素; } } }) module.exports.parseVue2Wx = parseVue2Wx; ================================================ FILE: packages/chameleon-template-parse/test/common/process-template.test.js ================================================ const processTemplate = require('../../src/common/process-template'); const expect = require('chai').expect; let options = {lang: 'cml', buildInComponents: {button: "cml-buildin-button", 'c-tab-item': 'cml-buildin-tab'}, filePath: '/User/Jim-W/didi/component/button.cml', cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', 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('preParseAliComponent', function() { // 组件需要进行一层包裹, 有些特定的组件不要被包裹 c-tab-item it('test alipay component wraped', function() { let source = `` expect(processTemplate.preParseAliComponent(source, 'alipay', options)).to.equal(``) }) // 组件上的 c-if c-else v-if class style需要外移到包裹层 it('test alipay component wraped', function() { let source = `` expect(processTemplate.preParseAliComponent(source, 'alipay', options)).to.equal(``) }); // 一元标签的处理,组件一元标签也是需要被包裹,非组件一元标签则不会,组件在options中有声明 it('test alipay component wraped', function() { let source = ``, options)).to.include.keys('usedBuildInTagMap'); expect(processTemplate.analyzeTemplate(``, options)).to.include.keys('buildInComponents') }) }); describe('analyzeTemplate', function() { it('collect which build-in-tag is used in template', function() { let options = {buildInComponents: {button: "cml-buildin-button"}}; expect(processTemplate.analyzeTemplate(``, options)).to.include.keys('usedBuildInTagMap'); expect(processTemplate.analyzeTemplate(``, options)).to.include.keys('buildInComponents') }) }); describe('analyzeTemplate', function() { it('collect which build-in-tag is used in template', function() { let options = {buildInComponents: {button: "cml-buildin-button"}}; expect(processTemplate.analyzeTemplate(``, options)).to.include.keys('buildInComponents'); }) }); describe('_operationGtLt', function() { it('transform _operationGtLt', function() { expect(processTemplate._operationGtLt(`{{value}}`)).to.equal(`{{value}}`) }) }); describe('_deOperationGtLt', function() { it('transform _deOperationGtLt', function() { expect(processTemplate._operationGtLt(`{{value}}`)).to.equal(`{{value}}`) }) }); describe('transformNativeEvent', function() { it('transform transformNativeEvent', function() { expect(processTemplate.transformNativeEvent(``)).to.equal(``) }) }); }) ================================================ FILE: packages/chameleon-template-parse/test/common/utils.test.js ================================================ const utils = require('../../src/common/utils'); var expect = require('chai').expect; let options = {lang: 'cml', buildInComponents: {button: "cml-buildin-button", 'c-tab-item': 'cml-buildin-tab'}, filePath: '/User/Jim-W/didi/component/button.cml', cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; describe('utils', function() { describe('trimCurly', function() { it('trim {{variable}} to variable', function() { expect(utils.trimCurly(`{{name}}`)).to.equal(`name`) }) }); // 驼峰化 describe('camelize', function() { it('transform ab-c to abC', function() { expect(utils.camelize(`abc-de-f`)).to.equal(`abcDeF`) }) }); // 中划线化 describe('dasherise', function() { it('transform abcDeF to abc-de-f', function() { expect(utils.dasherise(`abcDeF`)).to.equal(`abc-de-f`) }) }); describe('analysisFor', function() { it('transform analysisFor ', function() { expect(utils.analysisFor(`(item,index) in items`)).to.includes.keys(`item`) expect(utils.analysisFor(`(item,index) in items`)).to.includes.keys(`index`) expect(utils.analysisFor(`(item,index) in items`)).to.includes.keys(`list`) }) }); describe('analysisFor', function() { it('transform analysisFor ', function() { expect(utils.analysisFor(`item in items`)).to.includes.keys(`item`) expect(utils.analysisFor(`item in items`)).to.includes.keys(`index`) expect(utils.analysisFor(`item in items`)).to.includes.keys(`list`) }) }); describe('getModelKey', function() { it('trim {{ variable }} to variable', function() { expect(utils.getModelKey(`{{ name }}`)).to.equal(`name`) }) }); describe('titleLize', function() { it(`titleLize a word asked for that the word cant't start with space character`, function() { expect(utils.titleLize('name')).to.equal(`Name`) }) }); describe('trim', function() { it(`trim the word`, function() { expect(utils.trim(' beTrimed ')).to.equal('beTrimed'); }) }); describe('isInlineStatementFn', function() { it(`judge if the expression is inlineStatement`, function() { expect(utils.isInlineStatementFn(`c-bind:click="handleClick(item,1,'1', 'index')"`)).to.be.an('array'); }) }); describe('isInlineStatementFn', function() { it(`judge if the expression is inlineStatement`, function() { expect(utils.isInlineStatementFn(`c-bind:click="handleClick"`)).to.not.be.ok; }) }) describe('isReactive', function() { it('judge if the arguments is reactive', function() { expect(utils.isReactive(`'index'`)).to.be.an('array') }) }); describe('isReactive', function() { it('judge if the arguments is not reactive', function() { expect(utils.isReactive(`'index'+1`)).to.not.be.ok; }) }); describe('doublequot2singlequot', function() { it('transform doublequot to singlequot', function() { expect(utils.doublequot2singlequot(`"name"`)).to.equal(`'name'`) }) }); describe('isMustacheReactive', function() { it('judge if the value is Reactive', function() { expect(utils.isMustacheReactive(`{{value}}`)).to.equal(true) }) }); describe('isOnlySpaceContent', function() { it('judge if the value is only space key', function() { expect(utils.isOnlySpaceContent(' ')).to.be.ok; }) }); describe('getReactiveValue', function() { it('getReactiveValue for vue:such as {{value}} to (value)', function() { expect(utils.getReactiveValue(`{{value1+value2}}`)).to.equal(`(value1+value2)`); }) }); describe('getReactiveValue', function() { it(`getReactiveValue for vue:such as main-{{index}} to 'main-'+(value)`, function() { expect(utils.getReactiveValue(`main-{{value1+value2}}`)).to.equal(`'main-'+(value1+value2)`); }) }); describe('getStaticValueFromMixinValue', function() { it('getStaticValueFromMixinValue for cml', function() { let result = utils.getStaticValueFromMixinValue(`a b{{true? 'cls1':'cls2'}} {{variable}}b c `); expect(result).to.equal(`a b b c `); }) }); describe('transformWxDynamicStyleCpxToRpx', function() { it('cml-syanx:transformWxDynamicStyleCpxToRpx for wx dynamic style', function() { let result = utils.transformWxDynamicStyleCpxToRpx(`height:100cpx;{{'width:'+cpx+'cpx;'+'height:'+cpx2+'cpx;background-color:red;'}}height:200cpx;width:100cpx;`); expect(result).to.equal(`height:100rpx;{{'width:' + cpx + 'rpx;' + 'height:' + cpx2 + 'rpx;background-color:red;'}}height:200rpx;width:100rpx;`); }) }); describe('transformWxDynamicStyleCpxToRpx', function() { it('vue-syanx:ransformWxDynamicStyleCpxToRpx for wx dynamic style', function() { let result = utils.transformWxDynamicStyleCpxToRpx(`{{'width:'+cpx+'cpx;'+'height:'+cpx2+'cpx;background-color:red'}}`); expect(result).to.equal(`{{'width:' + cpx + 'rpx;' + 'height:' + cpx2 + 'rpx;background-color:red'}}`); }) }); // getInlineStatementArgs describe('getInlineStatementArgs', function() { it('getInlineStatementArgs', function() { let result = utils.getInlineStatementArgs("1,'index'+1,$event,'item',index+1,item"); expect(result).to.equal(`1,'index'+1,'$event','item',index+1,item`); }) }); describe('isOriginTagOrNativeComp', function() { it('isOriginTagOrNativeComp-nativecomp', function() { let result = utils.isOriginTagOrNativeComp('thirdComp1', options); expect(result).to.be.ok; }); it('isOriginTagOrNativeComp-origin-tag', function() { let result = utils.isOriginTagOrNativeComp('origin-tag', options); expect(result).to.be.ok; }); it('isOriginTagOrNativeComp-not-nativecomp', function() { let result = utils.isOriginTagOrNativeComp('thirdComp2', options); expect(result).to.be.not.ok; }); it('isusualComp', function() { let result = utils.isOriginTagOrNativeComp('view', options); expect(result).to.be.not.ok; }); }); describe('isNativeComp', function() { it('isOriginTagOrNativeComp-nativecomp', function() { let result = utils.isOriginTagOrNativeComp('thirdComp1', options); expect(result).to.be.ok; }); it('isOriginTagOrNativeComp-origin-tag', function() { let result = utils.isNativeComp('origin-tag', options); expect(result).to.be.not.ok; }); it('isOriginTagOrNativeComp-not-nativecomp', function() { let result = utils.isNativeComp('thirdComp2', options); expect(result).to.be.not.not.ok; }); it('isusualComp', function() { let result = utils.isNativeComp('view', options); expect(result).to.be.not.ok; }); }); // 不是对应端的原生组件 describe('isNotNativeComponent', function() { it('isNotNativeComponent-nativecomp', function() { let result = utils.isNotNativeComponent('cml-buildin-button', options); expect(result).to.be.ok; }); it('isNotNativeComponent-not-nativecomp', function() { let result = utils.isNotNativeComponent('thirdComp1', options); expect(result).to.be.not.ok; }); it('isNotNativeComponent', function() { let result = utils.isNotNativeComponent('thirdComp2', options); expect(result).to.be.ok; }); it('isNotNativeComponent', function() { let result = utils.isNotNativeComponent('view', options); console.log('result-isNotNativeComponent', result) expect(result).to.be.not.ok; }); }); }) ================================================ FILE: packages/chameleon-template-parse/test/index.js ================================================ const compileTemplate = require('../src/index.js'); const source = `` // fafafa // fafafa // // let result = compileTemplate(source,'web'); let options = {lang: 'cml', filePath: '/Users/didi/components.cml', buildInComponents: {button: "cml-buildin-button"}, isInjectBaseStyle: false, cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'cube-button', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; let analyzeOptions = { buildInComponents: { audio: 'cml-buildin-audio', button: 'cml-buildin-button', 'carousel-item': 'cml-buildin-carousel-item', carousel: 'cml-buildin-carousel', checkbox: 'cml-buildin-checkbox', input: 'cml-buildin-input', aside: 'cml-buildin-aside', col: 'cml-buildin-col', container: 'cml-buildin-container', foot: 'cml-buildin-foot', head: 'cml-buildin-head', main: 'cml-buildin-main', row: 'cml-buildin-row', list: 'cml-buildin-list', 'native-scroller': 'cml-buildin-native-scroller', page: 'cml-buildin-page', radio: 'cml-buildin-radio', 'refresh-loadding': 'cml-buildin-refresh-loadding', 'refresh-view': 'cml-buildin-refresh-view', richtext: 'cml-buildin-richtext', scroller: 'cml-buildin-scroller', switch: 'cml-buildin-switch', textarea: 'cml-buildin-textarea', video: 'cml-buildin-video' }, usedBuildInTagMap: {}, cmlType: 'wx' } // console.log('before-compile', source); // let result_web = compileTemplate(source, 'web', options); // let result_weex = compileTemplate(source, 'weex', options); // let result_wx = compileTemplate(source, 'wx', options); // let result_baidu = compileTemplate(source, 'baidu', options); // // let result_alipay = compileTemplate(source, 'alipay', options); // console.log('result_web', result_web) // console.log('result_weex', result_weex) // console.log('result_wx', result_wx) // console.log('result_baidu', result_baidu) // console.log('result_alipay', result_alipay) const {preParseMultiTemplate ,analyzeTemplate}= compileTemplate; // const result = preParseMultiTemplate(source,'web',{needTranJSX:true,needDelTemplate:true}) // console.log('result',result) const result2 = analyzeTemplate(source,analyzeOptions) console.log('result',result2) ================================================ FILE: packages/chameleon-template-parse/test/parser/cml.test.js ================================================ // cml 或者vue语法整体单元测试 const compileTemplate = require('../../src/index.js'); const expect = require('chai').expect; let options = {lang: 'cml', buildInComponents: {button: "cml-buildin-button"}, filePath: '/User/Jim-W/didi/component/button.cml', isInjectBaseStyle: true, cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; describe('parse-template-cml-all', function() { // parse-no-source describe('parse-no-source', function() { let source = ``; it('parse-no-source', function() { expect(compileTemplate(source, 'web', options).source).to.equal(source); }); }); // parseTag describe('parse-tag-transform', function() { let source = ``; it('test-tag-transform', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); }); // directive c-model describe('parse-directive-transform', function() { let source = ``; it('test-directive-comodel-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-directive-comodel-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`) }); it('test-directive-comodel-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); it('test-directive-comodel-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``) }); it('test-directive-comodel-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``) }); it('test-directive-comodel-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``) }); }); // directive c-show describe('parse-directive-transform', function() { let source = ``; it('test-directive-c-show-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-directive-c-show-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`) }); it('test-directive-c-show-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); it('test-directive-c-show-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``) }); it('test-directive-c-show-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``) }); it('test-directive-c-show-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``) }); }); // directive c-text describe('parse-directive-transform', function() { let source = ``; it('test-directive-c-text-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{value1}}
`); }); it('test-directive-c-text-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{value1}}
`) }); it('test-directive-c-text-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{value1}}`) }); it('test-directive-c-text-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{value1}}`) }); it('test-directive-c-text-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{value1}}`) }); it('test-directive-c-text-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{value1}}`) }); }); // directive c-if c-else-if c-else describe('parse-directive-transform', function() { let source = ` `; it('test-directive-c-condition-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
\n
\n
`); }); it('test-directive-c-condition-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
\n
\n
`) }); it('test-directive-c-condition-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`\n \n `) }); it('test-directive-c-condition-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`\n \n `) }); it('test-directive-c-condition-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`\n \n `) }); it('test-directive-c-condition-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`\n \n `) }); }); // directive c-for c-for-index c-for-item c-key describe('parse-c-interation-transform', function() { let source = `{{item.id}} `; it('test-directive-c-interator-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-c-interator-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-c-interator-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // directive c-for c-for-index c-for-item c-key describe('parse-directive-transform', function() { let source = `{{item.id}} `; it('test-directive-c-interator-key-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-c-interator-key-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-c-interator-key-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-key-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-key-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-key-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // directive c-for c-for-index c-for-item c-key:*this describe('parse-directive-transform', function() { let source = `{{item.id}} `; it('test-directive-c-interator-*this-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-c-interator-*this-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-c-interator-*this-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-*this-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-*this-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-c-interator-*this-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // parseEvent describe('parse-event-transform', function() { let source = ``; it('test-event-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-event-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-event-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-event-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-event-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); it('test-event-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); }); // parseEvent-stop describe('parse-event-transform-stop', function() { let source = ``; it('test-event-transform-web-stop', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-event-transform-weex-stop', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-event-transform-wx-stop', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-event-transform-qq-stop', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-event-transform-baidu-stop', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-event-transform-alipay-stop', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // 非原生组件外面包的view要添加原生事件 describe('parse-event-transform-alicomponent', function() { let source = ``; it('test-event-transform-alipay-alicomponent', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // class describe('parse-class-transform', function() { let source = ``; it('parse-class-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('parse-class-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); // wx baidu alipay it('parse-class-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }); // style 以及 miniappp端cpx动态测试 describe('parse-style-transform', function() { let source = ``; it('parse-style-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('parse-style-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); // wx baidu alipay it('parse-style-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }); // ref 动态 describe('parse-ref-transform-dynamic', function() { let source = ``; it('test-ref-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-ref-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-ref-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-ref-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-ref-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-ref-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // ref 静态 describe('parse-ref-transform-static', function() { let source = ``; it('test-ref-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-ref-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-ref-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-ref-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-ref-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-ref-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // component is describe('parse-component-is-transform', function() { let source = ``; it('test-component-is-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(``); }); it('test-component-is-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(``); }); it('test-component-is-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`;\n`); }); it('test-component-is-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`;\n`); }); it('test-component-is-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`;\n`); }); it('test-component-is-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`;\n`); }); }); // animation describe('parse-c-animation-transform', function() { let source = ``; it('test-c-animation-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-c-animation-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-c-animation-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-c-animation-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-c-animation-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-c-animation-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // attribute describe('parse-attribute-transform', function() { let source = ``; it('test-attribute-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-attribute-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-attribute-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }) // 各种 < > 的转化 describe('parse-gtlt-transform', function() { let source = `{{ 5 > 6 ? 'this is 5':'this is 6'}}`; it('test-gtlt-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{5 > 6 ? \'this is 5\' : \'this is 6\'}}
`); }); it('test-gtlt-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{5 > 6 ? \'this is 5\' : \'this is 6\'}}
`); }); it('test-gtlt-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{5 > 6 ? \'this is 5\' : \'this is 6\'}}`); }); }); describe('test-alipaycomponent-wraped-cml', function() { let source = ``; it('test-alipaycomponent-wraped', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); describe('test-class-noinjected', function() { let source = ``; let cpOptions = JSON.parse(JSON.stringify(options)); cpOptions.isInjectBaseStyle = false; it('test-class-noinjected-web', function() { expect(compileTemplate(source, 'web', cpOptions).source).to.equal(`
`); }); it('test-class-noinjected-weex', function() { expect(compileTemplate(source, 'weex', cpOptions).source).to.equal(`
`); }); it('test-class-noinjected-alipay', function() { expect(compileTemplate(source, 'alipay', cpOptions).source).to.equal(``); }); it('test-class-noinjected-baidu', function() { expect(compileTemplate(source, 'baidu', cpOptions).source).to.equal(``); }); it('test-class-noinjected-wx', function() { expect(compileTemplate(source, 'wx', cpOptions).source).to.equal(``); }); it('test-class-noinjected-qq', function() { expect(compileTemplate(source, 'qq', cpOptions).source).to.equal(``); }); }); }) ================================================ FILE: packages/chameleon-template-parse/test/parser/index.cml.test.js ================================================ // cml单个语法的单元测试 const babylon = require('babylon'); const traverse = require('@babel/traverse')["default"]; const generate = require('@babel/generator')["default"]; const parseTemplate = require('../../src/parser/index.js'); const expect = require('chai').expect; let options = {lang: 'cml', buildInComponents: {button: "cml-buildin-button"}, filePath: '/User/Jim-W/didi/component/button.cml', isInjectBaseStyle: true, cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; function compileTemplate(source, type, options, callback) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { callback(path, type, options); } }); let result = generate(ast).code; if (/;$/.test(result)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的; result = result.slice(0, -1); } return result; } // cml语法的单元测试 describe('parse-template-cml', function() { // parseTag describe('parseTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.parseTag; let result = compileTemplate(source, 'web', options, callback); it('test-tag-transform', function() { expect(result).to.equal(`
`) }); }); describe('afterParseTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.afterParseTag; it('test-after-tag-transform-web-weex', function() { expect(compileTemplate(source, 'web', options, callback)).to.equal(``) }); it('test-after-tag-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options, callback)).to.equal(``) }); }); describe('parseBuildTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.parseBuildTag; let result = compileTemplate(source, 'web', options, callback); it('test-build-tag-transform', function() { expect(result).to.equal(``) }); }); // wx baidu alipay一样 describe('parseTagForSlider', function() { let source = ``; let callback = parseTemplate.parseTagForSlider; let result = compileTemplate(source, 'wx', options, callback); it('parseTagForSlider for wx', function() { expect(result).to.equal(``) }); }); // parseRefStatement:仅在所有的小程序端进行处理 describe('parseRefStatement-wx-alipay-baidu', function() { let source = ``; let callback = parseTemplate.parseRefStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-ref-transform', function() { expect(result).to.equal(``) }); }); // parseConditionalStatement describe('parseConditionalStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseConditionalStatement; let result = compileTemplate(source, 'web', options, callback); it('test-condition-web-transform', function() { expect(result).to.equal(``) }); }); describe('parseConditionalStatement-wx', function() { let source = ``; let callback = parseTemplate.parseConditionalStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-condition-wx-transform', function() { expect(result).to.equal(``) }); }); describe('parseConditionalStatement-alipay', function() { let source = ``; let callback = parseTemplate.parseConditionalStatement; let result = compileTemplate(source, 'alipay', options, callback); it('test-condition-alipay-transform', function() { expect(result).to.equal(``) }); }); describe('parseConditionalStatement-baidu', function() { let source = ``; let callback = parseTemplate.parseConditionalStatement; let result = compileTemplate(source, 'baidu', options, callback); console.log('condition-baidu', result) it('test-condition-baidu-transform', function() { expect(result).to.equal(``) }); }); // parseEventListener describe('parseEventListener-web-weex', function() { let source = ``; let originSource = ` ` let callback = parseTemplate.parseEventListener; let result = compileTemplate(source, 'web', options, callback); it('test-event-transform', function() { expect(result).to.equal(``) }); // 原生组件事件不进行代理 it('test-origin-tag-event-transform', function() { expect(compileTemplate(originSource, 'web', options, callback)).to.equal(``) }); }); describe('parseEventListener-wx-baidu', function() { let source = ``; let originSource = ` ` let callback = parseTemplate.parseEventListener; let result = compileTemplate(source, 'wx', options, callback); it('test-event-transform', function() { expect(result).to.equal(``) }); it('test-origin-tag-event-transform', function() { expect(compileTemplate(originSource, 'wx', options, callback)).to.equal(``) }); }); describe('parseEventListener-alipay', function() { let source = ``; let originSource = ` ` let callback = parseTemplate.parseEventListener; let result = compileTemplate(source, 'alipay', options, callback); it('test-event-transform', function() { expect(result).to.equal(``) }); it('test-origin-tag-event-transform', function() { expect(compileTemplate(originSource, 'alipay', options, callback)).to.equal(``) }); }); // parseIterationStatement describe('parseIterationStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result = compileTemplate(source, 'web', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseIterationStatement-wx', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result1 = compileTemplate(source, 'wx', options, callback); it('test-Iteration-transform', function() { expect(result1).to.equal(``) }); }); describe('parseIterationStatement-alipay', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result2 = compileTemplate(source, 'alipay', options, callback); it('test-Iteration-transform', function() { expect(result2).to.equal(``) }); }); describe('parseIterationStatement-baidu', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result = compileTemplate(source, 'baidu', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); // parseIterationStatement c-for-inde c-for-item c-key的测试 describe('parseIterationStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result = compileTemplate(source, 'web', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseIterationStatement-wx', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result1 = compileTemplate(source, 'wx', options, callback); it('test-Iteration-transform', function() { expect(result1).to.equal(``) }); }); describe('parseIterationStatement-alipay', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result2 = compileTemplate(source, 'alipay', options, callback); it('test-Iteration-transform', function() { expect(result2).to.equal(``) }); }); describe('parseIterationStatement-baidu', function() { let source = ``; let callback = parseTemplate.parseIterationStatement; let result = compileTemplate(source, 'baidu', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); // parseAttributeStatement describe('parseAttributeStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseAttributeStatement; let result = compileTemplate(source, 'web', options, callback); it('test-attribute-transform', function() { expect(result).to.equal(``) }); }); describe('parseAttributeStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseAttributeStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-attribute-transform', function() { expect(result).to.equal(``) }); }); // cml语法下style只支持一个style;parseStyleStatement describe('parseStyleStatement-web', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result = compileTemplate(source, 'web', options, callback); it('test-style-transform', function() { expect(result).to.equal(``) }); }); describe('parseStyleStatement-weex', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-style-transform', function() { expect(result).to.equal(``) }); }); describe('parseStyleStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-style-transform', function() { expect(result).to.equal(``) }); }); // parseClassStatement:cml语法下只能写一个class describe('parseClassStatement-web', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result = compileTemplate(source, 'web', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseClassStatement-weex', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseClassStatement-wx-alipay-baidu', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_alipay = compileTemplate(source, 'wx', options, callback); let result_baidu = compileTemplate(source, 'wx', options, callback); it('test-class-transform', function() { expect(result_wx).to.equal(``) expect(result_alipay).to.equal(``) expect(result_baidu).to.equal(``) }); }); // parseAnimationStatement describe('parseAnimationStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseAnimationStatement; let result = compileTemplate(source, 'web', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseAnimationStatement-wx', function() { let source = ``; let callback = parseTemplate.parseAnimationStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); // parseDirectiveStatement:c-model describe('parseDirectiveStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'web', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-wx', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'wx', options, callback); it('parseDirectiveStatement-c-model-wx', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-alipay', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'alipay', options, callback); it('parseDirectiveStatement-c-model-alipay', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-baidu', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'baidu', options, callback); it('parseDirectiveStatement-c-model-baidu', function() { expect(result).to.equal(``) }); }); // c-show describe('parseDirectiveStatement-web', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'web', options, callback); it('test-c-show-transform', function() { // cml语法下线解析成style后续会通过parseStyle接着进行解析; expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-weex', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-c-show-transform', function() { // cml语法下线解析成style后续会通过parseStyle接着进行解析; expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-wx-alipay-baidu', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_baidu = compileTemplate(source, 'baidu', options, callback); let result_alipay = compileTemplate(source, 'alipay', options, callback); it('test-c-show-transform', function() { expect(result_wx).to.equal(``) expect(result_baidu).to.equal(``) expect(result_alipay).to.equal(``) }); }); // c-text describe('parseDirectiveStatement-web-miniapp', function() { let source = `everything will be replaced`; let callback = parseTemplate.parseDirectiveStatement; it('test-c-text-transform', function() { expect(compileTemplate(source, 'web', options, callback)).to.equal(`{{value1}}`) }); it('test-c-text-transform', function() { expect(compileTemplate(source, 'wx', options, callback)).to.equal(`{{value1}}`) }); it('test-c-text-transform', function() { expect(compileTemplate(source, 'weex', options, callback)).to.equal(`{{value1}}`) }); }); describe('parse-vue2wx-wx', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'wx', options, callback); it('component is', function() { // cml语法下线解析成style后续会通过parseStyle接着进行解析; expect(result).to.equal(`;\n`) }); }); describe('parse-vue2wx-baidu', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'baidu', options, callback); it('component is', function() { expect(result).to.equal(`;\n`) }); }); describe('parse-vue2wx-alipay', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'alipay', options, callback); it('component is', function() { expect(result).to.equal(`;\n`) }); }); }) ================================================ FILE: packages/chameleon-template-parse/test/parser/index.vue.test.js ================================================ // vue单个语法的单元测试 const babylon = require('babylon'); const t = require('@babel/types'); const traverse = require('@babel/traverse')["default"]; const generate = require('@babel/generator')["default"]; const parseTemplate = require('../../src/parser/index.js'); const expect = require('chai').expect; let options = {lang: 'vue', buildInComponents: {button: "cml-buildin-button"}, filePath: '/User/Jim-W/didi/component/button.cml', isInjectBaseStyle: true, cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; function compileTemplate(source, type, options, callback) { const ast = babylon.parse(source, { plugins: ['jsx'] }) traverse(ast, { enter(path) { callback(path, type, options); } }); let result = generate(ast).code; if (/;$/.test(result)) { // 这里有个坑,jsx解析语法的时候,默认解析的是js语法,所以会在最后多了一个 ; 字符串;但是在 html中 ; 是无法解析的; result = result.slice(0, -1); } return result; } // cml语法的单元测试 describe('parse-template-vue', function() { describe('parseTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.parseTag; let result = compileTemplate(source, 'web', options, callback); it('test-tag-transform', function() { expect(result).to.equal(`
`) }); }); describe('afterParseTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.afterParseTag; it('test-after-tag-transform-web-weex', function() { expect(compileTemplate(source, 'web', options, callback)).to.equal(``) }); it('test-after-tag-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options, callback)).to.equal(``) }); }); describe('parseBuildTag', function() { // 各个端的标签转化单元测试不做全覆盖,逻辑相对简单 let source = ``; let callback = parseTemplate.parseBuildTag; let result = compileTemplate(source, 'web', options, callback); it('test-build-tag-transform', function() { expect(result).to.equal(``) }); }); describe('parseTagForSlider', function() { let source = ``; let callback = parseTemplate.parseTagForSlider; let result = compileTemplate(source, 'wx', options, callback); it('parseTagForSlider for wx', function() { expect(result).to.equal(``) }); }); // parseRefStatement:仅在所有的小程序端进行处理 describe('parseRefStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseRefStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-ref-transform', function() { expect(result).to.equal(``) }); }); // parseVue2WxStatement:测试v-if语法转化为小程序 describe('parseVue2WxStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_baidu = compileTemplate(source, 'baidu', options, callback); let result_alipay = compileTemplate(source, 'alipay', options, callback); it('test-condition-web-transform', function() { expect(result_wx).to.equal(``) expect(result_alipay).to.equal(``) expect(result_baidu).to.equal(``) }); }); // parseVue2WxStatement:测试v-for语法转化为小程序 describe('parseVue2WxStatement-web', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'web', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseVue2WxStatement-weex', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseVue2WxStatement-wx', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseVue2WxStatement-alipay', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'alipay', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); describe('parseVue2WxStatement-baidu', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'baidu', options, callback); it('test-Iteration-transform', function() { expect(result).to.equal(``) }); }); // parseVue2WxStatement:测试 v-bind转化为小程序端的响应值 describe('parseVue2WxStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_alipay = compileTemplate(source, 'alipay', options, callback); let result_baidu = compileTemplate(source, 'baidu', options, callback); it('test-attribute-transform', function() { expect(result_wx).to.equal(``) expect(result_alipay).to.equal(``) expect(result_baidu).to.equal(``) }); }); describe('parseAttributeStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseAttributeStatement; let result = compileTemplate(source, 'web', options, callback); it('test-attribute-transform', function() { expect(result).to.equal(``) }); }); // vue语法下style只支持一个style;parseStyleStatement describe('parseStyleStatement-web', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result = compileTemplate(source, 'web', options, callback); it('test-style-transform', function() { expect(result).to.equal(``) }); }); describe('parseStyleStatement-weex', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-style-transform', function() { expect(result).to.equal(``) }); }); describe('parseStyleStatement-miniapp', function() { let source = ``; let callback = parseTemplate.parseStyleStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_alipay = compileTemplate(source, 'alipay', options, callback); let result_baidu = compileTemplate(source, 'baidu', options, callback); it('test-style-transform', function() { expect(result_wx).to.equal(``) expect(result_alipay).to.equal(``) expect(result_baidu).to.equal(``) }); }); // parseClassStatement describe('parseClassStatement-web', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result = compileTemplate(source, 'web', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseClassStatement-weex', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); describe('parseClassStatement-wx-alipay-baidu', function() { let source = ``; let callback = parseTemplate.parseClassStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-class-transform', function() { expect(result).to.equal(``) }); }); // parseDirectiveStatement describe('parseDirectiveStatement-web-weex', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'web', options, callback); it('parseDirectiveStatement-v-model-web', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-wx-v-model', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'wx', options, callback); it('test-v-model', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-baidu-v-mode', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'baidu', options, callback); it('test-v-model', function() { expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-alipay-v-model', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'alipay', options, callback); it('test-v-model', function() { expect(result).to.equal(``) }); }); // c-show describe('parseDirectiveStatement-web', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'web', options, callback); it('test-c-show-transform', function() { // style后续会通过parseStyle接着进行解析; expect(result).to.equal(``) }); }); // c-text describe('parseDirectiveStatement-web-miniapp', function() { let source = `everything will be replaced`; let callback = parseTemplate.parseDirectiveStatement; it('test-c-text-transform', function() { expect(compileTemplate(source, 'web', options, callback)).to.equal(`{{value1}}`) }); it('test-c-text-transform', function() { expect(compileTemplate(source, 'wx', options, callback)).to.equal(`{{value1}}`) }); it('test-c-text-transform', function() { expect(compileTemplate(source, 'weex', options, callback)).to.equal(`{{value1}}`) }); }); describe('parseDirectiveStatement-weex', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result = compileTemplate(source, 'weex', options, callback); it('test-c-show-transform', function() { // style后续会通过parseStyle接着进行解析; expect(result).to.equal(``) }); }); describe('parseDirectiveStatement-wx-alipay-baidu', function() { let source = ``; let callback = parseTemplate.parseDirectiveStatement; let result_wx = compileTemplate(source, 'wx', options, callback); let result_baidu = compileTemplate(source, 'baidu', options, callback); let result_alipay = compileTemplate(source, 'alipay', options, callback); it('test-class-transform', function() { expect(result_wx).to.equal(``) expect(result_baidu).to.equal(``) expect(result_alipay).to.equal(``) }); }); describe('parse-vue2wx-wx', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'alipay', options, callback); it('component is', function() { // cml语法下线解析成style后续会通过parseStyle接着进行解析; expect(result).to.equal(`;\n`) }); }); describe('parse-vue2wx-baidu', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'baidu', options, callback); it('component is', function() { // cml语法下线解析成style后续会通过parseStyle接着进行解析; expect(result).to.equal(`;\n`) }); }); describe('parse-vue2wx-alipay', function() { let source = ``; let callback = parseTemplate.parseVue2WxStatement; let result = compileTemplate(source, 'alipay', options, callback); it('component is', function() { expect(result).to.equal(`;\n`) }); }); }) ================================================ FILE: packages/chameleon-template-parse/test/parser/vue.test.js ================================================ // cml 或者vue语法整体单元测试 const compileTemplate = require('../../src/index.js'); const expect = require('chai').expect; let options = {lang: 'vue', buildInComponents: {button: "cml-buildin-button"}, filePath: '/User/Jim-W/didi/component/button.cml', isInjectBaseStyle: true, cmss: { rem: true, scale: 0.5, remOptions: { // base on 750px standard. rootValue: 75, // to leave 1px alone. minPixelValue: 1.01 }, autoprefixOptions: { browsers: ['> 0.1%', 'ios >= 8', 'not ie < 12'] } }, usingComponents: [{ tagName: 'thirdComp1', refUrl: '/path/to/ref1', filePath: 'path/to/real1', isNative: true }, { tagName: 'thirdComp2', refUrl: '/path/to/ref2', filePath: 'path/to/real2', isNative: false }] }; describe('parse-template-vue-all', function() { // parseTag describe('parse-tag-transform', function() { let source = ``; it('test-tag-transform', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); }); // directive v-model describe('parse-directive-transform', function() { let source = ``; it('test-directive-v-model-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-directive-v-model-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`) }); it('test-directive-v-model-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); it('test-directive-v-model-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``) }); it('test-directive-v-model-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``) }); it('test-directive-v-model-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``) }); }); // directive v-show describe('parse-directive-transform', function() { let source = ``; it('test-directive-v-show-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-directive-v-show-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`) }); it('test-directive-v-show-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``) }); it('test-directive-v-show-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``) }); it('test-directive-v-show-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``) }); it('test-directive-v-show-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``) }); }); // directive v-text describe('parse-directive-transform', function() { let source = ``; it('test-directive-v-text-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{value1}}
`); }); it('test-directive-v-text-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{value1}}
`) }); it('test-directive-v-text-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{value1}}`) }); it('test-directive-v-text-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{value1}}`) }); it('test-directive-v-text-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{value1}}`) }); it('test-directive-v-text-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{value1}}`) }); }); // directive v-if v-else-if v-else describe('parse-directive-transform', function() { let source = ` `; it('test-directive-v-condition-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
\n
\n
`); }); it('test-directive-v-condition-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
\n
\n
`) }); it('test-directive-v-condition-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`\n \n `) }); it('test-directive-v-condition-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`\n \n `) }); it('test-directive-v-condition-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`\n \n `) }); it('test-directive-v-condition-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`\n \n `) }); }); // directive v-for describe('parse-directive-transform', function() { let source = `{{item.id}} `; it('test-directive-v-interator-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-v-interator-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-v-interator-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // directive c-for c-for-index c-for-item c-key describe('parse-directive-transform', function() { let source = `{{item.id}} `; it('test-directive-v-interator-key-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-v-interator-key-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-v-interator-key-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-key-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-key-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-key-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // directive c-for c-for-index c-for-item c-key:*this describe('parse-directive-transform', function() { let source = `{{item.id}} `; it('test-directive-v-interator-*this-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{item.id}}
`); }); it('test-directive-v-interator-*this-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{item.id}}
`) }); it('test-directive-v-interator-*this-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-*this-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-*this-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`{{item.id}} `) }); it('test-directive-v-interator-*this-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`{{item.id}} `) }); }); // parseEvent describe('parse-event-transform', function() { let source = ``; it('test-event-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-event-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-event-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-event-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-event-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); it('test-event-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); }); // parseEvent-.stop web weex describe('parse-event-transform-stop', function() { let source = ``; it('test-event-transform-web-stop', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-event-transform-weex-stop', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-event-transform-wx-stop', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-event-transform-qq-stop', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-event-transform-baidu-stop', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-event-transform-alipay-stop', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // parseEvent- origin-tag 的click不处理成tap describe('parse-event-transform-origin-tag', function() { let source = ``; it('test-event-transform-web-origin-tag', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-event-transform-weex-origin-tag', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); }); // 非原生组件外面包的view要添加原生事件 describe('parse-event-transform-alicomponent', function() { let source = ``; it('test-event-transform-alipay-alicomponent', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // class describe('parse-class-transform', function() { let source = ``; it('parse-class-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('parse-class-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); // wx baidu alipay it('parse-class-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }); // style 以及 miniappp端cpx动态测试 describe('parse-style-transform', function() { let source = ``; it('parse-style-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('parse-style-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); // wx baidu alipay it('parse-style-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }); // ref 动态 describe('parse-ref-transform', function() { let source = ``; it('test-ref-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-ref-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-ref-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-ref-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-ref-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-ref-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // ref 静态 describe('parse-ref-transform', function() { let source = ``; it('test-ref-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-ref-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-ref-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }); // component is describe('parse-component-is-transform', function() { let source = ``; it('test-component-is-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(``); }); it('test-component-is-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(``); }); it('test-component-is-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`;\n`); }); it('test-component-is-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(`;\n`); }); it('test-component-is-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(`;\n`); }); it('test-component-is-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(`;\n`); }); }); // // animation describe('parse-v-animation-transform', function() { let source = ``; it('test-c-animation-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-c-animation-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-c-animation-transform-wx', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); it('test-c-animation-transform-qq', function() { expect(compileTemplate(source, 'qq', options).source).to.equal(``); }); it('test-c-animation-transform-baidu', function() { expect(compileTemplate(source, 'baidu', options).source).to.equal(``); }); it('test-c-animation-transform-alipay', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); // // attribute describe('parse-attribute-transform', function() { let source = ``; it('test-attribute-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
`); }); it('test-attribute-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
`); }); it('test-attribute-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(``); }); }) // //各种 < > 的转化 describe('parse-gtlt-transform', function() { let source = `{{ 5 > 6 ? 'this is 5':'this is 6'}}`; it('test-gtlt-transform-web', function() { expect(compileTemplate(source, 'web', options).source).to.equal(`
{{5 > 6 ? \'this is 5\' : \'this is 6\'}}
`); }); it('test-gtlt-transform-weex', function() { expect(compileTemplate(source, 'weex', options).source).to.equal(`
{{5 > 6 ? \'this is 5\' : \'this is 6\'}}
`); }); it('test-gtlt-transform-miniapp', function() { expect(compileTemplate(source, 'wx', options).source).to.equal(`{{5 > 6 ? \'this is 5\' : \'this is 6\'}}`); }); }); describe('test-alipaycomponent-wraped-vue', function() { let source = ``; it('test-alipaycomponent-wraped', function() { expect(compileTemplate(source, 'alipay', options).source).to.equal(``); }); }); describe('test-class-noinjected', function() { let source = ``; let cpOptions = JSON.parse(JSON.stringify(options)); cpOptions.isInjectBaseStyle = false; it('test-class-noinjected-web', function() { expect(compileTemplate(source, 'web', cpOptions).source).to.equal(`
`); }); it('test-class-noinjected-weex', function() { expect(compileTemplate(source, 'weex', cpOptions).source).to.equal(`
`); }); it('test-class-noinjected-alipay', function() { expect(compileTemplate(source, 'alipay', cpOptions).source).to.equal(``); }); it('test-class-noinjected-baidu', function() { expect(compileTemplate(source, 'baidu', cpOptions).source).to.equal(``); }); it('test-class-noinjected-wx', function() { expect(compileTemplate(source, 'wx', cpOptions).source).to.equal(``); }); }); }) ================================================ FILE: packages/chameleon-template-parse/webpack.config.js ================================================ var webpack = require('webpack'); var path = require('path'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var WebpackDevServer = require('webpack-dev-server'); var CleanWebpackPlugin = require('clean-webpack-plugin'); var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); module.exports = { mode:'production', entry:{ app:path.resolve(__dirname,'./old-test/index.js') }, output:{ path:path.resolve(__dirname,'distdev'), filename:'[name].js', }, resolve:{ extensions: [ '.js','.vue'], }, devtool: 'inline-source-map',//方便调试的时候定位到源码,而不是经过打包压缩之后的代码; plugins:[ new HtmlWebpackPlugin({ title:'webpack-base-learn', filename:'index.html', template:'./index.html', inject: true }), new CleanWebpackPlugin(path.resolve(__dirname,'distdev')), ], devServer:{ contentBase:path.join(__dirname,'distdev'), compress:true, port:9000, }, } ================================================ FILE: packages/chameleon-templates/.eslintrc ================================================ { // 在配置文件里配置全局变量时,使用 globals 指出你要使用的全局变量。将变量设置为 true 将允许变量被重写,或 false 将不允许被重写 "globals": { "cml": false }, // 环境定义了预定义的全局变量。 "env": { //环境定义了预定义的全局变量。更多在官网查看 "browser": true, "node": true, "commonjs": true, "amd": true, "es6": true, "mocha": true }, // JavaScript 语言选项 "parserOptions": { // ECMAScript 版本 "ecmaVersion": 6, "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", "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/chameleon-templates/.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 node_modules ================================================ FILE: packages/chameleon-templates/component/component/index.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.alipay.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.baidu.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.interface ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.qq.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.tt.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.web.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.weex.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-component/index.wx.cml ================================================ ================================================ FILE: packages/chameleon-templates/component/interface-js/index.interface ================================================ ================================================ FILE: packages/chameleon-templates/index.js ================================================ const path = require('path'); exports.serverTpl = path.join(__dirname, './server'); exports.blankDemoTpl = path.join(__dirname, './project'); exports.pageTpl = path.join(__dirname, './page'); exports.componentTpl = path.join(__dirname, './component'); exports.package = path.join(__dirname, './package.json'); exports.todoDemoTpl = path.join(__dirname, './todo-demo'); ================================================ FILE: packages/chameleon-templates/package.json ================================================ { "name": "chameleon-templates", "version": "1.0.8", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" " }, "author": "Chameleon-Team", "license": "Apache", "mail": "ChameleonCore@didiglobal.com", "gitHead": "0cd244cfa971463102a7bef668921b38a9d42fac" } ================================================ FILE: packages/chameleon-templates/page/index.cml ================================================ ================================================ FILE: packages/chameleon-templates/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 node_modules/ 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 node_modules ================================================ FILE: packages/chameleon-templates/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', platforms: ['web', 'weex', 'wx'], buildInfo: { wxAppId: '123456' }, wx: { dev: { }, build: { apiPrefix } }, web: { dev: { analysis: false, console: false, isWrapComponent: false // 取消默认对组件的包裹 }, build: { analysis: false, publicPath: `${publicPath}/web/`, apiPrefix, isWrapComponent: false // 取消默认对组件的包裹 } }, weex: { dev: { isWrapComponent: false // 取消默认对组件的包裹 }, build: { publicPath: `${publicPath}/weex/`, apiPrefix, isWrapComponent: false // 取消默认对组件的包裹 }, custom: { publicPath: `${publicPath}/wx/`, apiPrefix } }, optimize: { watchNodeModules: false, // 设置为true对于调试 node_modules 里面的内容很有帮助 showWarning: false, // 设置为true可以在构建过程中看到警告信息,比如编译过程中引入了同一个npm包的不同版本会在终端输出信息 dropConsole: true, // 可以配置是否压缩模式下删除调试信息 processBar: true // 可以配置是否需要构建进度条 } }) ================================================ FILE: packages/chameleon-templates/project/mock/api/index.js ================================================ module.exports = [ { method: ['get', 'post'], path: '/api/getMessage', controller: function (req, res, next) { res.json({ total: 0, message: [{ name: 'Hello chameleon!' }] }); } } ] ================================================ FILE: packages/chameleon-templates/project/mock/template/index.php ================================================ "0", "errmsg"=> "", "pageData"=> array( "name"=>"chameleon", "age" => 10 ) ); ================================================ FILE: packages/chameleon-templates/project/package.json ================================================ { "name": "chameleon", "version": "1.0.0", "description": "A chameleon project", "author": "", "private": true, "scripts": {}, "license": "MIT", "dependencies": { "chameleon-api": "1.0.0", "chameleon-runtime": "1.0.1", "chameleon-store": "1.0.1", "chameleon-ui-builtin": "1.0.6", "cml-ui": "1.0.3" } } ================================================ FILE: packages/chameleon-templates/project/src/app/app.cml ================================================ ================================================ FILE: packages/chameleon-templates/project/src/components/demo-com/demo-com.cml ================================================ ================================================ FILE: packages/chameleon-templates/project/src/pages/index/index.cml ================================================ ================================================ FILE: packages/chameleon-templates/project/src/router.config.json ================================================ { "mode": "history", "domain": "https://www.chameleon.com", "routes":[ { "url": "/cml/h5/index", "path": "/pages/index/index", "name": "首页", "mock": "index.php" } ] } ================================================ FILE: packages/chameleon-templates/project/src/store/actions.js ================================================ export default { } ================================================ FILE: packages/chameleon-templates/project/src/store/getters.js ================================================ export default { } ================================================ FILE: packages/chameleon-templates/project/src/store/index.js ================================================ import actions from './actions' import getters from './getters' import state from './state' import mutations from './mutations' import createStore from "chameleon-store"; export default createStore({ actions, getters, state, mutations }) ================================================ FILE: packages/chameleon-templates/project/src/store/mutations.js ================================================ export default { } ================================================ FILE: packages/chameleon-templates/project/src/store/state.js ================================================ const state = { } export default state ================================================ FILE: packages/chameleon-templates/server/fisdata/FISData.class.php ================================================ datatype; } protected function existDataFile($id) { $filepath = Util::normalizePath(WWW_ROOT . '/test/' . preg_replace('/\.[a-z]{2,6}$/i', '.' . $this->getDatatype(), $id)); if (is_file($filepath)) { return $filepath; } return false; } /** * @param $tmpl 当前渲染模板路径 */ protected function getId($tmpl) { $root = Util::normalizePath(WWW_ROOT . 'template'); $id = str_replace($root, '', Util::normalizePath($tmpl)); //add zhangnan03 $root_page = Util::normalizePath(WWW_ROOT . 'page'); $id = str_replace($root_page, '', $id); //add end return $id; } public function getCookieId() { $cookie_id = $_COOKIE['FIS_DEBUG_DATA_ID']; if ($cookie_id) { $cookie_id = trim($cookie_id); if ($cookie_id !== '') { $arr = explode('|', $cookie_id); if (trim($arr[0]) == $this->datatype) { $cookie_id = $arr[1]; } else { $cookie_id = ''; } } } else { $cookie_id = ''; } return $cookie_id; } public function getData($tmpl) {} /** * 获取选定的数据文件路径 * @param $tmpl * @return bool|string */ protected function getFile($tmpl) { $id = $this->getId($tmpl); $info = pathinfo($id); $filepath = ''; //特定数据 if ($cookie_id = $this->getCookieId()) { $tmp_id = $info['dirname'] . '/' .$info['filename'] .'/'. $cookie_id; $filepath = $this->existDataFile($tmp_id); } else if (($list = $this->getDataList($tmpl))) { //当前提供多份数据 $filepath = current($list); //first } if (!$filepath) { //没有多份数据时,默认数据路径 $filepath = Util::normalizePath(WWW_ROOT . '/test/' . preg_replace('/\.[a-z]{2,6}$/i', '.' . $this->datatype, $id)); } return $filepath; } /** * 多份测试数据 * @param $tmpl * @return array */ public function getDataList($tmpl) { $id = $this->getId($tmpl); $info = pathinfo($id); $test_dir = Util::normalizePath(WWW_ROOT . '/test/' . $info['dirname'] . '/' . $info['filename']); $files = Util::find($test_dir, '/' . $info['filename'] . '_\d+\.'.$this->datatype.'/i'); if ($files) { foreach ($files as $k => $filepath) { $files[str_replace($test_dir . '/', '', $filepath)] = $filepath; unset($files[$k]); } } return $files; } public function get($post) { $filepath = $post['path']; if (!is_file($filepath)) { echo ""; return; } echo file_get_contents($filepath); } public function save($post) { $file = $post['path']; $dir = dirname($file); if (!is_dir($dir) && !mkdir($dir, 0755, true)) { echo '{"message": "填写的路径无法创建,请重新填写!", "code": 1}'; exit(1); } if (!is_file($file) && false === file_put_contents($file, '')) { echo '{"message": "填写的路径无法创建,请重新填写!", "code": 1}'; exit(1); } $data = $post['data']; file_put_contents($file, $data); echo '{"message": "保存成功", "code": 0}'; } public function getCurrentFilePath($tmpl) { //以后支持多份测试数据 return $this->getFile($tmpl); } } ================================================ FILE: packages/chameleon-templates/server/fisdata/Manager.class.php ================================================ , 'json' => ...) * @param $tmpl 当前展示页面的模板路径 */ public function __construct($data_arr, $tmpl) { $this->_data_arr = $data_arr; $this->_tmpl = $tmpl; } public function getDefault() { $default_arr = array(); $default = $this->_data_arr[$this->getCurrentDatatype()]; $default_arr['datatype'] = $default->getDatatype(); $default_arr['path'] = $default->getCurrentFilepath($this->_tmpl); $default_arr['data'] = htmlspecialchars(file_get_contents($default->getCurrentFilepath($this->_tmpl))); $default_arr['list'] = $default->getDataList($this->_tmpl); //默认选定 $default_arr['list_default'] = $default->getCookieId(); return $default_arr; } public function getCurrentDatatype() { $datatype = trim($_COOKIE['FIS_DEBUG_DATATYPE']); if (!$datatype) { $datatype = 'php'; } return $datatype; } public function getRenderData() { $datatypes = array(); foreach ($this->_data_arr as $data_instance) { $datatypes[] = $data_instance->getDatatype(); } return array( 'datatypes' => $datatypes, 'default' => $this->getDefault(), ); } public function render() { require_once (WWW_ROOT . 'smarty/Smarty.class.php'); $smarty = new Smarty(); $smarty->setPluginsDir(array( WWW_ROOT . 'smarty/plugins' )); $smarty->setLeftDelimiter('{%'); $smarty->setRightDelimiter('%}'); $smarty->assign($this->getRenderData()); $smarty->display(dirname(__FILE__) . '/index.tpl'); exit(); } } ================================================ FILE: packages/chameleon-templates/server/fisdata/README.md ================================================ ##fis-server-fisdata 使用FIS开发时,提供本地调试数据功能。 ###使用 //require必要的类文件 require_once ("/TestData.php"); //初始化类,必须放在其他router的最前面 TestData::init(); //渲染数据 // $templateEngine 模板引擎,必须包含方法assign, display 如:smarty // $tmpl 当前要渲染的模板 TestData::renderHelper($templateEngine, $tmpl); ####浏览器书签 //新建浏览器书签,网址为以下内容 javascript: void function() {var d = new Date();d.setFullYear(d.getFullYear() + 1);document.cookie='FIS_DEBUG_DATA=4f10e208f47bfb4d35a5e6f115a6df1a;path=/;expires=' + d.toGMTString() + '';location.reload(); }(); 在预览的时候点击书签,进入数据管理页面,修改数据后再进行渲染。 ###在FIS安装 //安装特定版本 fis server install fisdata@1.0.1 or //安装最新版本 fis server install fisdata ###测试数据 测试数据存储在网站根目录的test目录下。 模板文件放在网站根目录的template目录下。 模板文件和测试数据文件对应关系如下: 模板: `/template/page/photo/index.tpl` 对应数据文件: 0. /test/page/photo/index.php (php格式) 0. /test/page/photo/index.json (json格式) 0. /test/page/photo/index.text (adoc格式) 也支持多份数据(php格式为例): 0. /test/page/photo/index/index_1.php 0. /test/page/photo/index/index_2.php ... 文件名: `index_\d+.php` **注:adoc多份数据参见http://fe.baidu.com/doc/fis** ================================================ FILE: packages/chameleon-templates/server/fisdata/TestData.class.php ================================================ 'image/bmp', 'css' => 'text/css', 'doc' => 'application/msword', 'dtd' => 'text/xml', 'gif' => 'image/gif', 'hta' => 'application/hta', 'htc' => 'text/x-component', 'htm' => 'text/html', 'html' => 'text/html', 'xhtml' => 'text/html', 'ico' => 'image/x-icon', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'text/javascript', 'json' => 'application/json', 'mocha' => 'text/javascript', 'mp3' => 'audio/mp3', 'mp4' => 'video/mpeg4', 'mpeg' => 'video/mpg', 'mpg' => 'video/mpg', 'manifest' => 'text/cache-manifest', 'pdf' => 'application/pdf', 'png' => 'image/png', 'ppt' => 'application/vnd.ms-powerpoint', 'rmvb' => 'application/vnd.rn-realmedia-vbr', 'rm' => 'application/vnd.rn-realmedia', 'rtf' => 'application/msword', 'svg' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'txt' => 'text/plain', 'vml' => 'text/xml', 'vxml' => 'text/xml', 'wav' => 'audio/wav', 'wma' => 'audio/x-ms-wma', 'wmv' => 'video/x-ms-wmv', 'xml' => 'text/xml', 'xls' => 'application/vnd.ms-excel', 'xq' => 'text/xml', 'xql' => 'text/xml', 'xquery' => 'text/xml', 'xsd' => 'text/xml', 'xsl' => 'text/xml', 'xslt' => 'text/xml' ); public static function register(FISData $data_instance) { self::$_data_queue[$data_instance->getDatatype()] = $data_instance; } public static function init() { //注册对象 self::register(new FISPHPData()); self::register(new FISJSONData()); self::register(new FISAdocData()); self::$_chameleon_data = new ChameleonData(); self::$_flush_data_queue = array(); //暂时只需要一份数据 $datatype = $_COOKIE['FIS_DEBUG_DATATYPE']; $flush_data = self::$_data_queue[$datatype]; if (!$flush_data) { //取数组第一个元素 FISPHPData对象 $flush_data = current(self::$_data_queue); } if ($flush_data) { self::$_flush_data_queue[] = $flush_data; } self::router(); } public static function renderHelper($template_instance, $candidate_tmpl) { $mockData = self::$_chameleon_data->getMockContent(); //如果通过json文件找到mock数据 if($mockData) { $puredata=$_GET[puredata]; //返回ajax数据 if($puredata == 1) { $ajaxData = array(); $ajaxData[errno] = $mockData[errno]; $ajaxData[errmsg] = $mockData[errmsg]; $ajaxData[data] = $mockData[pageData]; echo json_encode($ajaxData); return; } else { $template_instance->assign($mockData); } } else { // 走之前的mock逻辑 mock与模板同名的php文件 foreach (self::$_flush_data_queue as $data_instance) { //$data_instance FISPHPData对象 $template_instance->assign($data_instance->getData($candidate_tmpl)); } } $template_instance->display($candidate_tmpl); } public static function router() { $request_uri = $_SERVER['REQUEST_URI']; $pos = strpos($request_uri, '?'); if ($pos !== false) { $request_uri = substr($request_uri, 0, $pos); } $uris = explode('/', $request_uri); //没有进入 if ($uris[1] == 'fisdata') { if ($uris[2] == 'static') { $uri_file = realpath(WWW_ROOT . $request_uri); if (is_file($uri_file)) { $info = pathinfo($uri_file); header("Content-Type: " . self::$MIME[$info['extension']]); echo file_get_contents($uri_file); exit; } } else { self::doAction($uris[2]); } } } private static function doAction($action) { $params = $_POST; foreach (self::$_flush_data_queue as $data_instance) { if (method_exists($data_instance, $action)) { call_user_func_array(array($data_instance, $action), array('params' => $params)); } } exit(); } } ================================================ FILE: packages/chameleon-templates/server/fisdata/bookmark.js ================================================ javascript: void function() { var d = new Date(); d.setFullYear(d.getFullYear() + 1); document.cookie = 'FIS_DEBUG_DATA=4f10e208f47bfb4d35a5e6f115a6df1a;path=/;expires=' + d.toGMTString() + ''; location.reload(); }(); ================================================ FILE: packages/chameleon-templates/server/fisdata/index.tpl ================================================ FIS - 本地测试数据
{%foreach $datatypes as $datatype%} {%/foreach%}
{%if (count($default.list) > 0) and ($default.datatype !== 'adoc')%} {%elseif (count($default.list) > 0)%} {%foreach $default.list as $key => $data%}
  数据{%$key%}
{%$data%}
{%/foreach%} {%/if%}
{%$default.data%}
================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/CommonHelper.php ================================================ 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } //echo $new_json; return $new_json; } public static function json_Html_format($json) { //echo $json; $tab = " "; $new_json = ""; $indent_level = 0; $in_string = false; $json_obj = json_decode($json); if($json_obj === false) return false; // $json = json_encode($json_obj); $len = strlen($json); $strBlank = ""; for($c = 0; $c < $len; $c++) { $char = $json[$c]; switch($char) { case '{': case '[': if(!$in_string) { $strBlank .= "   "; $new_json .= $char . "
\n $strBlank" . str_repeat($tab, $indent_level+1); $indent_level++; } else { $new_json .= $char; } break; case '}': case ']': if(!$in_string) { $strBlank = substr($strBlank, 0, (strlen($strBlank) - 18)); $indent_level--; $new_json .= "
\n $strBlank" . str_repeat($tab, $indent_level) . $char; } else { $new_json .= $char; } break; case ',': if(!$in_string) { $new_json .= ",
\n $strBlank" . str_repeat($tab, $indent_level); } else { $new_json .= $char; } break; case ':': if(!$in_string) { $new_json .= ": "; } else { $new_json .= $char; } break; case '"': if($c > 0 && $json[$c-1] != '\\') { $in_string = !$in_string; } default: $new_json .= $char; break; } } return $new_json; } //û�õ� public static function remove_redundant_backslash($json) //ɾ��������json_encode()����Ķ���ķ�б�� { // $trans = array('\\\/' => '/', '\"' => '"', '\/\/' => '//'); $trans = array('\\\/' => '/'); $new_json = strtr($json, $trans); return $new_json; } //�������е������ַ���utf8���� ���õݹ� public static function array_utf8_encode($arrSrc) { if (is_array($arrSrc)) { // echo "111111111111\n"; foreach($arrSrc as $k=>$v) { if (is_array($v)) { $arrSrc[$k] = CommonHelper::array_utf8_encode($v); } else if(is_string($v)) { $v = urlencode($v); $arrSrc[$k] = $v; // echo "22222222222".$v."\n"; } } } else if(is_string($arrSrc)) { $arrSrc = urlencode($arrSrc); // echo "33333333".$arrSrc."\n"; } return $arrSrc; } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/Demo.php ================================================ setPath(); /* * 接口2 生成测试数据,并将数据保存至接口1设置的位置 * 输入:adoc文档的内容 * 返回: true or false 提示生成成功或失败 */ //$demo->genCaseData($Content,"http://fe.baidu.com/repos/doc/iknow/exp/api/fisApi/php"); $demo->genCaseData($Content,"D:/build20120809/sample01/list/test/adoc/"); //$demo->genCaseData($Content); /* * 接口3 测试数据case获取 * 输入:uri,不包含地址及参数 * 返回:json格式的case内容,包含多份case数据 */ $testCase = $demo->getCaseData("list"); //echo $testJsonCase; file_put_contents(Config::$strPath.'/./tmp/Terminal.php',print_r($testCase, true)); $testHtmlCase = $demo->getHtmlCaseData("list"); // echo "".$testHtmlCase; file_put_contents(Config::$strPath.'/./tmp/TerminalHtml.php',print_r($testHtmlCase, true)); /* $aa=$testCase[1]; foreach ( $aa as $key=>$value ) { echo "key=".$key." and value=".$value."
"; } //echo $testCase;*/ echo "OK"; ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/adocParser.php ================================================ getInheritNewAdoc($jsonFileContent); // $newAdocArray = $this->getNewAdocArray($jsonFileContent); $newAdocArray = $this->getJsonInheritNewAdoc($adocFilePath); //var_dump($newAdocArray); $posNote = array(); $this->array_all("", $newAdocArray, "", $posNote); file_put_contents(Config::$strPath."/./tmp/PosNote.php", print_r($posNote, true)); //首先去掉无用的注释,无用的注释指非范围标识的注释 // $adocAfterStep1 = preg_replace('/\/\/[\s]*[\((.*)\)/]', '', $jsonFileContent); // file_put_contents(dirname(__FILE__)."/./tmp/adocAfterStep1.php", $adocAfterStep1); // return $arrPosNote; return $posNote; } public function getPhpNotes($adocFilePath) { // $this->getInheritNewAdoc($jsonFileContent); // $newAdocArray = $this->getNewAdocArray($jsonFileContent); $newAdocArray = $this->getPhpInheritNewAdoc($adocFilePath); //var_dump($newAdocArray); $posNote = array(); $this->array_all("", $newAdocArray, "", $posNote); file_put_contents(Config::$strPath."/./tmp/PosNote.php", print_r($posNote, true)); //首先去掉无用的注释,无用的注释指非范围标识的注释 // $adocAfterStep1 = preg_replace('/\/\/[\s]*[\((.*)\)/]', '', $jsonFileContent); // file_put_contents(dirname(__FILE__)."/./tmp/adocAfterStep1.php", $adocAfterStep1); // return $arrPosNote; return $posNote; } //从adoc解析出需要动态生成值的字段及其可选的值 //将注释"na=>me" => "network"中的双引号中的'=>'转变为"||ArRoW_In_StR||" public function changeArrowInNote($note) { $notelen = strlen($note); // $idxFront = strpos($note, '"') -1; // $idxRear = strpos($note, '"') + 1; $i = 0; $bInString = false; while($i != $notelen) { $oneChar = substr($note, $i, 1); $twoChar = substr($note, $i, 2); $frontChar = ""; if($i > 1) { $frontChar = substr($note, $i - 1, 1); } //echo "oneChar: $oneChar, frontChar: $frontChar\n"; if($oneChar == '"') { if(!$bInString) { $bInString = true; } else { $bInString = false; } } if($bInString && $twoChar == '=>') { $strFront = substr($note, 0, $i); $strRear = substr($note, ($i + 2)); $note = $strFront."||ArRoW_In_StR||".$strRear; //echo "$note\n"; $notelen = strlen($note); } $i ++; } //file_put_contents(dirname(__FILE__)."/./tmp/note.php", "NOTE: $note\n"); return $note; } //将字符串中的"||ArRoW_In_StR||"变回"=>" function changeArrowBack($value) { $trans = array("||ArRoW_In_StR||" => "=>"); $value = strtr($value, $trans); //file_put_contents(dirname(__FILE__)."/./tmp/note.php", "BACK: $value\n"); return $value; } //将注释//<"st,r1", "str2">中的双引号中的','转变为"||CoMmA_In_StR||" public function changeCommaInNote($note) { // $note = '//<"点,心1","饼干1">'; //echo "NOTE: $note\n"; $notelen = strlen($note); // $idxFront = strpos($note, '"') -1; // $idxRear = strpos($note, '"') + 1; $i = 0; $bInString = false; while($i != $notelen) { $oneChar = substr($note, $i, 1); $frontChar = ""; if($i > 1) { $frontChar = substr($note, $i - 1, 1); } //echo "oneChar: $oneChar, frontChar: $frontChar\n"; if($oneChar == '"' && $frontChar != '\\') { if(!$bInString) { $bInString = true; } else { $bInString = false; } } if($bInString && $oneChar == ',') { $strFront = substr($note, 0, $i); $strRear = substr($note, ($i + 1)); $note = $strFront."||CoMmA_In_StR||".$strRear; //echo "$note\n"; $notelen = strlen($note); } $i ++; } //file_put_contents(dirname(__FILE__)."/./tmp/note.php", "NOTE: $note\n"); return $note; } //由 修改后的adoc文档生成的数组$newAdocArray 生成范围数组 //$key 传入的element的key //$arrSrc 传入的element的value //$parentKey 传入的element的路径 //$posNote 记录用的数组 public function array_all($key, $arrSrc, $parentKey, &$posNote) { if (is_array($arrSrc)) { if($parentKey == "") { $parentKey = $key; } else { $parentKey .= ",$key"; } foreach($arrSrc as $k=>$v) { $this->array_all($k, $v, $parentKey, $posNote); } } else { $elementPath = ""; $elementValue = ""; $elementType = ""; //获取$elementPath if($parentKey == "") { //echo "$key $arrSrc\n"; //echo "$key\n"; $elementPath = $key; } else { //echo "$parentKey,$key $arrSrc\n"; //echo "$parentKey,$key\n"; $elementPath = $parentKey.",".$key; } //获取$elementValue $elementValue = $arrSrc; $tmpValueStrArray = explode(":", $elementValue); $tmpValueStr = $tmpValueStrArray[0]; $colonPos = strpos($elementValue, ":"); if($tmpValueStr == "STRsHoUlDbE") { // $elementValue = $tmpValueStrArray[1]; $elementValue = substr($elementValue, $colonPos + 1); $elementType = "string"; } else if($tmpValueStr == "INTsHoUlDbE") { // $elementValue = $tmpValueStrArray[1]; $elementValue = substr($elementValue, $colonPos + 1); $elementType = "int"; } else if($tmpValueStr == "ArRaYrEpEaTe") { $elementPath = $this->popJsonPos($elementPath); //$elementPath = $this->popJsonPos($elementPath); $elementValue = substr($elementValue, $colonPos + 1); $elementValue = trim($elementValue); $elementValue = "/...".$elementValue; $elementType = "object"; } else { $elementValue = $arrSrc; // //var_dump($elementValue); //echo "NIMA!!!\n"; if(is_int($elementValue)) { $elementType = "int"; } else if(is_string($elementValue)) { $elementType = "string"; } else if(is_bool($elementValue)) { $elementType = "string"; } else if(is_array($elementValue)) { //echo "NIMEI???\n"; } $elementValue = ""; } // //echo "posNote\n"; // //var_dump($posNote); //echo "key: $elementPath; value: $elementValue; type: $elementType\n"; $elementArray = array(); array_push($elementArray, $elementPath); array_push($elementArray, $elementValue); array_push($elementArray, $elementType); array_push($posNote, $elementArray); // return $posNote; } } //从adoc文件中解析出json部分。 public function getJsonBlock($adocPath) { $contant=$this->getDataBlock($adocPath,"main"); //echo "ctctctctct".$contant."END"; return $contant; //return $this->getDataBlock($adocPath,"main"); } public function getDefineBlock($adocPath){ $contant=$this->getDataBlock($adocPath,"define"); return $contant; } //从adoc中解析出url内容。 public function getUrlBlock($adocPath) { $contant=$this->getDataBlock($adocPath,"url"); //echo "ctctctctct".$contant."END"; return $contant; } //从adoc文件中解析出adoctype部分。 public function getAdocTypeBlock($adocPath) { $contant=$this->getDataBlock($adocPath,"adoctype"); //echo "contant:$contant\n"; return $contant; //return $this->getDataBlock($adocPath,"main"); } //从adoc中解析出ref内容,即异步数据文件的相对路径。 public function getRefPathArr($adocPath) { $asyncPathString=$this->getDataBlock($adocPath,"ref"); return explode(",",$asyncPathString); } public function getParentAdocPath($adocPath) { //echo "getParentAdocPath\n"; $content=$this->getDataBlock($adocPath,"parent"); //echo "content:$content\n"; return $content; } //从aodc中解析出addon内容,即模板组件的相对路径 public function getAddonArr($adocPath) { $addonString=$this->getDataBlock($adocPath,"addon"); //echo "addonString: ".$addonString."\n"; if($addonString == "") { $addonString = trim($addonString); return null; } return explode(",",$addonString); } /* *由Adoc文档路径 *提取json和url串并返回 */ private function getDataBlock($adocPath,$blockName) { $fOpen = fopen($adocPath,"r"); $content = ""; $isJsonStr = false; while(!feof($fOpen)) { $line = fgets($fOpen); //echo $line."\n"; if(preg_match('/^\s*\#\#\#\#'.$blockName.'$/',trim($line))) { $isJsonStr = true; continue; } else if(preg_match('/\#\#\#\#/i',trim($line)) && $isJsonStr) { break; } if(!$isJsonStr || (trim($line) == "") || (trim($line) == ":::javascript")) { continue; } else { if ($blockName=="main") { //we need the \n at the end of line. $content = $content.$line; } elseif ( $blockName=="url" ) { $content = $content.trim($line); } elseif ( $blockName=="parent" ) { $content = $content.trim($line); } elseif ($blockName=="ref") { $content = $content.trim($line).","; } elseif ($blockName == "addon") { $content = $content.trim($line).","; } elseif ($blockName=="define") { //we need the \n at the end of line. $content = $content.$line; } elseif ($blockName == "adoctype") { $content = $content.trim($line); //echo $content; } } } fclose($fOpen); //echo $content; return $content; } //拼接处json中的所有需要改值的key。 private function pushJsonPos($arrPos,$key) { $inStr = "";//最终入串的字符串 $reStr = ""; //echo "push json pos function param: ".$key." line:".__LINE__."
"; if(substr($key,0,1) == "'" || substr($key,0,1) == '"') { //去掉引号 $inStr = substr($key,1,strlen($key)-2); //echo $inStr."\n"; } else { //不带引号的,直接添加。 $inStr = $key; } if($arrPos != "") { $reStr = $arrPos.",".$inStr; } else { $reStr = $inStr; } // //echo "push json pos function return: ".$reStr."
\n"; return $reStr; } private function popJsonPos($arrPos) { //echo "before pop: ".$arrPos." line:".__LINE__."\n"; $arr = explode(",",$arrPos); $reStr = ""; if(is_array($arr)) { //var_dump($arr); $tmpPop = array_pop($arr); //var_dump($tmpPop); if(is_int($tmpPop)) { $this->strPop = $tmpPop; //echo "pop array no ".$this->strPop." line:".__LINE__; } $lastNum = count($arr) - 1; $arr[$lastNum] = $arr[$lastNum] - 1; for($i=0;$i\n"; return $reStr; } private function popArrIndex($arrIndex) { //echo "before pop: ".$arrIndex." line:".__LINE__."\n"; $arr = explode(",",$arrIndex); $reStr = ""; if(is_array($arr)) { $tmpPop = array_pop($arr); for($i=0;$ireduce_string($line); if ($lineTem != "") { $commaMark = 0; if ( substr($lineTem, 0, 1) != "]") { $newAdoc = trim($newAdoc); $newAdoc .= ",\n"; } } } //preg_match执行一个正则表达式匹配“枚举类型” if(preg_match('/\/\/[\s]*<(.*)>[\s]*/', $line, $matchs) || preg_match('/\/\/[\s]*\[(.*)\][\s]*/', $line, $matchs) || preg_match('/\/\/[\s]*\((.*)\)[\s]*/', $line, $matchs)) { //echo $line; //获取范围注释内容 $note = $matchs[0]; //echo "note: $note\n"; //将注释//<"st,r1", "str2">中的双引号中的','转变为"||CoMmA_In_StR||" $note = $this->changeCommaInNote($note); //echo "note: $note\n"; //将$line中的"key":"value"的value用可选的值$strNote替换 $line = $genArray->reduce_string($line);//删除所有注释 // $line = $genArray->trim_string($line); $lastSymbol = substr($line, -1);// //echo "lastSymbol: ".$lastSymbol."\n"; $hasComos = false; if(',' == $lastSymbol) { $line = substr($line, 0, strlen($line)-1); $hasComos = true; } $line = "{".$line."}"; //echo "line:".$line."\n"; $lineArray = json_decode($line); //var_dump($lineArray); $key = ""; $value = ""; //var_dump($lineArray); foreach ($lineArray as $k => $v) { $key = $k; $value = $v; } //echo "type: ".gettype($value)."\n"; //获取value要改成的字符串$strNote //获取$line中"key":"value"的value的类型,写入$strValueType $strValueType = gettype($value); if($strValueType != "string") { $strValueType = "int"; } //echo "strValueType : $strValueType\n"; //根据注释内容$note和类型$strValueType生成可选的值$strNote require_once(dirname(__FILE__).'/genValue.php'); $genValue = new genValue($note,$strValueType); $strNote = $genValue->getValue($value); if($strValueType == "string") { $strNote = "STRsHoUlDbE:".$strNote; } else { $strNote = "INTsHoUlDbE:".$strNote; } //echo "note : $note\n"; //echo "strNote : $strNote\n"; $lineArray = array(urlencode($key) => urlencode($strNote)); //var_dump($lineArray); $line = json_encode($lineArray); $line = urldecode($line); $line = substr($line, 1, strlen($line)-2); if($hasComos) $line .= ","; $line .= "\n"; //echo "newline: $line"; } //匹配//(messycode) if(preg_match('/\/\/\(messycode\)[\s]*$/', $line, $matchs)) { //echo "55555555555555555555555555555555555\n"; //echo $line; //获取注释内容 $note = $matchs[0]; //获取$line中"key":"value"的value的类型,写入$strValueType $strValueType = "string"; //echo "$note\n"; require_once(dirname(__FILE__).'/genValue.php'); $genValue = new genValue($note,$strValueType); $value=""; $strNote = $genValue->getValue($value); $strNote = "STRsHoUlDbE:".$strNote; //将$line中的"key":"value"的value用可选的值$strNote替换 $line = $genArray->reduce_string($line); // $line = $genArray->trim_string($line); $lastSymbol = substr($line, -1); $hasComos = false; if(',' == $lastSymbol) { $line = substr($line, 0, strlen($line)-1); $hasComos = true; } $line = "{".$line."}"; $lineArray = json_decode($line); //var_dump($lineArray); $key = ""; foreach ($lineArray as $k => $v) { $key = $k; } $lineArray = array(urlencode($key) => urlencode($strNote)); //var_dump($lineArray); $line = json_encode($lineArray); $line = urldecode($line); $line = substr($line, 1, strlen($line)-2); if($hasComos) $line .= ","; $line .= "\n"; //echo "newline: $line"; } if(preg_match('/\/\/...\[(.*)\][\s]*$/', $line)) { //echo $line; $line = trim($line); $line = substr($line, 5); $line = "{\"arrayRepeate\" :\"ArRaYrEpEaTe: $line\"}\n"; $commaMark = 1; // 后续用于判断是否要加逗号 //echo "newline: $line\n"; //判断是否给上一行补"," $newAdoc = trim($newAdoc); if (substr($newAdoc, -1) != ",") { $newAdoc .= ",\n"; }else { $newAdoc .= "\n"; } } $newAdoc .= $line; } fclose($file_handle); // //echo "newAdoc: $newAdoc\n"; //echo "\n\n\n\n\n\n\n\n"; //错误,getArray()传入参数是路径 // $newAdocArray = $genArray->getArray($newAdoc); //读取json数据,并进行替换写回 $fileStr = $genArray->reduce_string($newAdoc); $tmpPath = Config::$strPath."/./tmp/AdocAfterStep1.php"; file_put_contents($tmpPath,$fileStr); //逐行trim decode 获得array $jsonStr = $genArray->trim_string($tmpPath); //$jsonStr = iconv("GBK","UTF-8",$jsonStr); //echo "encoding".mb_detect_encoding($jsonStr)."\n"; //echo "before".$jsonStr; //echo "jsonStr: $jsonStr\n"; $newAdocArray = json_decode($jsonStr,true); //print_r($newAdocArray); return $newAdocArray; } public function getPhpNewAdocArray($jsonFileContent) { //echo "jsonFileContent:$jsonFileContent\n"; $genArray = new genArray(); //CommonHelper::makeDir(dirname(__FILE__)."/./tmp"); CommonHelper::makeDir(Config::$strPath."/./tmp"); //将adoc文档另外存一个文件 file_put_contents(Config::$strPath."/./tmp/AdocFile2.php", $jsonFileContent); //按行读入文件,匹配处理 $file_handle = fopen(Config::$strPath."/./tmp/AdocFile2.php", "r"); $newAdoc = ""; $commaMark = 0; // 后续用于判断是否要加逗号 while (!feof($file_handle)) { $line = fgets($file_handle); $matchs = array(); // 判断是否要在...[]后加逗号 if ($commaMark == 1) { $lineTem = $genArray->reduce_string($line); if ($lineTem != "") { $commaMark = 0; if ( substr($lineTem, 0, 1) != ")") { $newAdoc = trim($newAdoc); $newAdoc .= ",\n"; //echo "a"; } } } //preg_match执行一个正则表达式匹配 if(preg_match('/\/\/[\s]*<(.*)>[\s]*/', $line, $matchs) || preg_match('/\/\/[\s]*\[(.*)\][\s]*/', $line, $matchs)) { //获取范围注释内容 $note = $matchs[0]; //echo "note: $note\n"; //将注释//<"st,r1", "str2">中的双引号中的','转变为"||CoMmA_In_StR||" $note = $this->changeCommaInNote($note); //echo "note: $note\n"; //将$line中的"key":"value"的value用可选的值$strNote替换 $line = $genArray->reduce_string($line);//删除所有注释 //echo $line; $lastSymbol = substr($line, -1);// //echo "lastSymbol: ".$lastSymbol."\n"; $hasComos = false; if(',' == $lastSymbol) { $line = substr($line, 0, strlen($line)-1); $hasComos = true; } $line=$this->changeArrowInNote($line); //var_dump($line); $lineArray = explode(" => ", $line); //将字符串中的"||ArRoW_In_StR||"变回"=>" $lineArray[0]=$this->changeArrowBack($lineArray[0]); $lineArray[1]=$this->changeArrowBack($lineArray[1]); //var_dump($lineArray); //----------change by xumeng02---------------- if('"'==substr($lineArray[1],0,1)) { $strValueType="string"; $value = substr($lineArray[1],1,strlen($lineArray[1])-2); //var_dump($value); }else { $strValueType = "int"; $value = intval($lineArray[1]); } //--end-----change by xumeng02---------------- //echo "strValueType : $strValueType\n"; //根据注释内容$note和类型$strValueType生成可选的值$strNote require_once(dirname(__FILE__).'/genValue.php'); $genValue = new genValue($note,$strValueType); $strNote = $genValue->getValue($value); if($strValueType == "string") { $strNote = "STRsHoUlDbE:".$strNote; } else { $strNote = "INTsHoUlDbE:".$strNote; } $line=$lineArray[0]."=>\"".$strNote."\""; //echo "newline:".$line."\n"; if($hasComos) $line .= ","; $line .= "\n"; } //匹配//(messycode) if(preg_match('/\/\/\(messycode\)[\s]*$/', $line, $matchs)) { //获取注释内容 $note = $matchs[0]; //获取$line中"key":"value"的value的类型,写入$strValueType $strValueType = "string"; //echo "$note\n"; require_once(dirname(__FILE__).'/genValue.php'); $genValue = new genValue($note,$strValueType); $value = ""; $strNote = $genValue->getValue($value); $strNote = "STRsHoUlDbE:".$strNote; //将$line中的"key":"value"的value用可选的值$strNote替换 $line = $genArray->reduce_string($line); $lastSymbol = substr($line, -1); $hasComos = false; if(',' == $lastSymbol) { $line = substr($line, 0, strlen($line)-1); $hasComos = true; } $line=$this->changeArrowInNote($line); $lineArray=explode(" => ", $line); $lineArray[0]=$this->changeArrowBack($lineArray[0]); $lineArray[1]=$this->changeArrowBack($lineArray[1]); $line=$lineArray[0]." => \"".$strNote."\""; //$line = urldecode($line); if($hasComos) $line .= ","; $line .= "\n"; //echo "newline: $line"; } if(preg_match('/\/\/...\[(.*)\][\s]*$/', $line)) { //echo $line; $line = trim($line); $line = substr($line, 5); $commaMark = 1; //判断范围注释前是否有逗号,如果没有,加上逗号 $comos_test = substr(trim($newAdoc),-1,1); if($comos_test != ",") $newAdoc=trim($newAdoc).","."\n"; $line = "array(\n\"arrayRepeate\" => \"ArRaYrEpEaTe: $line\")\n"; } $newAdoc .= $line; //echo $newAdoc; } fclose($file_handle); //读取json数据,并进行替换写回 $fileStr = $genArray->reduce_string($newAdoc); //echo "fileStr:$fileStr\n"; $tmpPath = Config::$strPath."/./tmp/AdocAfterStep1.php"; file_put_contents($tmpPath,$fileStr); eval($newAdoc); //print_r($newAdocArray); return $newAdocArray; } public function getJsonInheritNewAdoc($adocFilePath) { $jsonFileContent = $this->getJsonBlock($adocFilePath);//得到main中的json数据块 //echo $jsonFileContent; //获取 父adoc的内容,传入参数是 子adoc的本地地址;如果没有父adoc则返回"" $parentContent = $this->getParentContent($adocFilePath); if($parentContent == "") { $inheritNewAdoc = $this->getJsonNewAdocArray($jsonFileContent); } else { file_put_contents(Config::$strPath."/./Parent.php", $parentContent); $parentJson = $this->getJsonBlock(Config::$strPath."/./Parent.php"); //echo "parentJson:\n".$parentJson."\n"; $parentNewAdocArray = $this->getJsonNewAdocArray($parentJson); //echo "\n\n\nparentNewAdocArray:\n"; //var_dump($parentNewAdocArray); file_put_contents(Config::$strPath."/./tmp/ParentNewAdocArray.php",print_r($parentNewAdocArray,true)); //echo "childJson:\n$jsonFileContent\n"; $childNewAdocArray = $this->getJsonNewAdocArray($jsonFileContent); //echo "\n\n\nchildNewAdocArray:\n"; //var_dump($childNewAdocArray); file_put_contents(Config::$strPath."/./tmp/ChildNewAdocArray.php",print_r($childNewAdocArray,true)); $inheritNewAdoc = $this->getInheritResult($parentNewAdocArray, $childNewAdocArray); file_put_contents(Config::$strPath."/./tmp/InheritNewAdoc.php",print_r($inheritNewAdoc,true)); } return $inheritNewAdoc; } public function getPhpInheritNewAdoc($adocFilePath) { $jsonFileContent = $this->getJsonBlock($adocFilePath);//得到main中的json数据块 //echo $jsonFileContent; //获取 父adoc的内容,传入参数是 子adoc的本地地址;如果没有父adoc则返回"" $parentContent = $this->getParentContent($adocFilePath); //echo "parentContent:$parentContent\n"; if($parentContent == "") { $inheritNewAdoc = $this->getPhpNewAdocArray($jsonFileContent); } else { file_put_contents(Config::$strPath."/./Parent.php", $parentContent); $parentJson = $this->getJsonBlock(Config::$strPath."/./Parent.php"); //echo "parentJson:\n".$parentJson."\n"; $parentNewAdocArray = $this->getPhpNewAdocArray($parentJson); //echo "\n\n\nparentNewAdocArray:\n"; //var_dump($parentNewAdocArray); file_put_contents(Config::$strPath."/./tmp/ParentNewAdocArray.php",print_r($parentNewAdocArray,true)); //echo "childJson:\n$jsonFileContent\n"; $childNewAdocArray = $this->getPhpNewAdocArray($jsonFileContent); //echo "\n\n\nchildNewAdocArray:\n"; //var_dump($childNewAdocArray); file_put_contents(Config::$strPath."/./tmp/ChildNewAdocArray.php",print_r($childNewAdocArray,true)); $inheritNewAdoc = $this->getInheritResult($parentNewAdocArray, $childNewAdocArray); file_put_contents(Config::$strPath."/./tmp/InheritNewAdoc.php",print_r($inheritNewAdoc,true)); } //echo $inheritNewAdoc; return $inheritNewAdoc; } public function getInheritResult($parentNewAdocArray, $childNewAdocArray) { $inheritResult = $parentNewAdocArray; //遍历$childNewAdocArray,当 foreach ($childNewAdocArray as $childKey => $childValue) { //echo "childKey: $childKey\n"; // $inheritResult[$childKey] = $childValue; //替换已有内容,或添加没有的内容 if(array_key_exists($childKey, $inheritResult)) { //echo "Exists, "; if(is_array($childValue)) { //echo "but is array!\n"; $inheritResult[$childKey] = $this->getInheritResult($inheritResult[$childKey], $childValue); } else { //echo "and is not array!\n"; $inheritResult[$childKey] = $childValue; } } else { //echo "Not exists!\n"; $inheritResult[$childKey] = $childValue; } } //var_dump($inheritResult); return $inheritResult; } //获取 父adoc的内容,传入参数是 子adoc的本地地址 //如果没有父adoc则返回"" public function getParentContent($adocFilePath) { $parentAdocPath = $this->getParentAdocPath($adocFilePath); //echo "parentAdocPath:$parentAdocPath\n"; //var_dump($parentAdocPath); if(trim($parentAdocPath) == "") { return ""; } //echo "asyncBasePath: ".Config::$asyncBasePath."\n"; $parentAdocPath = Config::$asyncBasePath.trim($parentAdocPath).".text"; //echo $parentAdocPath; $parentContent = file_get_contents($parentAdocPath); //echo "parentContent: ".$parentContent."\n"; return $parentContent; } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genArray.php ================================================ getParentContent($adocFilePath); //echo $parentContent; if($parentContent == "") { $inheritStandArray = $this->getJsonArray($jsonFilePath); //echo $inheritStandArray; } else { $parentJsonFileContent = $adocParser->getJsonBlock(Config::$strPath."/./Parent.php"); $parentJsonFilePath = Config::$strPath."/./tmp/parentFile.php"; file_put_contents($parentJsonFilePath,$parentJsonFileContent); //分别获取 子adoc文档的标准数组 和 父adoc文档的标准数组 $childStandArr = $this->getJsonArray($jsonFilePath); //echo "childStandArr:\n"; //var_dump($childStandArr); $parentStandArr = $this->getJsonArray($parentJsonFilePath); //echo "parentStandArr:\n"; //var_dump($parentStandArr); //合并 子adoc文档的标准数组 和 父adoc文档的标准数组,得到$inheritStandArray $inheritStandArray = $this->getInheritStand($parentStandArr, $childStandArr); //var_dump($inheritStandArray); } return $inheritStandArray; } public function getPhpInheritStandArray($jsonFilePath, $adocFilePath) { //echo "getInheritStandArray\n"; //存储父adoc文档的main部分到/tmp/parentJsonFile.php $adocParser = new AdocParser(); $parentContent = $adocParser->getParentContent($adocFilePath); if($parentContent == "") { $inheritStandArray = $this->getPhpArray($jsonFilePath); } else { $parentJsonFileContent = $adocParser->getJsonBlock(Config::$strPath."/./Parent.php"); $parentJsonFilePath = Config::$strPath."/./tmp/parentFile.php"; file_put_contents($parentJsonFilePath,$parentJsonFileContent); //分别获取 子adoc文档的标准数组 和 父adoc文档的标准数组 $childStandArr = $this->getPhpArray($jsonFilePath); //echo "childStandArr:\n"; //var_dump($childStandArr); $parentStandArr = $this->getPhpArray($parentJsonFilePath); //echo "parentStandArr:\n"; //var_dump($parentStandArr); //合并 子adoc文档的标准数组 和 父adoc文档的标准数组,得到$inheritStandArray $inheritStandArray = $this->getInheritStand($parentStandArr, $childStandArr); //var_dump($inheritStandArray); } return $inheritStandArray; } public function getInheritStand($parentStandArr, $childStandArr) { $inheritResult = $parentStandArr; //遍历$childNewAdocArray,当 foreach ($childStandArr as $childKey => $childValue) { //echo "childKey: $childKey\n"; // $inheritResult[$childKey] = $childValue; //替换已有内容,或添加没有的内容 if(array_key_exists($childKey, $inheritResult)) { //echo "Exists, "; if(is_array($childValue)) { //echo "but is array!\n"; $inheritResult[$childKey] = $this->getInheritStand($inheritResult[$childKey], $childValue); } else { //echo "and is not array!\n"; $inheritResult[$childKey] = $childValue; } } else { //echo "Not exists!\n"; $inheritResult[$childKey] = $childValue; } } //var_dump($inheritResult); return $inheritResult; } public function getJsonArray($jsonFilePath) { //读取adoc文档,获取json串 /*$fileAdoc = dirname(__FILE__)."/./home.text";//home5.text、home.text $path = dirname(__FILE__)."/./jsonData.json"; $fileAdoc = $this->gen_JsonData($fileAdoc); file_put_contents($path,$fileAdoc);*/ //读取json数据,并进行替换写回 $fileStr = file_get_contents($jsonFilePath); $fileStr = $this->reduce_string($fileStr); $tmpPath = dirname($jsonFilePath)."/StanStr.php"; file_put_contents($tmpPath,$fileStr); //逐行trim decode 获得array $jsonStr = $this->trim_string($tmpPath); //$jsonStr = iconv("GBK","UTF-8",$jsonStr); //echo "encoding".mb_detect_encoding($jsonStr)."\n"; //echo "before".$jsonStr; $jsonArr = json_decode($jsonStr,true); // if($jsonArr == null) { // echo "\n stanArr is null!\n"; // } file_put_contents(dirname($jsonFilePath)."/Standard.php",print_r($jsonArr,true)); return $jsonArr; } public function getPhpArray($jsonFilePath) { //echo "getArray\n"; //读取adoc文档,获取json串 /*$fileAdoc = dirname(__FILE__)."/./home.text";//home5.text、home.text $path = dirname(__FILE__)."/./jsonData.json"; $fileAdoc = $this->gen_JsonData($fileAdoc); file_put_contents($path,$fileAdoc);*/ //读取json数据,并进行替换写回 $fileStr = file_get_contents($jsonFilePath); //echo "fileStr: $fileStr\n"; $fileStr = $this->reduce_string($fileStr); //echo "jsonFilePath:$jsonFilePath\n\n"; //jsonFilePath:GAEA_TEMP_PATHdata/File.php //jsonFilePath:GAEA_TEMP_PATHdata//./tmp/parentFile.php //jsonFilePath:GAEA_TEMP_PATHdata/File.php //jsonFilePath:GAEA_TEMP_PATHdata//./tmp/parentFile.php $tmpPath = dirname($jsonFilePath)."/StanStr.php"; file_put_contents($tmpPath,$fileStr); //逐行trim decode 获得array // $jsonStr = $this->trim_string($tmpPath); //$jsonStr = iconv("GBK","UTF-8",$jsonStr); //echo "encoding".mb_detect_encoding($jsonStr)."\n"; //echo "before".$jsonStr; // $jsonArr = json_decode($jsonStr,true); eval($fileStr); //echo "newAdocArray:\n"; //print_r($newAdocArray); // if($jsonArr == null) { // echo "\n stanArr is null!\n"; // } // file_put_contents(dirname($jsonFilePath)."/standard.php",print_r($jsonArr,true)); file_put_contents(dirname($jsonFilePath)."/Standard.php",print_r($newAdocArray,true)); return $newAdocArray; } function gen_JsonData($pathAdoc,$keyStartAdoc="main") { $fOpen = fopen($pathAdoc,"r"); $content = ""; $isJsonStr = false; while(!feof($fOpen)) { $line = fgets($fOpen); //if(preg_match('/^\s*\#\#\#\#'.$keyStartAdoc.'(.+)$/',$line)) if(preg_match('/^\s*\#\#\#\#'.$keyStartAdoc.'$/',$line) || preg_match('/^\s*\#\#\#\#'.$keyStartAdoc.'(.+)$/',$line)) { $isJsonStr = true; continue; } else if(preg_match('/\#\#\#\#/i',$line)) { $isJsonStr = false; } if(!$isJsonStr || (trim($line) == "") || (trim($line) == ":::javascript")) { continue; } if(content != "") { $content = $content.$line; } else { $content = $line; } } fclose($fOpen); return $content; } /* * 匹配正则,删除// 等注释语句(包括范围注释) * @param string $str json string * @return string $str 去除注释后的语句 * @access public */ function reduce_string($str) { $str = preg_replace(array( //m 则^、$针对行的开始和结尾 //s 匹配多行 // eliminate single line comments in '// ...' form '#\s*(?process_json($line); if(content != "") { $content = $content.$split.$line; } else { $content = $line; } } fclose($fOpen); return $content; } /* * 用于json标准化处理 * 1 key值添加双引号 * 2 value值如果是单引号变成双引号 * 输入 $line 一行数据 * 输出 $reStr */ function process_json($line) { //source: {url:'#',name:'奇艺'} //{name:'词条/关系名',semanticName:'义项名', $bInString = false; $strS = ""; $strKey = ""; $reStr = ""; for($i=0;$i'!', '"'=>'"', '#'=>'#', '¥'=>'$', '%'=>'%', '&'=>'&', '''=>"'", '('=>'(', ')'=>')', '*'=>'*', '+'=>'+', ','=>',', '-'=>'-', '.'=>'.', '/'=>'/', '0'=>'0', '1'=>'1', '2'=>'2', '3'=>'3', '4'=>'4', '5'=>'5', '6'=>'6', '7'=>'7', '8'=>'8', '9'=>'9', ':'=>':', ';'=>';', '<'=>'<', '='=>'=', '>'=>'>', '?'=>'?', '@'=>'@', 'A'=>'A', 'B'=>'B', 'C'=>'C', 'D'=>'D', 'E'=>'E', 'F'=>'F', 'G'=>'G', 'H'=>'H', 'I'=>'I', 'J'=>'J', 'K'=>'K', 'L'=>'L', 'M'=>'M', 'N'=>'N', 'O'=>'O', 'P'=>'P', 'Q'=>'Q', 'R'=>'R', 'S'=>'S', 'T'=>'T', 'U'=>'U', 'V'=>'V', 'W'=>'W', 'X'=>'X', 'Y'=>'Y', 'Z'=>'Z', '['=>'[', '\'=>'\\',']'=>']', '^'=>'^', '_'=>'_', '`'=>'`', 'a'=>'a', 'b'=>'b', 'c'=>'c', 'd'=>'d', 'e'=>'e', 'f'=>'f', 'g'=>'g', 'h'=>'h', 'i'=>'i', 'j'=>'j', 'k'=>'k', 'l'=>'l', 'm'=>'m', 'n'=>'n', 'o'=>'o', 'p'=>'p', 'q'=>'q', 'r'=>'r', 's'=>'s', 't'=>'t', 'u'=>'u', 'v'=>'v', 'w'=>'w', 'x'=>'x', 'y'=>'y', 'z'=>'z', '{'=>'{', '|'=>'|', '}'=>'}', '、'=>',', '。'=>'.', '∶'=>':','$'=>'$'); return strtr($str,$arrWordMap); } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genCase.php ================================================ arrCase = $arrC; //范围数组$arrPosNote $this->arrNormal = $standJson; //标准数组$standJson } //注多数组重复构造在此处进行 ,需要传入位置及数组边界值提条件 public function getCase() { $arrReCase = array(); $maxCaseNo = 0;//最大case数 $tmpCaseNo = 0;//接口字段的case数 $randomInfoArray = array(); //记录需要重复的数组的可变元素的信息 //$arrCase[$i][0] 位置 [1]所有可能值[2]类型 for($i=0;$iarrCase);$i++) { $tmpArr = explode(",",$this->arrCase[$i][1]); $tmpCaseNo = count($tmpArr); if($tmpCaseNo > $maxCaseNo) { $maxCaseNo = $tmpCaseNo; } } $randomInfoArray = $this->getRandomInfoArray(); //var_dump($randomInfoArray); //echo "MAX_CASE_NO: $maxCaseNo\n"; if($maxCaseNo > 1) { array_push($arrReCase,$this->arrNormal); //标准数组是 最终数组 中的第一个数组 } for($i=0;$i<$maxCaseNo+1;$i++) //将循环次数上限加1,用来生成最后一个所有数组重复都是中间值的数据 { // //echo "\ni: $i maxCaseNo: $maxCaseNo\n"; $tmpArrNormal = $this->arrNormal; $arrCaseCount = count($this->arrCase); //总共进行$maxCaseNo次大循环,当$arrValueCount<$maxCaseNo的情况,会求余$i%$arrValueCount for($j=0;$j<$arrCaseCount;$j++) { $arrKey = explode(",",$this->arrCase[$j][0]);//拆分后的位置信息 //$arrKey 是 $arrPosNote的一个条目的key $arrValue = explode(",",$this->arrCase[$j][1]); //$arrValue 是 $arrPosNote的一个条目的所有可能value //将可选取值数组中的","变回来 foreach ($arrValue as $keyInArrValue => $valueInArrValue) { // ---add by xumeng change string to int--- if ($this->arrCase[$j][2]=="int") { $arrValue[$keyInArrValue] = intval($valueInArrValue); } else { $arrValue[$keyInArrValue] = $this->changeCommaBack($valueInArrValue); } // ---end--- } //var_dump($arrValue); $arrKeyCount = count($arrKey); $arrValueCount = count($arrValue); $t = $arrKey[$arrKeyCount-1]; // add by xumeng02 if($arrValueCount == 1 && $arrValue[$i%$arrValueCount] == "") { continue; } $assign_cmd = "\$tmpArrNormal"; //下面都是拼装命令的,会在eval()中执行 for ( $c=0;$c<$arrKeyCount;$c++) { $assign_cmd=$assign_cmd."[\"$arrKey[$c]\"]"; } if(substr($assign_cmd,-4)=="[\"\"]") { //去掉类似这种key:$tmpArrNormal["index_show_movie_hot"]["videos"]["0"][""] continue; } //如果是数组 则特殊处理 if(substr($this->arrCase[$j][1],0,4) == "/...") { $change_cmd = "\$tmpArrNormal"; for ( $c=0;$c<$arrKeyCount-1;$c++) { $change_cmd=$change_cmd."[\"$arrKey[$c]\"]"; } //echo $assign_cmd; $start = stripos($this->arrCase[$j][1],"[",0); $end = stripos($this->arrCase[$j][1],"]",0); $strArray = substr($this->arrCase[$j][1],$start+1,$end - $start-1); $arrArray = explode(",",$strArray); //var_dump($arrArray); $arrArrayCount = array(); // 数组的个数策略:lowBorder-1, lowBorder, mid, highBorder, highBorder+1 $numTem = intval($arrArray[0]-1); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[0]); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval(floor( ($arrArray[0]+$arrArray[1])/2 )); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[1]); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[1]+1); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } //var_dump($arrArrayCount); //当$maxCaseNo比数组的变化次数少的话,将$maxCaseNo设为$arrArrayCount if($maxCaseNo < count($arrArrayCount)) { // //echo "\nSET maxCaseNo to NEW NUM!!!\n"; $maxCaseNo = count($arrArrayCount); } //计算数组的重复次数索引 $repeateIdx //如果不是最后一个,即$i<$maxCaseNo,则$repeateIdx = $i%count($arrArrayCount) //如果是最后一个,即$i=$maxCaseNo,则$repeateIdx = count($arrArrayCount)/2 $repeateIdx = $i%count($arrArrayCount); if($i == $maxCaseNo) //count($arrArrayCount)只有4和5两种情况 { if(count($arrArrayCount) == 5) { $repeateIdx = 2; } else if (count($arrArrayCount) == 4) { $repeateIdx = 1; } } //echo "i: $i; repeateIdx: $repeateIdx; $arrArrayCount[$repeateIdx]\n"; // $repeate = $arrArrayCount[$i%count($arrArrayCount)]; // //echo "Repeate: $repeate\n"; // for($k=1;$k<$arrArrayCount[$i%count($arrArrayCount)];$k++) //用$i余count($arrArrayCount),这里$arrArrayCount应该是5吧。如果 $maxCaseNo<5 呢? //------ 改变序列,防止覆盖------ //if ($i==4)var_dump($this->arrCase[$j][0]); $tmpArrNormal = $this->changeIndex($tmpArrNormal,$arrArrayCount[$repeateIdx],$this->arrCase[$j][0]); //------end--------------------- //if ($i==4) var_dump($tmpArrNormal); for($k=1;$k<$arrArrayCount[$repeateIdx];$k++) //用$i余count($arrArrayCount),这里$arrArrayCount应该是5吧。如果 $maxCaseNo<5 呢? { //echo $k."\n"; //Like this: $tmpArrNormal["obj"][0][1][$k]=$tmpArrNormal["obj"][0][1][0]; $tem = $t + $k; $result_cmd=$change_cmd."[\"".$tem."\"]=".$assign_cmd.";"; //echo $result_cmd."\n"; //上面是 让后面重复的数组和第一个数组内容一样。如果数组里的条目有多个选项呢?重复的是数组处理前的赋值,这个每次都不同的 //echo $result_cmd."\n"; eval($result_cmd); //if ($i==4) var_dump($tmpArrNormal); //除数组的第一个重复外,其余的重复里的 可变元素的值 随机改变 if(array_key_exists($this->arrCase[$j][0], $randomInfoArray)) { $randomInfo = $randomInfoArray[$this->arrCase[$j][0]]; $arrayNameLength = strlen($this->arrCase[$j][0]); foreach ($randomInfo as $key => $value) { $inArray_cmd = $change_cmd."[\"".$tem."\"]";//$assign_cmd."[\$k]"; //echo $inArray_cmd."\n"; $tempArrKey = substr($value[0], ($arrayNameLength + 1)); //echo "tempArrKey: $tempArrKey\n"; $arrKey = explode(",",$tempArrKey);//拆分后的位置信息 //unset($arrKey[0]); //var_dump($arrKey); //$arrKey 是 $arrPosNote的一个条目的key $arrValue = explode(",",$value[1]); //-----------xumeng02-------------- if ($value[2]=="int") { foreach ($arrValue as $kk => $vv) { $arrValue[$kk] = intval($vv); } //var_dump($arrValue); } //-----------end------------------- //$arrValue 是 $arrPosNote的一个条目的所有可能value $arrKeyCount = count($arrKey); $arrValueCount = count($arrValue); //准备赋值语句的前半段 foreach ($arrKey as $arrKeyElement) { $inArray_cmd=$inArray_cmd."[\"$arrKeyElement\"]"; } $inArray_cmd .= "="; //准备要赋的值,给出的值是从可选范围中随机选出的 $randonValue = $arrValue[rand(0, $arrValueCount - 1)]; if (!is_int($randonValue)) { $randonValue = $this->changeCommaBack($randonValue); } //var_dump($randonValue); //当要赋的值是异常字符时,使这个值只有1/4的概率是异常字符,让fe看的舒服些 if($randonValue == "Ӓ%璎玥£123丂 亐 儈 凗 狛 癄 鳌 煪 伄 骺牛肩猪肉 ") { //echo "11111\n"; $randomRlt = rand(0, 3); //echo "i: $i, maxCaseNo: $maxCaseNo\n"; if($randomRlt != 3 || $i == $maxCaseNo) //如果是最后一个数组使用原值 { $specialStr_cmd = "\$randonValue = \$arrReCase[0]"; $speicalKey = $value[0]; $arrSpecialKey = explode(",", $speicalKey); foreach ($arrSpecialKey as $arrSpecialKeyElement) { $specialStr_cmd = $specialStr_cmd."[\"$arrSpecialKeyElement\"]"; } $specialStr_cmd .= ";"; //echo $specialStr_cmd."\n"; eval($specialStr_cmd); } $inArray_cmd .= "\$randonValue;"; //遇到异常字符使用了原值时不飘红 } else { $inArray_cmd .= "\$randonValue;"; } //var_dump($tmpArrNormal); eval($inArray_cmd); } } } } else { //如果是最后一个数组,替换掉所有的异常字符 if($arrValue[$i%$arrValueCount] === "Ӓ%璎玥£123丂 亐 儈 凗 狛 癄 鳌 煪 伄 骺牛肩猪肉 " && $i == $maxCaseNo) { //echo "11111\n"; $randonValue1 = ""; //echo "i: $i, maxCaseNo: $maxCaseNo\n"; $specialStr_cmd1 = "\$randonValue1 = \$arrReCase[0]"; $arrSpecialKey1 = $arrKey; foreach ($arrSpecialKey1 as $arrSpecialKeyElement) { $specialStr_cmd1 = $specialStr_cmd1."[\"$arrSpecialKeyElement\"]"; } $specialStr_cmd1 .= ";"; //echo $specialStr_cmd."\n"; eval($specialStr_cmd1); //echo "randonValue1: $randonValue1\n"; $assign_cmd = $assign_cmd."=\$randonValue1;"; } else { //Like this:$tmpArrNormal["obj"][0][1]=$arrValue[$i%$arrValueCount]; $assign_cmd=$assign_cmd."=\$arrValue[$i%$arrValueCount];"; //echo $assign_cmd."\n"; } //echo "result1: $assign_cmd\n"; eval($assign_cmd); } } //if ($i == 4)var_dump($tmpArrNormal); // 自动计数 $dfssubstr="ImageIndexNumber"; $dfsindex=0; $dfsi=0; //var_dump($arrFinalCase[0]); $this->dfs($tmpArrNormal,$dfssubstr,$dfsindex,0,$dfsi); array_push($arrReCase,$tmpArrNormal); } return $arrReCase; } public function getHtmlCase($srcArrReCase) { $arrReCase = array(); $maxCaseNo = 0;//最大case数 $tmpCaseNo = 0;//接口字段的case数 $randomInfoArray = array(); //记录需要重复的数组的可变元素的信息 //$arrCase[$i][0] 位置 [1]所有可能值[2]类型 for($i=0;$iarrCase);$i++) { $tmpArr = explode(",",$this->arrCase[$i][1]); $tmpCaseNo = count($tmpArr); if($tmpCaseNo > $maxCaseNo) { $maxCaseNo = $tmpCaseNo; } } $randomInfoArray = $this->getRandomInfoArray(); //echo "MAX_CASE_NO: $maxCaseNo\n"; if($maxCaseNo > 1) { array_push($arrReCase,$this->arrNormal); //标准数组是 最终数组 中的第一个数组 } for($i=0;$i<$maxCaseNo+1;$i++) //将循环次数上限加1,用来生成最后一个所有数组重复都是中间值的数据 { // //echo "\ni: $i maxCaseNo: $maxCaseNo\n"; $tmpArrNormal = $this->arrNormal; $arrCaseCount = count($this->arrCase); //总共进行$maxCaseNo次大循环,当$arrValueCount<$maxCaseNo的情况,会求余$i%$arrValueCount for($j=0;$j<$arrCaseCount;$j++) { $arrKey = explode(",",$this->arrCase[$j][0]);//拆分后的位置信息 //$arrKey 是 $arrPosNote的一个条目的key $arrValue = explode(",",$this->arrCase[$j][1]); //$arrValue 是 $arrPosNote的一个条目的所有可能value //将可选取值数组中的","变回来 foreach ($arrValue as $keyInArrValue => $valueInArrValue) { // ---add by xumeng change string to int--- if ($this->arrCase[$j][2]=="int") { $arrValue[$keyInArrValue] = intval($valueInArrValue); } else { $arrValue[$keyInArrValue] = $this->changeCommaBack($valueInArrValue); } // ---end--- } //var_dump($arrValue); $arrKeyCount = count($arrKey); $arrValueCount = count($arrValue); $t = $arrKey[$arrKeyCount-1]; // add by xumeng02 if($arrValueCount == 1 && $arrValue[$i%$arrValueCount] == "") { continue; } $assign_cmd = "\$tmpArrNormal"; //下面都是拼装命令的,会在eval()中执行 for ( $c=0;$c<$arrKeyCount;$c++) { $assign_cmd=$assign_cmd."[\"$arrKey[$c]\"]"; } if(substr($assign_cmd,-4)=="[\"\"]") { //去掉类似这种key:$tmpArrNormal["index_show_movie_hot"]["videos"]["0"][""] continue; } //如果是数组 则特殊处理 if(substr($this->arrCase[$j][1],0,4) == "/...") { $change_cmd = "\$tmpArrNormal"; for ( $c=0;$c<$arrKeyCount-1;$c++) { $change_cmd=$change_cmd."[\"$arrKey[$c]\"]"; } //echo $assign_cmd; $start = stripos($this->arrCase[$j][1],"[",0); $end = stripos($this->arrCase[$j][1],"]",0); $strArray = substr($this->arrCase[$j][1],$start+1,$end - $start-1); $arrArray = explode(",",$strArray); //var_dump($arrArray); $arrArrayCount = array(); // 数组的个数策略:lowBorder-1, lowBorder, mid, highBorder, highBorder+1 $numTem = intval($arrArray[0]-1); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[0]); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval(floor( ($arrArray[0]+$arrArray[1])/2 )); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[1]); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } $numTem = intval($arrArray[1]+1); if ($numTem < 1) { array_push($arrArrayCount,1); }else { array_push($arrArrayCount,$numTem); } //var_dump($arrArrayCount); //当$maxCaseNo比数组的变化次数少的话,将$maxCaseNo设为$arrArrayCount if($maxCaseNo < count($arrArrayCount)) { // //echo "\nSET maxCaseNo to NEW NUM!!!\n"; $maxCaseNo = count($arrArrayCount); } //计算数组的重复次数索引 $repeateIdx //如果不是最后一个,即$i<$maxCaseNo,则$repeateIdx = $i%count($arrArrayCount) //如果是最后一个,即$i=$maxCaseNo,则$repeateIdx = count($arrArrayCount)/2 $repeateIdx = $i%count($arrArrayCount); if($i == $maxCaseNo) //count($arrArrayCount)只有4和5两种情况 { if(count($arrArrayCount) == 5) { $repeateIdx = 2; } else if (count($arrArrayCount) == 4) { $repeateIdx = 1; } } //echo "i: $i; repeateIdx: $repeateIdx; $arrArrayCount[$repeateIdx]\n"; // $repeate = $arrArrayCount[$i%count($arrArrayCount)]; // //echo "Repeate: $repeate\n"; // for($k=1;$k<$arrArrayCount[$i%count($arrArrayCount)];$k++) //用$i余count($arrArrayCount),这里$arrArrayCount应该是5吧。如果 $maxCaseNo<5 呢? //------ 改变序列,防止覆盖------ //var_dump($this->arrCase[$j][0]); $tmpArrNormal = $this->changeIndex($tmpArrNormal,$arrArrayCount[$repeateIdx],$this->arrCase[$j][0]); //------end--------------------- for($k=1;$k<$arrArrayCount[$repeateIdx];$k++) //用$i余count($arrArrayCount),这里$arrArrayCount应该是5吧。如果 $maxCaseNo<5 呢? { //Like this: $tmpArrNormal["obj"][0][1][$k]=$tmpArrNormal["obj"][0][1][0]; $tem = $t + $k; $result_cmd=$change_cmd."[\"".$tem."\"]=".$assign_cmd.";"; //上面是 让后面重复的数组和第一个数组内容一样。如果数组里的条目有多个选项呢?重复的是数组处理前的赋值,这个每次都不同的 //echo "result2: $result_cmd\n"; eval($result_cmd); //除数组的第一个重复外,其余的重复里的 可变元素的值 随机改变 if(array_key_exists($this->arrCase[$j][0], $randomInfoArray)) { //var_dump($randomInfoArray); $randomInfo = $randomInfoArray[$this->arrCase[$j][0]]; $arrayNameLength = strlen($this->arrCase[$j][0]); foreach ($randomInfo as $key => $value) { //echo "arrName: ".$this->arrCase[$j][0]."\n"; //echo "randomElement : $value[0]\n"; $inArray_cmd = $change_cmd."[\"".$tem."\"]";//$assign_cmd."[\$k]"; $tempArrKey = substr($value[0], ($arrayNameLength + 1)); //echo "tempArrKey: $tempArrKey\n"; $arrKey = explode(",",$tempArrKey);//拆分后的位置信息 //unset($arrKey[0]); //var_dump($arrKey); //$arrKey 是 $arrPosNote的一个条目的key $arrValue = explode(",",$value[1]); //-----------xumeng02-------------- if ($value[2]=="int") { foreach ($arrValue as $kk => $vv) { $arrValue[$kk] = intval($vv); } //var_dump($arrValue); } //-----------end------------------- //var_dump($arrValue); //$arrValue 是 $arrPosNote的一个条目的所有可能value $arrKeyCount = count($arrKey); $arrValueCount = count($arrValue); //准备赋值语句的前半段 foreach ($arrKey as $arrKeyElement) { $inArray_cmd=$inArray_cmd."[\"$arrKeyElement\"]"; } //echo $inArray_cmd."\n"; $orgArray_cmd = substr($inArray_cmd, 13); $orgArray_cmd = "\$srcArrReCase[\$i+1]".$orgArray_cmd; //拼装原case用对应的值的位置 $inArray_cmd .= "="; //从getCase()产生的数据拿值 $randonValue = ""; $orgArray_cmd = "\$randonValue = ".$orgArray_cmd.";"; //echo "orgArray_cmd: $orgArray_cmd i:$i k: $k\n"; eval($orgArray_cmd); //echo "randonValue: $randonValue\n"; if ($value[2] == "int") { $inArray_cmd .= "\"****\".\$randonValue.\"****\";"; }else { $inArray_cmd .= "\"\".\$randonValue.\"\";"; } //echo $inArray_cmd."\n"; eval($inArray_cmd); } } } } else { //如果是最后一个数组,替换掉所有的异常字符 if($arrValue[$i%$arrValueCount] === "Ӓ%璎玥£123丂 亐 儈 凗 狛 癄 鳌 煪 伄 骺牛肩猪肉 " && $i == $maxCaseNo) { //echo "11111\n"; $randonValue1 = ""; //echo "i: $i, maxCaseNo: $maxCaseNo\n"; $specialStr_cmd1 = "\$randonValue1 = \$arrReCase[0]"; $arrSpecialKey1 = $arrKey; foreach ($arrSpecialKey1 as $arrSpecialKeyElement) { $specialStr_cmd1 = $specialStr_cmd1."[\"$arrSpecialKeyElement\"]"; } $specialStr_cmd1 .= ";"; //echo $specialStr_cmd."\n"; eval($specialStr_cmd1); //echo "randonValue1: $randonValue1\n"; $assign_cmd=$assign_cmd."=\"\".\$randonValue1.\"\";"; } else { //Like this:$tmpArrNormal["obj"][0][1]=$arrValue[$i%$arrValueCount]; if ($this->arrCase[$j][2] == "int") { $assign_cmd=$assign_cmd."=\"****\".\$arrValue[$i%$arrValueCount].\"****\";"; } else { $assign_cmd=$assign_cmd."=\"\".\$arrValue[$i%$arrValueCount].\"\";"; } //var_dump($arrValue[$i%$arrValueCount]); } eval($assign_cmd); //echo "result1: $assign_cmd\n"; } } // 自动计数 $dfssubstr="ImageIndexNumber"; $dfsindex=0; $dfsi=0; //var_dump($arrFinalCase[0]); $this->dfs($tmpArrNormal,$dfssubstr,$dfsindex,0,$dfsi); array_push($arrReCase,$tmpArrNormal); } return $arrReCase; } //填充$randomInfoArray(记录需要重复的数组的可变元素的信息) public function getRandomInfoArray() { //填充$randomInfoArray $randomInfoArray = array(); $arrCaseCount = count($this->arrCase); for($idx=0; $idx<$arrCaseCount; $idx++) { if(substr($this->arrCase[$idx][1],0,4) == "/...") { //echo $this->arrCase[$idx][0]."\n"; //获取范围数组$this->arrCase中需要随机的element $elementToRandom = array(); $objectName = $this->arrCase[$idx][0]; if(!array_key_exists($objectName, $randomInfoArray)) { $nameLength = strlen($objectName); foreach($this->arrCase as $element) { // //echo "currName: $element[0]\n"; $currNameLength = strlen($element[0]); if($currNameLength < $nameLength) { continue; } $tmpNameStr = substr($element[0], 0, ($nameLength)); // //echo "tmpNameStr: $tmpNameStr\n"; if($tmpNameStr == $objectName) { if(substr($element[1],0,4) == "/..." && $objectName == $element[0]) { if(count($elementToRandom) > 0) { $randomInfoArray[$objectName] = $elementToRandom; } break; } //如果发现子数组,把已经记录下来的子数组的可变元素的信息都unset掉 else if(substr($element[1],0,4) == "/..." && $objectName != $element[0]) { //echo "has child array!!!\n"; $childArrayName = $element[0]; //echo "childArrayName: $childArrayName\n"; $childArrayNameLength = strlen($childArrayName); $elementCount = count($elementToRandom); //echo "elementCount: $elementCount\n"; if($elementCount != 0) { foreach ($elementToRandom as $key => $randomElement) { $randElementName = $randomElement[0]; $currRandEleNameLen = strlen($randElementName); //echo "randElementName: $randElementName\n"; if($currRandEleNameLen < $childArrayNameLength) { continue; } $tmpRandEleNameStr = substr($randElementName, 0, ($childArrayNameLength)); //echo "tmpRandEleNameStr: $tmpRandEleNameStr\n"; if($tmpRandEleNameStr == $childArrayName) { //echo "key: $key\n"; unset($elementToRandom[$key]); // array_pop($elementToRandom); //var_dump($elementToRandom); } } } continue; } else if($element[1] != "") { //echo "currName: $element[0]\n"; //echo "push in.\n"; array_push($elementToRandom, $element); } } } } } } //var_dump($randomInfoArray); return $randomInfoArray; } //将字符串中的"||CoMmA_In_StR||"变回"," function changeCommaBack($value) { $trans = array("||CoMmA_In_StR||" => ","); $value = strtr($value, $trans); //file_put_contents(dirname(__FILE__)."/./tmp/note.php", "BACK: $value\n"); return $value; } function dfs(&$arr,$substr,&$index,$flag,&$i) { $n=count($arr); //echo $flag; foreach($arr as $k => &$v) { if(is_array($v) && $i==0) { $this-> dfs($v,$substr,$index,0,$i); } else if(is_array($v)) { $this-> dfs($v,$substr,$index,1,$i); } else if($k==$substr) { $i++; if($flag==0) { //echo "a"; $index=$v; $index=$index+1; } else if($flag==1) { //echo "b"; $v=$index; $index=$index+1; } return; } } } // 在需要重复的数组后面插入模板数据,防止最后的原始数据被覆盖 function changeIndex($tmpArrNormal,$n,$keys) { $Arr = array(); unset($Arr); //var_dump($tmpArrNormal); $tmpArr = explode(",",$keys); $tmpNum = count($tmpArr); $index = intval( $tmpArr[$tmpNum-1] ); //var_dump($index); $count = 0; $parent = array(); $this->searchIndex($count, $Arr, $tmpArrNormal, $tmpArr, $parent, $n); //var_dump($Arr); return $Arr; } function searchIndex(&$count, &$Arr, $ArrNormal, $tmpArr, $parent, $n) { $tmpNum = count($tmpArr); $cmd = "\$Arr"; //下面都是拼装命令的,会在eval()中执行 for ($c = 0; $c < count($parent); $c++) { $cmd = $cmd."[\"".$parent[$c]."\"]"; } if ($count < $tmpNum) { $count++; $sign = 0; if(is_array($ArrNormal)) { foreach ($ArrNormal as $k => $v) { if ($sign == 1) { $kTmp = intval($k) + $n - 1; }else { $kTmp = $k; } $cmdTmp = $cmd."[\"".$kTmp."\"]=\$v;"; //echo $kTmp."\n"; eval($cmdTmp); if ($kTmp == $tmpArr[$count-1]) { array_push($parent, $kTmp); $this->searchIndex($count, $Arr, $v, $tmpArr, $parent, $n); if (count == $tmpNum) { $sign = 1; } } } } $count--; }else { $cmd = "\$Arr"; //下面都是拼装命令的,会在eval()中执行 for ($c = 0; $c < count($parent)-1; $c++) { $cmd = $cmd."[\"".$parent[$c]."\"]"; } for ($j = 1; $j < $n; $j++) { $jmp = $parent[$c]+$j; $emptyArr = array(); $cmdTmp = $cmd."[\"".$jmp."\"]=\$emptyArr;"; eval($cmdTmp); //echo $cmdTmp."\n"; } } return; } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genConf.php ================================================ "Ӓ", "sprintf函数的敏感" => "%", "gbk utf8冲突部分" => "璎玥", "含有欧元符号" => "£123", "边界字符" => "丂 亐 儈 凗 狛 癄 鳌 煪 伄 骺", "相邻字节" => "牛肩猪肉", "空格" => " "); } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genFile.php ================================================ arrFinal = $arr; } public function getFISFile($strUrl) { //echo $strUrl."\n"; $arrTmp = explode("/",$strUrl); $strFileName = ""; $nameArray = array(); foreach($arrTmp as $key) { if(trim($key) != "") { if($strFileName == "") { $strFileName .= $key; } else { $strFileName .= "_".$key; } array_push($nameArray, $key); } } //var_dump($nameArray); $tmpJson = json_encode($this->arrFinal); $strJson = CommonHelper::json_format($tmpJson); if($nameArray[0] == "widget" && array_key_exists(1, $nameArray)) { $strModule = $nameArray[1]; // $fisFileName=Config::$strPath."../$strModule/".$strFileName.".php"; $fisFileName=Config::$strPath.$strFileName.".php"; // CommonHelper::makeDir(Config::$strPath."../$strModule"); } else { $fisFileName=Config::$strPath.$strFileName.".php"; } //echo "fisFileName:" . $fisFileName . "\n"; //echo "strJson:" . $strJson . "\n"; file_put_contents($fisFileName,$strJson); return $fisFileName; } public function getFISHtmlFile($strUrl) { // echo $strUrl."\n"; $arrTmp = explode("/",$strUrl); $strFileName = ""; $nameArray = array(); foreach($arrTmp as $key) { if(trim($key) != "") { if($strFileName == "") { $strFileName .= $key; } else { $strFileName .= "_".$key; } array_push($nameArray, $key); } } $tmpJson = "[\n"; $tmpElementJson = ""; $strElementJson = ""; $arrWholeTmp = array(); $wholeTmpElementJson = ""; //1、utf8编码 //var_dump($this->arrFinal); $this->arrFinal = CommonHelper::array_utf8_encode($this->arrFinal); //var_dump($this->arrFinal); foreach ($this->arrFinal as $k => $arrElement) { $tmpElementJson = json_encode($arrElement); //2、Json编码 //3、utf8解码。得到的字符串中 中文正常 $tmpElementJson = urldecode($tmpElementJson); //var_dump($tmpElementJson); $wholeTmpElementJson .= $tmpElementJson."\n"; // echo "tmpElementJson: ".$tmpElementJson."\n"; $strElementJson = CommonHelper::json_Html_format($tmpElementJson); array_push($arrWholeTmp, $strElementJson); } file_put_contents(Config::$strPath."/tmp/".$strFileName."WTJson.php",$wholeTmpElementJson); $tmpJson = json_encode($arrWholeTmp); //var_dump($tmpJson); $tmpJson = CommonHelper::json_format($tmpJson); //-----解决int显示时带引号的问题-xumeng02---- //var_dump($tmpJson); $patterns = array(); $patterns[0] = '#."\*\*\*\*#m'; $patterns[1] = '#\*\*\*\*."#m'; $replacements = array(); $replacements[0] = ''; $replacements[1] = ''; $tmpJson = preg_replace($patterns, $replacements, $tmpJson); //-----end---------------- // $strJson = CommonHelper::remove_redundant_backslash($strJson); if($nameArray[0] == "widget" && array_key_exists(1, $nameArray)) { $strModule = $nameArray[1]; // $fisFileName=Config::$strPath."../$strModule/".$strFileName."Html.php"; $fisFileName=Config::$strPath.$strFileName."Html.php"; // CommonHelper::makeDir(Config::$strPath."../$strModule"); } else { $fisFileName=Config::$strPath.$strFileName."Html.php"; } //echo "fisHtmlFileName:" . $fisFileName . "\n"; file_put_contents($fisFileName,$tmpJson); //打印测试信息 $arrDecode = json_decode($tmpJson); file_put_contents(Config::$strPath."/tmp/".$strFileName."HtmlTest.php",$arrDecode[0]); //utf8解码 /* //测试array_utf8_encode方法 $arrTest = array('video_num' => 5, 'videos' => array(0 => array(title => '暮光之城', 'imgh_url' => 'http://t1.baidu.com/it/u=3490919773,988116911&fm=20'))); print_r($arrTest); $arrTest = CommonHelper::array_utf8_encode($arrTest); // CommonHelper::array_utf8_encode($arrTest); print_r($arrTest); $strTest = json_encode($arrTest); $strTest = urldecode($strTest); echo mb_convert_encoding("strTest: $strTest\n", "GBK", "UTF-8"); */ return $fisFileName; } public function getFile() { $strReFile = ""; $strReFile .= "//@fileOverview"; $strReFile .= "\n"; for($i=0;$iarrFinal);$i++) { $strJson = json_encode($this->arrFinal[$i]); /*$tmpJson = ""; for($j=0;$jjson_format($strJson); $tmpReFile = $strReFile.$strJson; file_put_contents(Config::$strPath.'/./data/testcase'.$i,$tmpReFile); } } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genInterface.php ================================================ parser = new AdocParser(); Json_Log::init(); // $this->tmpDataPath = dirname(__FILE__)."/tmp/"; $this->tmpDataPath = WWW_ROOT ."/tmp/data/"; CommonHelper::makeDir($this->tmpDataPath); //如果没有通过setPath方法设置存放路径的话,默认的case存放路径 // Config::$strPath = dirname(__FILE__)."/data/"; Config::$strPath = WWW_ROOT ."/test/data/"; if(substr(Config::$strPath,-1)!="/") { Config::$strPath.="/";//保证最后一个字符是"/",以免拼接路径出错 } CommonHelper::makeDir(Config::$strPath); CommonHelper::makeDir(Config::$strPath."/./tmp"); } /* * 输入$strAdoc 输入是adoc文档内容 * 输出 生成指定文件 */ public function genCaseData($adocFileContent,$asyncBasePath="") { Config::$asyncBasePath = $asyncBasePath; //echo Config::$asyncBasePath."\n"; if($adocFileContent==null) return false; $adocFilePath = $this->tmpDataPath.'adocFile.php'; //echo $adocFileContent; //echo $adocFilePath; file_put_contents($adocFilePath,$adocFileContent);//将用户传过来的content保存到本地 $adocType=$this->parser->getAdocTypeBlock($adocFilePath); $adocType=strtolower($adocType); //echo $adocType."\n"; if($adocType=="json") $this->genJsonCaseData($adocFileContent,$asyncBasePath); else if($adocType=="php") $this->genPhpCaseData($adocFileContent,$asyncBasePath); } public function genJsonCaseData($adocFileContent,$asyncBasePath) { if($adocFileContent==null) return false; //echo $this->tmpDataPath."\n"; $adocFilePath = $this->tmpDataPath.'AdocFile.php'; //echo $adocFileContent; //echo $adocFilePath; file_put_contents($adocFilePath,$adocFileContent);//将用户传过来的content保存到本地 $localAdocPathArr=array();//存放本地adoc文件路径的数组 //先生成同步数据case及其飘红数据,和addon数据以及飘红数据 $mainFisFileName = $this->translateJsonAdocToCase($adocFilePath); //echo $mainFisFileName; //保存同步数据和异步数据的关联关系 $caseRelationArray=array(); $caseRelationArray[$mainFisFileName]=array(); //获取异步adoc数据的本地路径数组 $asyncPathArr=$this->parser->getRefPathArr($adocFilePath); //var_dump($asyncPathArr); $asyncLocalAdocPathArr=$this->getAsyncLocalAdocPaths($asyncPathArr,$asyncBasePath); //var_dump($asyncLocalAdocPathArr); //逐个生成异步数据case foreach ( $asyncLocalAdocPathArr as $asynUrl=>$asynAdocPath ) { //echo "dealing:".$asynAdocPath."
"; $asynFisFileName = $this->translateJsonAdocToCase($asynAdocPath); $caseRelationArray[$mainFisFileName][$asynUrl]=$asynFisFileName; } // var_dump($caseRelationArray); if(count($caseRelationArray[$mainFisFileName])>0) { $this->saveAsyncFileNames($caseRelationArray,$mainFisFileName); $this->saveHtmlAsyncFileNames($caseRelationArray,$mainFisFileName); } return true; } public function genPhpCaseData($adocFileContent,$asyncBasePath) { //echo Config::$asyncBasePath."\n"; if($adocFileContent==null) return false; $adocFilePath = $this->tmpDataPath.'AdocFile.php'; //echo $adocFilePath; file_put_contents($adocFilePath,$adocFileContent);//将用户传过来的content保存到本地 //echo $adocFileContent; $localAdocPathArr=array();//存放本地adoc文件路径的数组 //先生成同步数据case $mainFisFileName = $this->translatePhpAdocToCase($adocFilePath); //echo $mainFisFileName; //保存同步数据和异步数据的关联关系 $caseRelationArray=array(); $caseRelationArray[$mainFisFileName]=array(); //获取异步adoc数据的本地路径数组 $asyncPathArr=$this->parser->getRefPathArr($adocFilePath); //var_dump($asyncPathArr); $asyncLocalAdocPathArr=$this->getAsyncLocalAdocPaths($asyncPathArr,$asyncBasePath); //echo "1243124132413245213432\n"; //var_dump($asyncLocalAdocPathArr); //逐个生成异步数据case foreach ( $asyncLocalAdocPathArr as $asynUrl=>$asynAdocPath ) { //echo "dealing:".$asynAdocPath."
"; $asynFisFileName = $this->translatePhpAdocToCase($asynAdocPath); //echo "dealing:".$asynAdocPath."
"; $caseRelationArray[$mainFisFileName][$asynUrl]=$asynFisFileName; } // var_dump($caseRelationArray); if(count($caseRelationArray[$mainFisFileName])>0) { $this->saveAsyncFileNames($caseRelationArray,$mainFisFileName); $this->saveHtmlAsyncFileNames($caseRelationArray,$mainFisFileName); } return true; } //保存同步case所对应的异步数据文件名到文件中,以备后续使用。 //文件命名方式为同步数据文件名_async.php.如user.php对用的异步数据关系文件名为user_async.php。 // private function saveAsyncFileNames($caseRelationArray,$mainFisFileName) { if($caseRelationArray==null or $mainFisFileName==null) { return null; } //echo "main fis file name=".$mainFisFileName."
"; $pathArr=explode("/",$mainFisFileName); $fileName=end($pathArr);//取出最后一个元素 //echo "file name=".$fileName."
"; //拼接处新的文件名 $newFileName=substr($fileName,0,-4)."_async.php"; $caseRelationFile=Config::$strPath.$newFileName; $asyncNameStr = ""; foreach ( $caseRelationArray[$mainFisFileName] as $url=>$name ) { $name = end(explode("/",$name));//单纯出去文件名,不带路径 $asyncNameStr.=$url."=".$name."\n";//home/demo=demo.php的形式 } file_put_contents($caseRelationFile,$asyncNameStr); } //保存Html版本同步case所对应的异步数据文件名到文件中,以备后续使用。 //文件命名方式为同步数据文件名_async.php.如user.php对用的异步数据关系文件名为user_async.php。 // private function saveHtmlAsyncFileNames($caseRelationArray,$mainFisFileName) { if($caseRelationArray==null or $mainFisFileName==null) { return null; } //echo "main fis file name=".$mainFisFileName."
"; $pathArr=explode("/",$mainFisFileName); $fileName=end($pathArr);//取出最后一个元素 //echo "file name=".$fileName."
"; //拼接处新的文件名 $newFileName=substr($fileName,0,-4)."_asyncHtml.php"; $caseRelationFile=Config::$strPath.$newFileName; $asyncNameStr = ""; foreach ( $caseRelationArray[$mainFisFileName] as $url=>$name ) { $name = end(explode("/",$name));//单纯出去文件名,不带路径 $nameArr = explode(".",$name); $name = $nameArr[0]."Html.".$nameArr[1]; $asyncNameStr.=$url."=".$name."\n";//home/demo=demo.php的形式 } file_put_contents($caseRelationFile,$asyncNameStr); //echo "asyncNameStr: $asyncNameStr\n"; } //将所有待转化的adoc文档内容存储到本地,并且返回这些本地adoc文档的文件路径 private function getAsyncLocalAdocPaths($asyncPathArr,$asyncBasePath="") { $localAdocPathArr=array(); //echo "asyncBasePath:$asyncBasePath\n\n\n"; $fileIndex=0; foreach ( $asyncPathArr as $path ) { if(trim($path)=="") continue; $fullAsyncPath=$asyncBasePath.$path.".text"; //echo "before exist check:".$fullAsyncPath."
"; //如果远端异步数据文件不存在,则跳过它,不转化 if($this->url_file_exists($fullAsyncPath)===false) continue; //echo "lalalal\n\n\n"; $tempDocFile=$this->tmpDataPath.$fileIndex.".php";//异步数据本地临时文件名 $tmpContent = file_get_contents($fullAsyncPath);//读取远端数据 file_put_contents($tempDocFile,$tmpContent);//写入本地文件 if(file_exists($tempDocFile)) { //写成功,一个新的本地adoc文档,加入待转化数组 $localAdocPathArr[$path]=$tempDocFile; } $fileIndex+=1; } return $localAdocPathArr; } //将指定路径下的adoc文档转换成case。 private function translatePhpAdocToCase($phpAdocFilePath) { //echo "translatePhpAdocToCase\n"; //先取出adoc中的json数据 $jsonFileContent = $this->parser->getJsonBlock($phpAdocFilePath); //echo $adocFilePath."=====".$jsonFileContent."
"; if (trim($jsonFileContent)=="" or $jsonFileContent==null) { return false; } //将解析出的json内容单独存放到临时文件中 $jsonFilePath = $this->tmpDataPath."File.php"; //echo "jsonFilePath:$jsonFilePath\n"; file_put_contents($jsonFilePath,$jsonFileContent); //解析json中需要变动的字段值 // $arrPosNote=$this->parser->getNotes($jsonFileContent); $arrPhpPosNote=$this->parser->getPhpNotes($phpAdocFilePath); //var_dump($arrPhpPosNote); if($arrPhpPosNote==null or trim($arrPhpPosNote)=="") { return false; } file_put_contents(Config::$strPath."/./tmp/PosNote.php",print_r($arrPhpPosNote,true)); // //echo "arrPosNote: "; // var_dump($arrPosNote); //调用array生成类 genArray 生成php array $genArray = new genArray(); // $standJosn = $genArray->getArray($jsonFilePath);#这是一个得到的标准数组 $standJosn = $genArray->getPhpInheritStandArray($jsonFilePath, $phpAdocFilePath);#这是一个得到的标准数组 //var_dump($standJosn); if($standJosn == null) { // //echo "\n standJosn is null!\n"; } //调用case组织类 genCase 原则是 case数=max(接口可能值) //返回的是一个大的数组 $arr[0] 为case0以此类推 $genCase = new genCase($standJosn,$arrPhpPosNote); $arrFinalCase = $genCase->getCase();//最终所有case均保存在该数组中 //var_dump($arrFinalCase); $arrFinalHtmlCase = $genCase->getHtmlCase($arrFinalCase);//最终所有Html case均保存在该数组中 $arrFinalCase = $this->adjustCaseOrder($arrFinalCase); // $substr="imageIndexNumber"; // $index=0; // $i = 0; // $genCase->dfs($arrFinalCase,$substr,$index,0,$i); file_put_contents(Config::$strPath."/./tmp/ArrFinalCase.php",print_r($arrFinalCase,true)); $arrFinalHtmlCase = $this->adjustCaseOrder($arrFinalHtmlCase); // $i = 0; // $genCase->dfs($arrFinalHtmlCase,$substr,$index,0,$i); file_put_contents(Config::$strPath."/./tmp/ArrFinalHtmlCase.php",print_r($arrFinalHtmlCase,true)); //调用文件生成类 用于FIS、百科、其他产品线文件调用 //该内容可以从配置文件中读,保存adoc中所写的url $fileUrl=$this->parser->getUrlBlock($phpAdocFilePath); //将case信息生成为文件 $genFile = new genFile($arrFinalCase); //$genFile->getFile(); $fisFileName=$genFile->getFISFile($fileUrl); //echo "fisFileName:$fisFileName\n"; $genHtmlFile = new genFile($arrFinalHtmlCase); $tmpArrFinalHtmlCase = $arrFinalHtmlCase; //保存记录$arrFinalHtmlCase $genHtmlFile->getFISHtmlFile($fileUrl); //产生html case的文件/////////////////////////////////////// //add by zs 解析addon数据获取数组 $addonArr = $this->parser->getAddonArr($phpAdocFilePath); //var_dump($addonArr); if($addonArr != null) { foreach($addonArr as $addonPath) { //todo change widget file name if(trim($addonPath) != "") { $addonPath = "/widget".$addonPath; $genFile->getFISFile($addonPath); $genAddFromHtmlFile = new genFile($tmpArrFinalHtmlCase); $genAddFromHtmlFile->getFISHtmlFile($addonPath); } } } return $fisFileName;//暫時返回生成的case文件全路径。 } private function translateJsonAdocToCase($jsonAdocFilePath) { //先取出adoc中的json数据 $jsonFileContent = $this->parser->getJsonBlock($jsonAdocFilePath); //echo $adocFilePath."=====".$jsonFileContent."
"; if (trim($jsonFileContent)=="" or $jsonFileContent==null) { return false; } //将解析出的json内容单独存放到临时文件中 $jsonFilePath = $this->tmpDataPath."File.php"; file_put_contents($jsonFilePath,$jsonFileContent); //解析json中需要变动的字段值 // $arrPosNote=$this->parser->getNotes($jsonFileContent); $arrJsonPosNote=$this->parser->getJsonNotes($jsonAdocFilePath); if($arrJsonPosNote==null or empty($arrJsonPosNote)) { return false; } file_put_contents(Config::$strPath."/./tmp/PosNote.php",print_r($arrJsonPosNote,true)); // //echo "arrPosNote: "; //var_dump($arrJsonPosNote); //调用array生成类 genArray 生成php array $genArray = new genArray(); // $standJosn = $genArray->getArray($jsonFilePath);#这是一个得到的标准数组 $standJosn = $genArray->getjsonInheritStandArray($jsonFilePath, $jsonAdocFilePath);#这是一个得到的标准数组 if($standJosn == null) { // //echo "\n standJosn is null!\n"; } //调用case组织类 genCase 原则是 case数=max(接口可能值) //返回的是一个大的数组 $arr[0] 为case0以此类推 $genCase = new genCase($standJosn,$arrJsonPosNote); $arrFinalCase = $genCase->getCase();//最终所有case均保存在该数组中 //define start $defineContent = $this->parser->getDefineBlock($jsonAdocFilePath); if(!empty($jsonFileContent) && trim($defineContent)!=""){ $defineFilePath = $this->tmpDataPath."define.php"; file_put_contents($defineFilePath,$defineContent); $defineArr = $this->parser->getJsonNewAdocArray($defineContent); if(!function_exists('_define_replace')){ function _define_replace($str, $map, $case){ $tmp = array(); if(preg_match_all("/\[(.+?)\]/", $str, $tmp)){ $params = $tmp[1]; foreach($params as $v){ // search case vars $case_params = array(); if(preg_match_all("/#(.+?)#/", $v, $case_params)){ $case_tmp = $case; foreach($case_params[1] as $vv){ if(isset($case_tmp[$vv])) $case_tmp = $case_tmp[$vv]; else return '$' . $str . '$'; } $v = $case_tmp; } // search case vars end if(isset($map[$v])) $map = $map[$v]; else return '$' . $str . '$'; } return $map; } return '$' . $str . '$'; } } if(!function_exists('_define_replace_deep')){ function _define_replace_deep(&$arr, &$map, &$case){ foreach($arr as &$v){ if(is_string($v)){ $ret = array(); if(preg_match_all('/\$([^$]*?)\$/', $v, $ret)){ foreach($ret[1] as $k => $var){ $ttt = _define_replace($var, $map, $case); if(is_string($ttt) || is_numeric($ttt)) $v = str_replace($ret[0][$k], $ttt, $v); else $v = $ttt; } } }elseif(is_array($v)){ _define_replace_deep($v, $map, $case); } } } } } //define end //var_dump($arrFinalCase); $arrFinalHtmlCase = $genCase->getHtmlCase($arrFinalCase);//最终所有Html case均保存在该数组中 if(!empty($jsonFileContent) && trim($defineContent)!=""){ foreach($arrFinalCase as &$v){ _define_replace_deep($v, $defineArr, $v); } foreach($arrFinalHtmlCase as $k=>&$v){ _define_replace_deep($v, $defineArr, $arrFinalCase[$k]); } } //var_dump($arrFinalCase); //var_dump($arrFinalHtmlCase); //exit(); $arrFinalCase = $this->adjustCaseOrder($arrFinalCase); //var_dump($arrFinalCase); // $substr="imageIndexNumber"; // $index=0; // $i=0; // //var_dump($arrFinalCase[0]); // $genCase->dfs($arrFinalCase,$substr,$index,0,$i); file_put_contents(Config::$strPath."/./tmp/ArrFinalCase.php",print_r($arrFinalCase,true)); // $i=0; $arrFinalHtmlCase = $this->adjustCaseOrder($arrFinalHtmlCase); // $genCase->dfs($arrFinalHtmlCase,$substr,$index,0,$i); //var_dump($arrFinalHtmlCase); file_put_contents(Config::$strPath."/./tmp/ArrFinalHtmlCase.php",print_r($arrFinalHtmlCase,true)); //调用文件生成类 用于FIS、百科、其他产品线文件调用 //该内容可以从配置文件中读,保存adoc中所写的url $fileUrl=$this->parser->getUrlBlock($jsonAdocFilePath); //将case信息生成为文件 $genFile = new genFile($arrFinalCase); //$genFile->getFile(); $fisFileName=$genFile->getFISFile($fileUrl); $genHtmlFile = new genFile($arrFinalHtmlCase); $tmpArrFinalHtmlCase = $arrFinalHtmlCase; //保存记录$arrFinalHtmlCase $genHtmlFile->getFISHtmlFile($fileUrl); //产生html case的文件/////////////////////////////////////// //add by zs 解析addon数据获取数组 $addonArr = $this->parser->getAddonArr($jsonAdocFilePath); //var_dump($addonArr); if($addonArr != null) { foreach($addonArr as $addonPath) { //todo change widget file name if(trim($addonPath) != "") { $addonPath = "/widget".$addonPath; $genFile->getFISFile($addonPath); $genAddFromHtmlFile = new genFile($tmpArrFinalHtmlCase); $genAddFromHtmlFile->getFISHtmlFile($addonPath); } } } return $fisFileName;//暫時返回生成的case文件全路径。 } //判断一个url格式的文件是否存在,如http://fe.baidu.com/data/demo/list.text private function url_file_exists($url) { $url = trim($url); if(substr($url,0,7)=="http://") { $http_headers = @get_headers($url,1); $exist_stat = "200"; $string_pieces = explode(" ",$http_headers[0]); for ($i = 0; $i < count($string_pieces);$i++) { if (strcmp($exist_stat,$string_pieces[$i]) == 0) { return true; } } return false; } else { $contents=file_get_contents($url); //echo "contents:$contents\n\n\n"; if($contents=="") return false; else return true; } } /* * 輸入url * 輸出返回 文件內容 */ public function getCaseData($url = "") { if($url==null or $url == "") { return array(); } $localFilePath=$this->getLocalFilePath($url); //echo "localFilePath: $localFilePath\n"; $resultArray=array();//返回值数组 //读取同步case中的数据并放入返回值数组中。 //echo Config::$strPath."\n"; $mainFileName = Config::$strPath.$localFilePath.".php"; //echo $mainFileName."\n"; $mainCaseContent = file_get_contents($mainFileName); //将异步数据文件名放入返回值数组中。 $resultArray[]=$mainCaseContent; $resultArray[]=$this->getAsyncFileArray($localFilePath); //var_dump($resultArray); return $resultArray; } /* * 輸入url * 輸出返回 Html文件內容 */ public function getHtmlCaseData($url = "") { if($url==null or $url == "") { return array(); } $localFilePath=$this->getLocalFilePath($url); //echo "localFilePath:$localFilePath\n"; //读取同步case中的数据并放入返回值数组中。 $mainFileName = Config::$strPath.$localFilePath."Html.php"; //echo $mainFileName."\n"; $htmlCaseContent = file_get_contents($mainFileName); //echo $htmlCaseContent; //将异步数据文件名放入返回值数组中。 $resultArray[] = $htmlCaseContent; $resultArray[] = $this->getHtmlAsyncFileArray($localFilePath); // var_dump($resultArray); return $resultArray; } //将用户输入的url转换成本地文件名,如用户输入的是/user/tim,那么本地存储tim数据的文件名为./data/user_tim.php. private function getLocalFilePath($url) { $arrTmp = explode("/",$url); $path = ""; foreach ($arrTmp as $key) { if(trim($key) == "") { continue; } if($path != "") { $path .= "_".$key; } else { $path .= $key; } } return $path; } //获取同步数据所对应的异步文件的文件路径 private function getAsyncFileArray($dataPath) { $asyncFileName=Config::$strPath.$dataPath."_async.php"; // //echo "11222222222222222222223333333333333\n"; // //echo "asyncFileName: $asyncFileName\n"; $asyncFilesArr=array(); if(file_exists($asyncFileName)) { $asyncFile = fopen($asyncFileName, "r"); while(!feof($asyncFile)) { $line = fgets($asyncFile); if($line!=null and trim($line)!="") { $url_path_array=explode("=",$line); $url = $url_path_array[0]; $path = $url_path_array[1]; $asyncFilesArr[$url]=Config::$strPath.$path; } } fclose($asyncFile); } // var_dump($asyncFilesArr); return $asyncFilesArr; } //获取Html版本同步数据所对应的异步文件的文件路径 private function getHtmlAsyncFileArray($dataPath) { $asyncFileName=Config::$strPath.$dataPath."_asyncHtml.php"; // //echo "11222222222222222222223333333333333\n"; // //echo "asyncFileName: $asyncFileName\n"; $asyncFilesArr=array(); if(file_exists($asyncFileName)) { $asyncFile = fopen($asyncFileName, "r"); while(!feof($asyncFile)) { $line = fgets($asyncFile); if($line!=null and trim($line)!="") { $url_path_array=explode("=",$line); $url = $url_path_array[0]; $path = $url_path_array[1]; $asyncFilesArr[$url]=Config::$strPath.$path; } } fclose($asyncFile); } // var_dump($asyncFilesArr); return $asyncFilesArr; } /* * 设置数据文件的存放位置 * 默认存放在jsonPro/data下 */ public function setPath($caseFilePath = "") { if($caseFilePath == "") return ;//直接返回,使用构造函数中默认的path。 if(substr($caseFilePath,-1)!="/") { $caseFilePath.="/";//保证最后一个字符是"/",以免拼接路径出错 } Config::$strPath = $caseFilePath; //创建case路径,确保case有地方存放 CommonHelper::makeDir(Config::$strPath); return true; } //调整case的顺序,把case数组中的最后一个case(优化case)提到第一个case //其余case依次后移 //输入是要调整次序的case数组 //返回是调整后的数组 public function adjustCaseOrder($arrCase) { $arrNewCase = array(); $lastElement = end($arrCase); $arrNewCase[] = $lastElement; for ($i = 0; $i < count($arrCase)-1; $i++) { $arrNewCase[] = $arrCase[$i]; } return $arrNewCase; } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genLog.php ================================================ 'FATAL', self::LOG_LEVEL_WARNING => 'WARNING', self::LOG_LEVEL_NOTICE => 'NOTICE', self::LOG_LEVEL_TRACE => 'TRACE', self::LOG_LEVEL_DEBUG => 'DEBUG', ); protected static $strLogFile = ""; private static $instance = null; private function __construct($logFile) { self::$strLogFile = $logFile; } public static function init($logFile="") { if(self::$instance == null) { if($logFile == "") { $logFile = dirname(__FILE__)."/./log/jsonPro.log"; self::$instance = new Json_Log($logFile); } else { self::$instance = new Json_Log($logFile); } } } public static function debug($str,$file="",$line="") { return self::$instance->writeLog(self::LOG_LEVEL_DEBUG, $str,$file,$line); } public static function trace($str,$file="",$line="") { return self::$instance->writeLog(self::LOG_LEVEL_TRACE, $str,$file,$line); } public static function notice($str,$file="",$line="") { return self::$instance->writeLog(self::LOG_LEVEL_NOTICE, $str,$file,$line); } public static function warning($str,$file="",$line="") { return self::$instance->writeLog(self::LOG_LEVEL_WARNING, $str,$file,$line); } public static function fatal($str,$file="",$line="") { return self::$instance->writeLog(self::LOG_LEVEL_FATAL, $str,$file,$line); } public static function writeLog($intLevel,$str,$file,$line) { $strLevel = self::$arrLogLevels[$intLevel]; $strLogFile = self::$strLogFile; if(self::LOG_LEVEL_WARNING || self::LOG_LEVEL_FATAL) { $strLogFile .= '.wf'; } $str = sprintf("%s:%s print_word[%s],file[%s],line[%s] \n", $strLevel, date('m-d H:i:s:',time()), $str, $file, $line); return file_put_contents($strLogFile,$str,FILE_APPEND); } } ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/JsonPro/genValue.php ================================================ note = $nt; $this->type = $ty; //echo "NOTE: " . mb_convert_encoding($nt, "GBK", "UTF-8") . "\n"; //echo "TYPE: ".$ty."\n"; } //目前只针对int string 类型的注释 public function getValue($value) { $reStr = "";//返回结果 $tmpValue = "";//临时的value值 //echo $this->note; //$arrNote = explode('/',$this->note); $strNote = trim($this->note); $strNote = substr($strNote, 2); //echo "strNote: $strNote\n"; $arrNote = array(); $arrNote[] = $strNote; //var_dump($arrNote); $arrNoteCount=count($arrNote); for($i=0;$i<$arrNoteCount;$i++) { if(substr($arrNote[$i],0,1) == "<")//枚举 { $arrNote[$i] = trim($arrNote[$i]);//过滤]之后的空格 $strEnum = substr($arrNote[$i],1,strlen($arrNote[$i])-2); $arrEnum = explode(",",$strEnum); //var_dump($arrEnum); //去掉空格 foreach ($arrEnum as $arrEnumKey => $arrEnumValue) { $arrEnum[$arrEnumKey] = trim($arrEnumValue); } $arrEnumCount=count($arrEnum); for($j=0;$j<$arrEnumCount;$j++)//遍历所有枚举内容 { //获取单个枚举内容 if($arrEnum[$j]{0} == "'" || $arrEnum[$j]{0} == '"') { $tmpValue = substr($arrEnum[$j],1,strlen($arrEnum[$j])-2); } else { $tmpValue = $arrEnum[$j]; } //压入返回字符串中 if($reStr == "") { $reStr .= $tmpValue; //echo $reStr."\n"; } else { $reStr .= ",".$tmpValue; //echo $reStr."\n"; } } } else if(substr($arrNote[$i],0,1) == "[")//范围 { $arrNote[$i] = trim($arrNote[$i]);//过滤]之后的空格 $strBord = substr($arrNote[$i],1,strlen($arrNote[$i])-2); $arrBord = explode(",",$strBord); //去掉空格 foreach ($arrBord as $arrBordKey => $arrBordValue) { $arrBord[$arrBordKey] = trim($arrBordValue); } if($this->type == "string") { //echo $reStr."\n"; $this->putString($arrBord,$reStr,$value); } else if($this->type == "int") { $this->putInt($arrBord,$reStr); } } else if(substr($arrNote[$i],0,11) == "(messycode)")//乱码测试 { $mess_string=$this->getMessStr(); //在这里添加乱码测试使用的内容。 if($reStr == "") { $reStr .= $mess_string; //echo $reStr; } else { $reStr .= ",".$mess_string; //echo $reStr; } } else if(substr($arrNote[$i],0,1) == "(")//范围 { $arrNote[$i] = trim($arrNote[$i]);//过滤]之后的空格 $strBord = substr($arrNote[$i],1,strlen($arrNote[$i])-2); $strnum = intval($strBord); $reStr = ""; foreach(range(1,4) as $v){ $tmp = ','; for($i = 1; $i <=$strnum;$i++){ do{ $n = mt_rand(35, 126); }while(92 == $n || 60 == $n || 62 == $n || 44 == $n); $tmp .= chr($n); } $reStr .= $tmp; } $reStr = substr($reStr, 1); } } //用于value值去重 $arrTerminal = explode(",",$reStr); $reStr = ""; for($i=0;$igetString($arrBord[0]-1,$value); } else { $reStr .= ",".$this->getString($arrBord[0]-1,$value); } $reStr .= ",".$this->getString($arrBord[0],$value); $reStr .= ",".$this->getString(($arrBord[0]+$arrBord[1])/2,$value); $reStr .= ",".$this->getString($arrBord[1],$value); $reStr .= ",".$this->getString($arrBord[1]+1,$value); } } public function putInt($arrBord,&$reStr) { //echo "before $arrBord[0] $arrBord[1] \n"; if(is_numeric($arrBord[0]) && is_numeric($arrBord[1])) { //echo "aaaaaaaaaa $arrBord[0] $arrBord[1] \n"; if($reStr == "") { $reStr .= $this->getNum($arrBord[0]-1); } else { $reStr .= ",".$this->getNum($arrBord[0]-1); } $reStr .= ",".$this->getNum($arrBord[0]); $reStr .= ",".$this->getNum(($arrBord[0]+$arrBord[1])/2); $reStr .= ",".$this->getNum($arrBord[1]); $reStr .= ",".$this->getNum($arrBord[1]+1); } } public function getString($strLength,$value) { $tmpStr = ""; $strLength = intval(floor($strLength)); if ($strLength <= 0) { $strLength = 1; } for($i=0;$i<$strLength;$i++) { $tmpStr .= $value; } return $tmpStr; } public function getNum($numLength) { $tmpStr = 0; /*for($i=0;$i<$numLength;$i++) { $tmpStr = $tmpStr * 10 + 1; }*/ $tmpStr = floor($numLength); $tmpStr = intval($tmpStr); return $tmpStr; } } ?> ================================================ FILE: packages/chameleon-templates/server/fisdata/libs/Util.class.php ================================================ read())) { if ($entry == '.' || $entry == '..' || ($entry{0} == '.' && is_dir($path . $entry))) { continue; } $entry = $path . $entry; if (is_dir($entry)) { if ($include_dir && self::filter($entry, $include, $exclude)) { $files[] = $entry; } if ($recursion) { self::find($entry, $include, $exclude, true, $include_dir, $files); } } else { if (!self::filter($entry, $include, $exclude)) { continue; } $files[] = $entry; } } $dir->close(); return $files; } else if (is_file($path) && self::filter($path, $include, $exclude)) { $files[] = $path; } return $files; } } ================================================ FILE: packages/chameleon-templates/server/fisdata/package.json ================================================ { "dependencies" : { "smarty" : "*" } } ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/ChameleonData.class.php ================================================ getData($jsonFile); return $content[projectName]; } //根据url,从配置的router.json中获取模拟php文件的数据 public function getMockContent() { $jsonFile = WWW_ROOT . "json/router.config.json"; $content = $this->getData($jsonFile); $path = $_SERVER['REQUEST_URI']; if (($pos = strpos($path, '?')) !== false) { $path = substr($path, 0, $pos); } $mockFileName; if($content[mode] === "history") { foreach ($content[routes] as $item) { if($item[url] === $path) { $mockFileName = $item[mock]; } } } else if ($content[mode] === "hash") { $mockFileName = $this->getProjectName() . ".php"; } if($mockFileName) { $mockPath = WWW_ROOT . 'test/' . $mockFileName; if (is_file($mockPath)) { require($mockPath); $ret = $chameleon; return $ret; } } } } ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/FISADOCData.class.php ================================================ datatype = 'adoc'; $this->adoc_data_path = WWW_ROOT . '/test/data/'; } protected function getId($tmpl) { $uri = $_SERVER['REQUEST_URI']; if (($p = strpos($uri, '?')) !== false) { $uri = substr($uri, 0, $p); } $uris = explode('/', $uri); if (isset($uris[1]) && $uris[1] !== '') { unset($uris[0]); $id = implode('_', $uris); } return $id; } protected function getFile($tmpl) { $root = Util::normalizePath(WWW_ROOT . 'template'); $id = str_replace($root, '', Util::normalizePath($tmpl)); return Util::normalizePath(WWW_ROOT . '/test/' . preg_replace('/\.[a-z]{2,6}$/i', '.text', $id)); } private function parseAdocFile($filepath) { $content = file_get_contents($filepath); require_once(LIBS_ROOT . 'JsonPro' . DIRECTORY_SEPARATOR . 'genInterface.php'); $genHandle = new genInterface(); $genHandle->setPath($this->adoc_data_path); $genHandle->genCaseData($content); } public function getData($tmpl) { $id = $this->getId(); if (!$id) { $this->parseAdocFile($this->getFile($tmpl)); } $data_path = Util::normalizePath($this->adoc_data_path . $id . '.php'); if (!is_file($data_path)) { return array(); } ob_start(); require_once($data_path); $data = ob_get_clean(); $data = json_decode($data, true); $render_data = array(); if ($cookie_id = $this->getCookieId()) { $render_data = $data[$cookie_id]; } else { $render_data = current($data); } return $render_data; } public function fetchRemoteData($tmpl_id) { } public function getDataList($tmpl) { $id = $this->getId(); if (!$id) { $this->parseAdocFile($this->getFile($tmpl)); } $data_path = Util::normalizePath($this->adoc_data_path . $id . 'HTML.php'); if (!is_file($data_path)) { return array(); } ob_start(); require_once($data_path); $data = ob_get_clean(); $data = json_decode($data, true); return $data; } public function save($post) { $file = $post['path']; $dir = dirname($file); if (!is_dir($dir) && !mkdir($dir, 0755, true)) { echo '{"message": "填写的路径无法创建,请重新填写!", "code": 1}'; exit(1); } if (!is_file($file) && false === file_put_contents($file, '')) { echo '{"message": "填写的路径无法创建,请重新填写!", "code": 1}'; exit(1); } $data = $post['data']; file_put_contents($file, $data); $this->parseAdocFile($file); echo '{"message": "保存成功", "code": 0}'; } } ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/FISJSONData.class.php ================================================ datatype = 'json'; } public function getData($tmpl) { $file = $this->getFile($tmpl); $ret = array(); if (is_file($file)) { $ret = json_decode(file_get_contents($file), true); if ($ret === null) { $ret = array(); } } return $ret; } } ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/FISPHPData.class.php ================================================ datatype = 'php'; } public function getData($tmpl) { $file = $this->getFile($tmpl); $ret = array(); if (is_file($file)) { require($file); $ret = $chameleon; } return $ret; } } ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/gen_plugins.php.sh ================================================ #!/usr/bin/env sh d=`pwd` cd $d && ls -1 | grep 'class' | awk 'BEGIN{content=" "plugins.php" }' ================================================ FILE: packages/chameleon-templates/server/fisdata/plugin/plugins.php ================================================ assignData as $test_data) { extract($test_data); } ob_start(); echo eval('?>'.preg_replace('/;*\s*\?>/', '; ?>', str_replace('assignData[] = $this->object_to_array($data); } function object_to_array($object) { return is_object($object) ? get_object_vars($object) : $object; } } TestData::init(); /** * 先匹配server-conf/rewrite.conf里面匹配 * 例如rewrite.conf里面写了: * template ^\/hao123$ index/page/hao123_home.tpl * 则因为使用了 * Rewrite::addRewriteRule('template', 'fis_debug_template_rewrite_rule'); * 用户访问/hao123则命中fis_debug_template_rewrite_rule函数处理 * * 固定如果是/static开头的则读取static目录的静态文件返回 * * 其他情况则用 fis_debug_template_rewrite_rule 取自动匹配 * * * 失败则返回错误 */ $path = $_SERVER['REQUEST_URI']; function fis_debug_render_smarty($tpl = null, $data = array()) { $root = dirname(__FILE__) . DIRECTORY_SEPARATOR; require_once $root . 'smarty/Smarty.class.php'; $smarty = new Smarty(); $default_conf = array( 'template_dir' => 'template', 'config_dir' => 'config', 'plugins_dir' => array('plugin'), 'left_delimiter' => '{%', 'right_delimiter' => '%}', ); if (file_exists($root . 'smarty.conf')) { $user_conf = parse_ini_file($root . 'smarty.conf'); if (!empty($user_conf)) { $default_conf = array_merge($default_conf, $user_conf); } } $smarty->setTemplateDir($root . $default_conf['template_dir']); $smarty->setConfigDir($root . $default_conf['config_dir']); foreach ($default_conf['plugins_dir'] as $dir) { $smarty->addPluginsDir($root . $dir); } $smarty->setLeftDelimiter($default_conf['left_delimiter']); $smarty->setRightDelimiter($default_conf['right_delimiter']); TestData::renderHelper($smarty, $tpl); // $smarty->assign($data); // $smarty->display($tpl); } function fis_debug_template_rewrite_rule($rewrite, $url, $root, $matches) { if (file_exists($root . 'template/' . $rewrite)) { header('Content-Type: text/html'); fis_debug_render_smarty($rewrite); } else { Rewrite::header(404); } } function fis_debug_render_php_older($tpl = null, $data = array()){ $root = dirname(__FILE__) ;//. DIRECTORY_SEPARATOR; $path = str_replace($root, '', $tpl); if (!$tpl) { $path = $_SERVER['REQUEST_URI']; $split = explode('/', $path); $last = array_pop($split); $len = count($split); if (($pos = strpos($path, '?')) !== false) { $path = substr($path, 0, $pos); } if ($path[strlen($path) - 1] === '/') { $path = substr($path, 0, -1); } if (1 === $len) { $path .= '/index.html'; } else { $path .= '.html'; } $tpl = $root . $path; } if(file_exists($root . 'test' . $testDataFile . 'php')){ require($root . 'test' . $testDataFile . 'php'); } if( file_exists($tpl) ){ require($tpl); }else{ $pathbefore = explode('.html',$tpl ); require( $pathbefore[0].'.php' ); } } function fis_debug_render_php($tpl = null, $data = array()) { $root = dirname(__FILE__) . DIRECTORY_SEPARATOR; $path = str_replace($root, '', $tpl); if (!$tpl) { $path = $_SERVER['REQUEST_URI']; $split = explode('/', $path); $last = array_pop($split); $len = count($split); if (($pos = strpos($path, '?')) !== false) { $path = substr($path, 0, $pos); } if ($path[strlen($path) - 1] === '/') { $path = substr($path, 0, -1); } if (1 === $len) { $path .= '/index.html'; } else { $path .= '.html'; } $tpl = $root . 'page' . $path; } $template_instance = new CITpl(); if(file_exists($tpl)){ TestData::renderHelper($template_instance , $tpl); }else{ die('Not found page '.$tpl); } } // Rewrite::addRewriteRule('php', 'fis_debug_php_rewrite_rule'); // Rewrite::addRewriteRule('template', 'fis_debug_template_rewrite_rule'); // if (!Rewrite::match($path)) { $projectName = TestData::$_chameleon_data->getProjectName(); $tpl = $root . 'template/' . $projectName . '.tpl'; fis_debug_render_smarty($tpl); // } ================================================ FILE: packages/chameleon-templates/server/rewrite/README.md ================================================ 请求转发模块说明 : ## Install ```bash $ fis server install rewrite ``` ## Usage 1. 对外提供match方法,供其他调试模块调用,具体方法参考代码注释说明。 1. 默认读取根目录server.conf文件,书写方式是: * rewrite和redirect开头的会被翻译成一条匹配规则,自上而下的匹配。所有非rewrite和redirect开头的会被当做注释处理。 * rewrite : 匹配规则后转发到一个文件 * redirect : 匹配规则后重定向到另一个url ```conf rewrite ^\/news\?.*tn\=[a-zA-Z0-9]+.* app/data/news.php redirect ^\/index\?.* /photo/index/a rewrite ^\/(.*)\?.* app/data/$1.php ``` ================================================ FILE: packages/chameleon-templates/server/rewrite/Rewrite.php ================================================ 'image/bmp', 'css' => 'text/css', 'doc' => 'application/msword', 'dtd' => 'text/xml', 'gif' => 'image/gif', 'hta' => 'application/hta', 'htc' => 'text/x-component', 'htm' => 'text/html', 'html' => 'text/html', 'xhtml' => 'text/html', 'ico' => 'image/x-icon', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'text/javascript', 'json' => 'application/json', 'mocha' => 'text/javascript', 'mp3' => 'audio/mp3', 'mp4' => 'video/mpeg4', 'mpeg' => 'video/mpg', 'mpg' => 'video/mpg', 'manifest' => 'text/cache-manifest', 'pdf' => 'application/pdf', 'png' => 'image/png', 'ppt' => 'application/vnd.ms-powerpoint', 'rmvb' => 'application/vnd.rn-realmedia-vbr', 'rm' => 'application/vnd.rn-realmedia', 'rtf' => 'application/msword', 'svg' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'txt' => 'text/plain', 'vml' => 'text/xml', 'vxml' => 'text/xml', 'wav' => 'audio/wav', 'wma' => 'audio/x-ms-wma', 'wmv' => 'video/x-ms-wmv', 'woff' => 'image/woff', 'xml' => 'text/xml', 'xls' => 'application/vnd.ms-excel', 'xq' => 'text/xml', 'xql' => 'text/xml', 'xquery' => 'text/xml', 'xsd' => 'text/xml', 'xsl' => 'text/xml', 'xslt' => 'text/xml' ); /** * HTTP状态码表 * @var array */ public static $statusMap = array( 200 => 'OK', 304 => 'Not Modified', 404 => 'File Not Found', 403 => 'Forbidden', 500 => 'Internal Server Error' ); /** * 设置HTTP请求头信息 * @param int $code HTTP状态码 * @param null|string $status 状态信息 */ public static function header($code = 200, $status = null) { if ($status === null && isset(self::$statusMap[$code])) { $status = self::$statusMap[$code]; } if (php_sapi_name() == 'cgi') { header("Status: $code $status"); } else { header("HTTP/1.1 $code $status"); } } /** * 添加用户自定义的url处理规则 * @param $type 规则名称 * @param callable $callback 用户处理callback函数,callback参数为匹配的$matches数组 */ public static function addRewriteRule($type, $callback){ if(is_callable($callback)){ $type = strtolower($type); self::$userRules[$type] = $callback; } } public static function setRoot($root){ self::$root = $root; } public static function getRoot(){ return isset(self::$root) ? self::$root : dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR; } private static function padString($path, $matches) { for ($i = 1, $len = count($matches); $i < $len; $i++) { $path = preg_replace('/\\$' . $i . '\\b/', $matches[$i], $path); $path = preg_replace('/\\\\' . $i . '\\b/', $matches[$i], $path); } return $path; } /** * $url : 需要匹配的url * $matches : 正则匹配的引用 * 返回值 : * true : 表示命中正则 * false : 表示没有命中 */ public static function match($url, &$matches = null){ $root = self::getRoot(); //命中server.conf文件中定义的rewrite,redirect规则 // $configFile = $root . 'server.conf'; $configFiles = self::getConfigFiles($root . 'server-conf/'); for ($i=0; $i < count($configFiles); $i++) { $configFile = $configFiles[$i]; if(file_exists($configFile) && ($handle = fopen($configFile, 'r'))){ while (($buffer = fgets($handle)) !== false) { $ruleTokens = preg_split('/\s+/', trim($buffer)); $type = strtolower($ruleTokens[0]); if(preg_match('/^\w+$/', $type)){ $rule = array( 'rule' => $ruleTokens[1], 'rewrite' => $ruleTokens[2], 'type' => $type ); $ret = self::_match($rule, $root, $url, $matches); if($ret) { fclose($handle); return $ret; } } } if (!feof($handle)) { echo "Error: unexpected fgets() fail\n"; } fclose($handle); } } return false; } private static function _match($rule, $root, $url, &$matches = null){ if(preg_match('/' . $rule['rule'] . '/', $url, $matches)){ $rewrite = self::padString($rule['rewrite'], $matches); $type = $rule['type']; if(isset(self::$userRules[$type])){ $ret = call_user_func(self::$userRules[$type], $rewrite, $url, $root, $matches); if($ret === false){ return false; } else { return true; } } if($type == 'rewrite'){ if(file_exists($file = $root . $rewrite)){ $pos = strrpos($rewrite, '.'); if(false !== $pos){ $ext = substr($rewrite, $pos + 1); if($ext == 'php'){ self::includePhp($root . $rewrite, $matches); } else { self::header(200); if(isset(self::$MIME[$ext])){ $content_type = 'Content-Type: ' . self::$MIME[$ext]; } else { $content_type = 'Content-Type: application/x-' . $ext; } header($content_type); echo file_get_contents($root . $rewrite); } } else { echo file_get_contents($root . $rewrite); } } else { self::header(404); } } else if($type == 'redirect'){ header('Location: ' . $rewrite); exit(); } return true; } return false; } private static function includePhp($file, $matches){ $fis_matches = $matches; include($file); } private static function getConfigFiles($path){ if(is_dir($path)){ $files = array(); $dir = dir($path); $containCommon = false; while (($file = $dir->read()) !== false) { if ($file == '.' || $file == '..' || is_dir($path . $file)) { continue; } if($file == "common.conf"){ $containCommon = true; } array_push($files, $path . $file); } if ($containCommon) { array_unshift($files, $path . "common.conf"); } return $files; } return array(); } } ================================================ FILE: packages/chameleon-templates/server/smarty/Smarty.class.php ================================================ * @author Uwe Tews * @author Rodney Rehm * @package Smarty * @version 3.1.13 */ /** * define shorthand directory separator constant */ if (!defined('DS')) { define('DS', DIRECTORY_SEPARATOR); } /** * set SMARTY_DIR to absolute path to Smarty library files. * Sets SMARTY_DIR only if user application has not already defined it. */ if (!defined('SMARTY_DIR')) { define('SMARTY_DIR', dirname(__FILE__) . DS); } /** * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it. */ if (!defined('SMARTY_SYSPLUGINS_DIR')) { define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DS); } if (!defined('SMARTY_PLUGINS_DIR')) { define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DS); } if (!defined('SMARTY_MBSTRING')) { define('SMARTY_MBSTRING', function_exists('mb_split')); } if (!defined('SMARTY_RESOURCE_CHAR_SET')) { // UTF-8 can only be done properly when mbstring is available! /** * @deprecated in favor of Smarty::$_CHARSET */ define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'); } if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) { /** * @deprecated in favor of Smarty::$_DATE_FORMAT */ define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y'); } /** * register the class autoloader */ if (!defined('SMARTY_SPL_AUTOLOAD')) { define('SMARTY_SPL_AUTOLOAD', 0); } if (SMARTY_SPL_AUTOLOAD && set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false) { $registeredAutoLoadFunctions = spl_autoload_functions(); if (!isset($registeredAutoLoadFunctions['spl_autoload'])) { spl_autoload_register(); } } else { spl_autoload_register('smartyAutoload'); } /** * Load always needed external class files */ include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_data.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_templatebase.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_template.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_resource.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_resource_file.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_cacheresource.php'; include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_cacheresource_file.php'; /** * This is the main Smarty class * @package Smarty */ class Smarty extends Smarty_Internal_TemplateBase { /**#@+ * constant definitions */ /** * smarty version */ const SMARTY_VERSION = 'Smarty-3.1.13'; /** * define variable scopes */ const SCOPE_LOCAL = 0; const SCOPE_PARENT = 1; const SCOPE_ROOT = 2; const SCOPE_GLOBAL = 3; /** * define caching modes */ const CACHING_OFF = 0; const CACHING_LIFETIME_CURRENT = 1; const CACHING_LIFETIME_SAVED = 2; /** * define compile check modes */ const COMPILECHECK_OFF = 0; const COMPILECHECK_ON = 1; const COMPILECHECK_CACHEMISS = 2; /** * modes for handling of "" tags in templates. */ const PHP_PASSTHRU = 0; //-> print tags as plain text const PHP_QUOTE = 1; //-> escape tags as entities const PHP_REMOVE = 2; //-> escape tags as entities const PHP_ALLOW = 3; //-> escape tags as entities /** * filter types */ const FILTER_POST = 'post'; const FILTER_PRE = 'pre'; const FILTER_OUTPUT = 'output'; const FILTER_VARIABLE = 'variable'; /** * plugin types */ const PLUGIN_FUNCTION = 'function'; const PLUGIN_BLOCK = 'block'; const PLUGIN_COMPILER = 'compiler'; const PLUGIN_MODIFIER = 'modifier'; const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler'; /**#@-*/ /** * assigned global tpl vars */ public static $global_tpl_vars = array(); /** * error handler returned by set_error_hanlder() in Smarty::muteExpectedErrors() */ public static $_previous_error_handler = null; /** * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors() */ public static $_muted_directories = array(); /** * Flag denoting if Multibyte String functions are available */ public static $_MBSTRING = SMARTY_MBSTRING; /** * The character set to adhere to (e.g. "UTF-8") */ public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET; /** * The date format to be used internally * (accepts date() and strftime()) */ public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT; /** * Flag denoting if PCRE should run in UTF-8 mode */ public static $_UTF8_MODIFIER = 'u'; /** * Flag denoting if operating system is windows */ public static $_IS_WINDOWS = false; /**#@+ * variables */ /** * auto literal on delimiters with whitspace * @var boolean */ public $auto_literal = true; /** * display error on not assigned variables * @var boolean */ public $error_unassigned = false; /** * look up relative filepaths in include_path * @var boolean */ public $use_include_path = false; /** * template directory * @var array */ private $template_dir = array(); /** * joined template directory string used in cache keys * @var string */ public $joined_template_dir = null; /** * joined config directory string used in cache keys * @var string */ public $joined_config_dir = null; /** * default template handler * @var callable */ public $default_template_handler_func = null; /** * default config handler * @var callable */ public $default_config_handler_func = null; /** * default plugin handler * @var callable */ public $default_plugin_handler_func = null; /** * compile directory * @var string */ private $compile_dir = null; /** * plugins directory * @var array */ private $plugins_dir = array(); /** * cache directory * @var string */ private $cache_dir = null; /** * config directory * @var array */ private $config_dir = array(); /** * force template compiling? * @var boolean */ public $force_compile = false; /** * check template for modifications? * @var boolean */ public $compile_check = true; /** * use sub dirs for compiled/cached files? * @var boolean */ public $use_sub_dirs = false; /** * allow ambiguous resources (that are made unique by the resource handler) * @var boolean */ public $allow_ambiguous_resources = false; /** * caching enabled * @var boolean */ public $caching = false; /** * merge compiled includes * @var boolean */ public $merge_compiled_includes = false; /** * cache lifetime in seconds * @var integer */ public $cache_lifetime = 3600; /** * force cache file creation * @var boolean */ public $force_cache = false; /** * Set this if you want different sets of cache files for the same * templates. * * @var string */ public $cache_id = null; /** * Set this if you want different sets of compiled files for the same * templates. * * @var string */ public $compile_id = null; /** * template left-delimiter * @var string */ public $left_delimiter = "{"; /** * template right-delimiter * @var string */ public $right_delimiter = "}"; /**#@+ * security */ /** * class name * * This should be instance of Smarty_Security. * * @var string * @see Smarty_Security */ public $security_class = 'Smarty_Security'; /** * implementation of security class * * @var Smarty_Security */ public $security_policy = null; /** * controls handling of PHP-blocks * * @var integer */ public $php_handling = self::PHP_PASSTHRU; /** * controls if the php template file resource is allowed * * @var bool */ public $allow_php_templates = false; /** * Should compiled-templates be prevented from being called directly? * * {@internal * Currently used by Smarty_Internal_Template only. * }} * * @var boolean */ public $direct_access_security = true; /**#@-*/ /** * debug mode * * Setting this to true enables the debug-console. * * @var boolean */ public $debugging = false; /** * This determines if debugging is enable-able from the browser. *
    *
  • NONE => no debugging control allowed
  • *
  • URL => enable debugging when SMARTY_DEBUG is found in the URL.
  • *
* @var string */ public $debugging_ctrl = 'NONE'; /** * Name of debugging URL-param. * * Only used when $debugging_ctrl is set to 'URL'. * The name of the URL-parameter that activates debugging. * * @var type */ public $smarty_debug_id = 'SMARTY_DEBUG'; /** * Path of debug template. * @var string */ public $debug_tpl = null; /** * When set, smarty uses this value as error_reporting-level. * @var int */ public $error_reporting = null; /** * Internal flag for getTags() * @var boolean */ public $get_used_tags = false; /**#@+ * config var settings */ /** * Controls whether variables with the same name overwrite each other. * @var boolean */ public $config_overwrite = true; /** * Controls whether config values of on/true/yes and off/false/no get converted to boolean. * @var boolean */ public $config_booleanize = true; /** * Controls whether hidden config sections/vars are read from the file. * @var boolean */ public $config_read_hidden = false; /**#@-*/ /**#@+ * resource locking */ /** * locking concurrent compiles * @var boolean */ public $compile_locking = true; /** * Controls whether cache resources should emply locking mechanism * @var boolean */ public $cache_locking = false; /** * seconds to wait for acquiring a lock before ignoring the write lock * @var float */ public $locking_timeout = 10; /**#@-*/ /** * global template functions * @var array */ public $template_functions = array(); /** * resource type used if none given * * Must be an valid key of $registered_resources. * @var string */ public $default_resource_type = 'file'; /** * caching type * * Must be an element of $cache_resource_types. * * @var string */ public $caching_type = 'file'; /** * internal config properties * @var array */ public $properties = array(); /** * config type * @var string */ public $default_config_type = 'file'; /** * cached template objects * @var array */ public $template_objects = array(); /** * check If-Modified-Since headers * @var boolean */ public $cache_modified_check = false; /** * registered plugins * @var array */ public $registered_plugins = array(); /** * plugin search order * @var array */ public $plugin_search_order = array('function', 'block', 'compiler', 'class'); /** * registered objects * @var array */ public $registered_objects = array(); /** * registered classes * @var array */ public $registered_classes = array(); /** * registered filters * @var array */ public $registered_filters = array(); /** * registered resources * @var array */ public $registered_resources = array(); /** * resource handler cache * @var array */ public $_resource_handlers = array(); /** * registered cache resources * @var array */ public $registered_cache_resources = array(); /** * cache resource handler cache * @var array */ public $_cacheresource_handlers = array(); /** * autoload filter * @var array */ public $autoload_filters = array(); /** * default modifier * @var array */ public $default_modifiers = array(); /** * autoescape variable output * @var boolean */ public $escape_html = false; /** * global internal smarty vars * @var array */ public static $_smarty_vars = array(); /** * start time for execution time calculation * @var int */ public $start_time = 0; /** * default file permissions * @var int */ public $_file_perms = 0644; /** * default dir permissions * @var int */ public $_dir_perms = 0771; /** * block tag hierarchy * @var array */ public $_tag_stack = array(); /** * self pointer to Smarty object * @var Smarty */ public $smarty; /** * required by the compiler for BC * @var string */ public $_current_file = null; /** * internal flag to enable parser debugging * @var bool */ public $_parserdebug = false; /** * Saved parameter of merged templates during compilation * * @var array */ public $merged_templates_func = array(); /**#@-*/ /** * Initialize new Smarty object * */ public function __construct() { // selfpointer needed by some other class methods $this->smarty = $this; if (is_callable('mb_internal_encoding')) { mb_internal_encoding(Smarty::$_CHARSET); } $this->start_time = microtime(true); // set default dirs $this->setTemplateDir('.' . DS . 'templates' . DS) ->setCompileDir('.' . DS . 'templates_c' . DS) ->setPluginsDir(SMARTY_PLUGINS_DIR) ->setCacheDir('.' . DS . 'cache' . DS) ->setConfigDir('.' . DS . 'configs' . DS); $this->debug_tpl = 'file:' . dirname(__FILE__) . '/debug.tpl'; if (isset($_SERVER['SCRIPT_NAME'])) { $this->assignGlobal('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']); } } /** * Class destructor */ public function __destruct() { // intentionally left blank } /** * <> set selfpointer on cloned object */ public function __clone() { $this->smarty = $this; } /** * <> Generic getter. * * Calls the appropriate getter function. * Issues an E_USER_NOTICE if no valid getter is found. * * @param string $name property name * @return mixed */ public function __get($name) { $allowed = array( 'template_dir' => 'getTemplateDir', 'config_dir' => 'getConfigDir', 'plugins_dir' => 'getPluginsDir', 'compile_dir' => 'getCompileDir', 'cache_dir' => 'getCacheDir', ); if (isset($allowed[$name])) { return $this->{$allowed[$name]}(); } else { trigger_error('Undefined property: '. get_class($this) .'::$'. $name, E_USER_NOTICE); } } /** * <> Generic setter. * * Calls the appropriate setter function. * Issues an E_USER_NOTICE if no valid setter is found. * * @param string $name property name * @param mixed $value parameter passed to setter */ public function __set($name, $value) { $allowed = array( 'template_dir' => 'setTemplateDir', 'config_dir' => 'setConfigDir', 'plugins_dir' => 'setPluginsDir', 'compile_dir' => 'setCompileDir', 'cache_dir' => 'setCacheDir', ); if (isset($allowed[$name])) { $this->{$allowed[$name]}($value); } else { trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); } } /** * Check if a template resource exists * * @param string $resource_name template name * @return boolean status */ public function templateExists($resource_name) { // create template object $save = $this->template_objects; $tpl = new $this->template_class($resource_name, $this); // check if it does exists $result = $tpl->source->exists; $this->template_objects = $save; return $result; } /** * Returns a single or all global variables * * @param object $smarty * @param string $varname variable name or null * @return string variable value or or array of variables */ public function getGlobal($varname = null) { if (isset($varname)) { if (isset(self::$global_tpl_vars[$varname])) { return self::$global_tpl_vars[$varname]->value; } else { return ''; } } else { $_result = array(); foreach (self::$global_tpl_vars AS $key => $var) { $_result[$key] = $var->value; } return $_result; } } /** * Empty cache folder * * @param integer $exp_time expiration time * @param string $type resource type * @return integer number of cache files deleted */ function clearAllCache($exp_time = null, $type = null) { // load cache resource and call clearAll $_cache_resource = Smarty_CacheResource::load($this, $type); Smarty_CacheResource::invalidLoadedCache($this); return $_cache_resource->clearAll($this, $exp_time); } /** * Empty cache for a specific template * * @param string $template_name template name * @param string $cache_id cache id * @param string $compile_id compile id * @param integer $exp_time expiration time * @param string $type resource type * @return integer number of cache files deleted */ public function clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) { // load cache resource and call clear $_cache_resource = Smarty_CacheResource::load($this, $type); Smarty_CacheResource::invalidLoadedCache($this); return $_cache_resource->clear($this, $template_name, $cache_id, $compile_id, $exp_time); } /** * Loads security class and enables security * * @param string|Smarty_Security $security_class if a string is used, it must be class-name * @return Smarty current Smarty instance for chaining * @throws SmartyException when an invalid class name is provided */ public function enableSecurity($security_class = null) { if ($security_class instanceof Smarty_Security) { $this->security_policy = $security_class; return $this; } elseif (is_object($security_class)) { throw new SmartyException("Class '" . get_class($security_class) . "' must extend Smarty_Security."); } if ($security_class == null) { $security_class = $this->security_class; } if (!class_exists($security_class)) { throw new SmartyException("Security class '$security_class' is not defined"); } elseif ($security_class !== 'Smarty_Security' && !is_subclass_of($security_class, 'Smarty_Security')) { throw new SmartyException("Class '$security_class' must extend Smarty_Security."); } else { $this->security_policy = new $security_class($this); } return $this; } /** * Disable security * @return Smarty current Smarty instance for chaining */ public function disableSecurity() { $this->security_policy = null; return $this; } /** * Set template directory * * @param string|array $template_dir directory(s) of template sources * @return Smarty current Smarty instance for chaining */ public function setTemplateDir($template_dir) { $this->template_dir = array(); foreach ((array) $template_dir as $k => $v) { $this->template_dir[$k] = rtrim($v, '/\\') . DS; } $this->joined_template_dir = join(DIRECTORY_SEPARATOR, $this->template_dir); return $this; } /** * Add template directory(s) * * @param string|array $template_dir directory(s) of template sources * @param string $key of the array element to assign the template dir to * @return Smarty current Smarty instance for chaining * @throws SmartyException when the given template directory is not valid */ public function addTemplateDir($template_dir, $key=null) { // make sure we're dealing with an array $this->template_dir = (array) $this->template_dir; if (is_array($template_dir)) { foreach ($template_dir as $k => $v) { if (is_int($k)) { // indexes are not merged but appended $this->template_dir[] = rtrim($v, '/\\') . DS; } else { // string indexes are overridden $this->template_dir[$k] = rtrim($v, '/\\') . DS; } } } elseif ($key !== null) { // override directory at specified index $this->template_dir[$key] = rtrim($template_dir, '/\\') . DS; } else { // append new directory $this->template_dir[] = rtrim($template_dir, '/\\') . DS; } $this->joined_template_dir = join(DIRECTORY_SEPARATOR, $this->template_dir); return $this; } /** * Get template directories * * @param mixed index of directory to get, null to get all * @return array|string list of template directories, or directory of $index */ public function getTemplateDir($index=null) { if ($index !== null) { return isset($this->template_dir[$index]) ? $this->template_dir[$index] : null; } return (array)$this->template_dir; } /** * Set config directory * * @param string|array $template_dir directory(s) of configuration sources * @return Smarty current Smarty instance for chaining */ public function setConfigDir($config_dir) { $this->config_dir = array(); foreach ((array) $config_dir as $k => $v) { $this->config_dir[$k] = rtrim($v, '/\\') . DS; } $this->joined_config_dir = join(DIRECTORY_SEPARATOR, $this->config_dir); return $this; } /** * Add config directory(s) * * @param string|array $config_dir directory(s) of config sources * @param string key of the array element to assign the config dir to * @return Smarty current Smarty instance for chaining */ public function addConfigDir($config_dir, $key=null) { // make sure we're dealing with an array $this->config_dir = (array) $this->config_dir; if (is_array($config_dir)) { foreach ($config_dir as $k => $v) { if (is_int($k)) { // indexes are not merged but appended $this->config_dir[] = rtrim($v, '/\\') . DS; } else { // string indexes are overridden $this->config_dir[$k] = rtrim($v, '/\\') . DS; } } } elseif( $key !== null ) { // override directory at specified index $this->config_dir[$key] = rtrim($config_dir, '/\\') . DS; } else { // append new directory $this->config_dir[] = rtrim($config_dir, '/\\') . DS; } $this->joined_config_dir = join(DIRECTORY_SEPARATOR, $this->config_dir); return $this; } /** * Get config directory * * @param mixed index of directory to get, null to get all * @return array|string configuration directory */ public function getConfigDir($index=null) { if ($index !== null) { return isset($this->config_dir[$index]) ? $this->config_dir[$index] : null; } return (array)$this->config_dir; } /** * Set plugins directory * * @param string|array $plugins_dir directory(s) of plugins * @return Smarty current Smarty instance for chaining */ public function setPluginsDir($plugins_dir) { $this->plugins_dir = array(); foreach ((array)$plugins_dir as $k => $v) { $this->plugins_dir[$k] = rtrim($v, '/\\') . DS; } return $this; } /** * Adds directory of plugin files * * @param object $smarty * @param string $ |array $ plugins folder * @return Smarty current Smarty instance for chaining */ public function addPluginsDir($plugins_dir) { // make sure we're dealing with an array $this->plugins_dir = (array) $this->plugins_dir; if (is_array($plugins_dir)) { foreach ($plugins_dir as $k => $v) { if (is_int($k)) { // indexes are not merged but appended $this->plugins_dir[] = rtrim($v, '/\\') . DS; } else { // string indexes are overridden $this->plugins_dir[$k] = rtrim($v, '/\\') . DS; } } } else { // append new directory $this->plugins_dir[] = rtrim($plugins_dir, '/\\') . DS; } $this->plugins_dir = array_unique($this->plugins_dir); return $this; } /** * Get plugin directories * * @return array list of plugin directories */ public function getPluginsDir() { return (array)$this->plugins_dir; } /** * Set compile directory * * @param string $compile_dir directory to store compiled templates in * @return Smarty current Smarty instance for chaining */ public function setCompileDir($compile_dir) { $this->compile_dir = rtrim($compile_dir, '/\\') . DS; if (!isset(Smarty::$_muted_directories[$this->compile_dir])) { Smarty::$_muted_directories[$this->compile_dir] = null; } return $this; } /** * Get compiled directory * * @return string path to compiled templates */ public function getCompileDir() { return $this->compile_dir; } /** * Set cache directory * * @param string $cache_dir directory to store cached templates in * @return Smarty current Smarty instance for chaining */ public function setCacheDir($cache_dir) { $this->cache_dir = rtrim($cache_dir, '/\\') . DS; if (!isset(Smarty::$_muted_directories[$this->cache_dir])) { Smarty::$_muted_directories[$this->cache_dir] = null; } return $this; } /** * Get cache directory * * @return string path of cache directory */ public function getCacheDir() { return $this->cache_dir; } /** * Set default modifiers * * @param array|string $modifiers modifier or list of modifiers to set * @return Smarty current Smarty instance for chaining */ public function setDefaultModifiers($modifiers) { $this->default_modifiers = (array) $modifiers; return $this; } /** * Add default modifiers * * @param array|string $modifiers modifier or list of modifiers to add * @return Smarty current Smarty instance for chaining */ public function addDefaultModifiers($modifiers) { if (is_array($modifiers)) { $this->default_modifiers = array_merge($this->default_modifiers, $modifiers); } else { $this->default_modifiers[] = $modifiers; } return $this; } /** * Get default modifiers * * @return array list of default modifiers */ public function getDefaultModifiers() { return $this->default_modifiers; } /** * Set autoload filters * * @param array $filters filters to load automatically * @param string $type "pre", "output", … specify the filter type to set. Defaults to none treating $filters' keys as the appropriate types * @return Smarty current Smarty instance for chaining */ public function setAutoloadFilters($filters, $type=null) { if ($type !== null) { $this->autoload_filters[$type] = (array) $filters; } else { $this->autoload_filters = (array) $filters; } return $this; } /** * Add autoload filters * * @param array $filters filters to load automatically * @param string $type "pre", "output", … specify the filter type to set. Defaults to none treating $filters' keys as the appropriate types * @return Smarty current Smarty instance for chaining */ public function addAutoloadFilters($filters, $type=null) { if ($type !== null) { if (!empty($this->autoload_filters[$type])) { $this->autoload_filters[$type] = array_merge($this->autoload_filters[$type], (array) $filters); } else { $this->autoload_filters[$type] = (array) $filters; } } else { foreach ((array) $filters as $key => $value) { if (!empty($this->autoload_filters[$key])) { $this->autoload_filters[$key] = array_merge($this->autoload_filters[$key], (array) $value); } else { $this->autoload_filters[$key] = (array) $value; } } } return $this; } /** * Get autoload filters * * @param string $type type of filter to get autoloads for. Defaults to all autoload filters * @return array array( 'type1' => array( 'filter1', 'filter2', … ) ) or array( 'filter1', 'filter2', …) if $type was specified */ public function getAutoloadFilters($type=null) { if ($type !== null) { return isset($this->autoload_filters[$type]) ? $this->autoload_filters[$type] : array(); } return $this->autoload_filters; } /** * return name of debugging template * * @return string */ public function getDebugTemplate() { return $this->debug_tpl; } /** * set the debug template * * @param string $tpl_name * @return Smarty current Smarty instance for chaining * @throws SmartyException if file is not readable */ public function setDebugTemplate($tpl_name) { if (!is_readable($tpl_name)) { throw new SmartyException("Unknown file '{$tpl_name}'"); } $this->debug_tpl = $tpl_name; return $this; } /** * creates a template object * * @param string $template the resource handle of the template file * @param mixed $cache_id cache id to be used with this template * @param mixed $compile_id compile id to be used with this template * @param object $parent next higher level of Smarty variables * @param boolean $do_clone flag is Smarty object shall be cloned * @return object template object */ public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) { if (!empty($cache_id) && (is_object($cache_id) || is_array($cache_id))) { $parent = $cache_id; $cache_id = null; } if (!empty($parent) && is_array($parent)) { $data = $parent; $parent = null; } else { $data = null; } // default to cache_id and compile_id of Smarty object $cache_id = $cache_id === null ? $this->cache_id : $cache_id; $compile_id = $compile_id === null ? $this->compile_id : $compile_id; // already in template cache? if ($this->allow_ambiguous_resources) { $_templateId = Smarty_Resource::getUniqueTemplateName($this, $template) . $cache_id . $compile_id; } else { $_templateId = $this->joined_template_dir . '#' . $template . $cache_id . $compile_id; } if (isset($_templateId[150])) { $_templateId = sha1($_templateId); } if ($do_clone) { if (isset($this->template_objects[$_templateId])) { // return cached template object $tpl = clone $this->template_objects[$_templateId]; $tpl->smarty = clone $tpl->smarty; $tpl->parent = $parent; $tpl->tpl_vars = array(); $tpl->config_vars = array(); } else { $tpl = new $this->template_class($template, clone $this, $parent, $cache_id, $compile_id); } } else { if (isset($this->template_objects[$_templateId])) { // return cached template object $tpl = $this->template_objects[$_templateId]; $tpl->parent = $parent; $tpl->tpl_vars = array(); $tpl->config_vars = array(); } else { $tpl = new $this->template_class($template, $this, $parent, $cache_id, $compile_id); } } // fill data if present if (!empty($data) && is_array($data)) { // set up variable values foreach ($data as $_key => $_val) { $tpl->tpl_vars[$_key] = new Smarty_variable($_val); } } return $tpl; } /** * Takes unknown classes and loads plugin files for them * class name format: Smarty_PluginType_PluginName * plugin filename format: plugintype.pluginname.php * * @param string $plugin_name class plugin name to load * @param bool $check check if already loaded * @return string |boolean filepath of loaded file or false */ public function loadPlugin($plugin_name, $check = true) { // if function or class exists, exit silently (already loaded) if ($check && (is_callable($plugin_name) || class_exists($plugin_name, false))) { return true; } // Plugin name is expected to be: Smarty_[Type]_[Name] $_name_parts = explode('_', $plugin_name, 3); // class name must have three parts to be valid plugin // count($_name_parts) < 3 === !isset($_name_parts[2]) if (!isset($_name_parts[2]) || strtolower($_name_parts[0]) !== 'smarty') { throw new SmartyException("plugin {$plugin_name} is not a valid name format"); return false; } // if type is "internal", get plugin from sysplugins if (strtolower($_name_parts[1]) == 'internal') { $file = SMARTY_SYSPLUGINS_DIR . strtolower($plugin_name) . '.php'; if (file_exists($file)) { require_once($file); return $file; } else { return false; } } // plugin filename is expected to be: [type].[name].php $_plugin_filename = "{$_name_parts[1]}.{$_name_parts[2]}.php"; $_stream_resolve_include_path = function_exists('stream_resolve_include_path'); // loop through plugin dirs and find the plugin foreach($this->getPluginsDir() as $_plugin_dir) { $names = array( $_plugin_dir . $_plugin_filename, $_plugin_dir . strtolower($_plugin_filename), ); foreach ($names as $file) { if (file_exists($file)) { require_once($file); return $file; } if ($this->use_include_path && !preg_match('/^([\/\\\\]|[a-zA-Z]:[\/\\\\])/', $_plugin_dir)) { // try PHP include_path if ($_stream_resolve_include_path) { $file = stream_resolve_include_path($file); } else { $file = Smarty_Internal_Get_Include_Path::getIncludePath($file); } if ($file !== false) { require_once($file); return $file; } } } } // no plugin loaded return false; } /** * Compile all template files * * @param string $extension file extension * @param bool $force_compile force all to recompile * @param int $time_limit * @param int $max_errors * @return integer number of template files recompiled */ public function compileAllTemplates($extention = '.tpl', $force_compile = false, $time_limit = 0, $max_errors = null) { return Smarty_Internal_Utility::compileAllTemplates($extention, $force_compile, $time_limit, $max_errors, $this); } /** * Compile all config files * * @param string $extension file extension * @param bool $force_compile force all to recompile * @param int $time_limit * @param int $max_errors * @return integer number of template files recompiled */ public function compileAllConfig($extention = '.conf', $force_compile = false, $time_limit = 0, $max_errors = null) { return Smarty_Internal_Utility::compileAllConfig($extention, $force_compile, $time_limit, $max_errors, $this); } /** * Delete compiled template file * * @param string $resource_name template name * @param string $compile_id compile id * @param integer $exp_time expiration time * @return integer number of template files deleted */ public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) { return Smarty_Internal_Utility::clearCompiledTemplate($resource_name, $compile_id, $exp_time, $this); } /** * Return array of tag/attributes of all tags used by an template * * @param object $templae template object * @return array of tag/attributes */ public function getTags(Smarty_Internal_Template $template) { return Smarty_Internal_Utility::getTags($template); } /** * Run installation test * * @param array $errors Array to write errors into, rather than outputting them * @return boolean true if setup is fine, false if something is wrong */ public function testInstall(&$errors=null) { return Smarty_Internal_Utility::testInstall($this, $errors); } /** * Error Handler to mute expected messages * * @link http://php.net/set_error_handler * @param integer $errno Error level * @return boolean */ public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) { $_is_muted_directory = false; // add the SMARTY_DIR to the list of muted directories if (!isset(Smarty::$_muted_directories[SMARTY_DIR])) { $smarty_dir = realpath(SMARTY_DIR); if ($smarty_dir !== false) { Smarty::$_muted_directories[SMARTY_DIR] = array( 'file' => $smarty_dir, 'length' => strlen($smarty_dir), ); } } // walk the muted directories and test against $errfile foreach (Smarty::$_muted_directories as $key => &$dir) { if (!$dir) { // resolve directory and length for speedy comparisons $file = realpath($key); if ($file === false) { // this directory does not exist, remove and skip it unset(Smarty::$_muted_directories[$key]); continue; } $dir = array( 'file' => $file, 'length' => strlen($file), ); } if (!strncmp($errfile, $dir['file'], $dir['length'])) { $_is_muted_directory = true; break; } } // pass to next error handler if this error did not occur inside SMARTY_DIR // or the error was within smarty but masked to be ignored if (!$_is_muted_directory || ($errno && $errno & error_reporting())) { if (Smarty::$_previous_error_handler) { return call_user_func(Smarty::$_previous_error_handler, $errno, $errstr, $errfile, $errline, $errcontext); } else { return false; } } } /** * Enable error handler to mute expected messages * * @return void */ public static function muteExpectedErrors() { /* error muting is done because some people implemented custom error_handlers using http://php.net/set_error_handler and for some reason did not understand the following paragraph: It is important to remember that the standard PHP error handler is completely bypassed for the error types specified by error_types unless the callback function returns FALSE. error_reporting() settings will have no effect and your error handler will be called regardless - however you are still able to read the current value of error_reporting and act appropriately. Of particular note is that this value will be 0 if the statement that caused the error was prepended by the @ error-control operator. Smarty deliberately uses @filemtime() over file_exists() and filemtime() in some places. Reasons include - @filemtime() is almost twice as fast as using an additional file_exists() - between file_exists() and filemtime() a possible race condition is opened, which does not exist using the simple @filemtime() approach. */ $error_handler = array('Smarty', 'mutingErrorHandler'); $previous = set_error_handler($error_handler); // avoid dead loops if ($previous !== $error_handler) { Smarty::$_previous_error_handler = $previous; } } /** * Disable error handler muting expected messages * * @return void */ public static function unmuteExpectedErrors() { restore_error_handler(); } } // Check if we're running on windows Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8 if (Smarty::$_CHARSET !== 'UTF-8') { Smarty::$_UTF8_MODIFIER = ''; } /** * Smarty exception class * @package Smarty */ class SmartyException extends Exception { public static $escape = true; public function __construct($message) { $this->message = self::$escape ? htmlentities($message) : $message; } } /** * Smarty compiler exception class * @package Smarty */ class SmartyCompilerException extends SmartyException { } /** * Autoloader */ function smartyAutoload($class) { $_class = strtolower($class); $_classes = array( 'smarty_config_source' => true, 'smarty_config_compiled' => true, 'smarty_security' => true, 'smarty_cacheresource' => true, 'smarty_cacheresource_custom' => true, 'smarty_cacheresource_keyvaluestore' => true, 'smarty_resource' => true, 'smarty_resource_custom' => true, 'smarty_resource_uncompiled' => true, 'smarty_resource_recompiled' => true, ); if (!strncmp($_class, 'smarty_internal_', 16) || isset($_classes[$_class])) { include SMARTY_SYSPLUGINS_DIR . $_class . '.php'; } } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/SmartyBC.class.php ================================================ * @author Uwe Tews * @author Rodney Rehm * @package Smarty */ /** * @ignore */ require(dirname(__FILE__) . '/Smarty.class.php'); /** * Smarty Backward Compatability Wrapper Class * * @package Smarty */ class SmartyBC extends Smarty { /** * Smarty 2 BC * @var string */ public $_version = self::SMARTY_VERSION; /** * Initialize new SmartyBC object * * @param array $options options to set during initialization, e.g. array( 'forceCompile' => false ) */ public function __construct(array $options=array()) { parent::__construct($options); // register {php} tag $this->registerPlugin('block', 'php', 'smarty_php_tag'); } /** * wrapper for assign_by_ref * * @param string $tpl_var the template variable name * @param mixed &$value the referenced value to assign */ public function assign_by_ref($tpl_var, &$value) { $this->assignByRef($tpl_var, $value); } /** * wrapper for append_by_ref * * @param string $tpl_var the template variable name * @param mixed &$value the referenced value to append * @param boolean $merge flag if array elements shall be merged */ public function append_by_ref($tpl_var, &$value, $merge = false) { $this->appendByRef($tpl_var, $value, $merge); } /** * clear the given assigned template variable. * * @param string $tpl_var the template variable to clear */ public function clear_assign($tpl_var) { $this->clearAssign($tpl_var); } /** * Registers custom function to be used in templates * * @param string $function the name of the template function * @param string $function_impl the name of the PHP function to register * @param bool $cacheable * @param mixed $cache_attrs */ public function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null) { $this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs); } /** * Unregisters custom function * * @param string $function name of template function */ public function unregister_function($function) { $this->unregisterPlugin('function', $function); } /** * Registers object to be used in templates * * @param string $object name of template object * @param object $object_impl the referenced PHP object to register * @param array $allowed list of allowed methods (empty = all) * @param boolean $smarty_args smarty argument format, else traditional * @param array $block_functs list of methods that are block format */ public function register_object($object, $object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) { settype($allowed, 'array'); settype($smarty_args, 'boolean'); $this->registerObject($object, $object_impl, $allowed, $smarty_args, $block_methods); } /** * Unregisters object * * @param string $object name of template object */ public function unregister_object($object) { $this->unregisterObject($object); } /** * Registers block function to be used in templates * * @param string $block name of template block * @param string $block_impl PHP function to register * @param bool $cacheable * @param mixed $cache_attrs */ public function register_block($block, $block_impl, $cacheable=true, $cache_attrs=null) { $this->registerPlugin('block', $block, $block_impl, $cacheable, $cache_attrs); } /** * Unregisters block function * * @param string $block name of template function */ public function unregister_block($block) { $this->unregisterPlugin('block', $block); } /** * Registers compiler function * * @param string $function name of template function * @param string $function_impl name of PHP function to register * @param bool $cacheable */ public function register_compiler_function($function, $function_impl, $cacheable=true) { $this->registerPlugin('compiler', $function, $function_impl, $cacheable); } /** * Unregisters compiler function * * @param string $function name of template function */ public function unregister_compiler_function($function) { $this->unregisterPlugin('compiler', $function); } /** * Registers modifier to be used in templates * * @param string $modifier name of template modifier * @param string $modifier_impl name of PHP function to register */ public function register_modifier($modifier, $modifier_impl) { $this->registerPlugin('modifier', $modifier, $modifier_impl); } /** * Unregisters modifier * * @param string $modifier name of template modifier */ public function unregister_modifier($modifier) { $this->unregisterPlugin('modifier', $modifier); } /** * Registers a resource to fetch a template * * @param string $type name of resource * @param array $functions array of functions to handle resource */ public function register_resource($type, $functions) { $this->registerResource($type, $functions); } /** * Unregisters a resource * * @param string $type name of resource */ public function unregister_resource($type) { $this->unregisterResource($type); } /** * Registers a prefilter function to apply * to a template before compiling * * @param callable $function */ public function register_prefilter($function) { $this->registerFilter('pre', $function); } /** * Unregisters a prefilter function * * @param callable $function */ public function unregister_prefilter($function) { $this->unregisterFilter('pre', $function); } /** * Registers a postfilter function to apply * to a compiled template after compilation * * @param callable $function */ public function register_postfilter($function) { $this->registerFilter('post', $function); } /** * Unregisters a postfilter function * * @param callable $function */ public function unregister_postfilter($function) { $this->unregisterFilter('post', $function); } /** * Registers an output filter function to apply * to a template output * * @param callable $function */ public function register_outputfilter($function) { $this->registerFilter('output', $function); } /** * Unregisters an outputfilter function * * @param callable $function */ public function unregister_outputfilter($function) { $this->unregisterFilter('output', $function); } /** * load a filter of specified type and name * * @param string $type filter type * @param string $name filter name */ public function load_filter($type, $name) { $this->loadFilter($type, $name); } /** * clear cached content for the given template and cache id * * @param string $tpl_file name of template file * @param string $cache_id name of cache_id * @param string $compile_id name of compile_id * @param string $exp_time expiration time * @return boolean */ public function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null) { return $this->clearCache($tpl_file, $cache_id, $compile_id, $exp_time); } /** * clear the entire contents of cache (all templates) * * @param string $exp_time expire time * @return boolean */ public function clear_all_cache($exp_time = null) { return $this->clearCache(null, null, null, $exp_time); } /** * test to see if valid cache exists for this template * * @param string $tpl_file name of template file * @param string $cache_id * @param string $compile_id * @return boolean */ public function is_cached($tpl_file, $cache_id = null, $compile_id = null) { return $this->isCached($tpl_file, $cache_id, $compile_id); } /** * clear all the assigned template variables. */ public function clear_all_assign() { $this->clearAllAssign(); } /** * clears compiled version of specified template resource, * or all compiled template files if one is not specified. * This function is for advanced use only, not normally needed. * * @param string $tpl_file * @param string $compile_id * @param string $exp_time * @return boolean results of {@link smarty_core_rm_auto()} */ public function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null) { return $this->clearCompiledTemplate($tpl_file, $compile_id, $exp_time); } /** * Checks whether requested template exists. * * @param string $tpl_file * @return boolean */ public function template_exists($tpl_file) { return $this->templateExists($tpl_file); } /** * Returns an array containing template variables * * @param string $name * @return array */ public function get_template_vars($name=null) { return $this->getTemplateVars($name); } /** * Returns an array containing config variables * * @param string $name * @return array */ public function get_config_vars($name=null) { return $this->getConfigVars($name); } /** * load configuration values * * @param string $file * @param string $section * @param string $scope */ public function config_load($file, $section = null, $scope = 'global') { $this->ConfigLoad($file, $section, $scope); } /** * return a reference to a registered object * * @param string $name * @return object */ public function get_registered_object($name) { return $this->getRegisteredObject($name); } /** * clear configuration values * * @param string $var */ public function clear_config($var = null) { $this->clearConfig($var); } /** * trigger Smarty error * * @param string $error_msg * @param integer $error_type */ public function trigger_error($error_msg, $error_type = E_USER_WARNING) { trigger_error("Smarty error: $error_msg", $error_type); } } /** * Smarty {php}{/php} block function * * @param array $params parameter list * @param string $content contents of the block * @param object $template template object * @param boolean &$repeat repeat flag * @return string content re-formatted */ function smarty_php_tag($params, $content, $template, &$repeat) { eval($content); return ''; } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/debug.tpl ================================================ {capture name='_smarty_debug' assign=debug_output} Smarty Debug Console

Smarty Debug Console - {if isset($template_name)}{$template_name|debug_print_var nofilter}{else}Total Time {$execution_time|string_format:"%.5f"}{/if}

{if !empty($template_data)}

included templates & config files (load time in seconds)

{foreach $template_data as $template} {$template.name} (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"})
{/foreach}
{/if}

assigned template variables

{foreach $assigned_vars as $vars} {/foreach}
${$vars@key|escape:'html'} {$vars|debug_print_var nofilter}

assigned config file variables (outer template scope)

{foreach $config_vars as $vars} {/foreach}
{$vars@key|escape:'html'} {$vars|debug_print_var nofilter}
{/capture} ================================================ FILE: packages/chameleon-templates/server/smarty/plugins/block.textformat.php ================================================ * Name: textformat
* Purpose: format text a certain way with preset styles * or custom wrap/indent settings
* Params: *
 * - style         - string (email)
 * - indent        - integer (0)
 * - wrap          - integer (80)
 * - wrap_char     - string ("\n")
 * - indent_char   - string (" ")
 * - wrap_boundary - boolean (true)
 * 
* * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} * (Smarty online manual) * @param array $params parameters * @param string $content contents of the block * @param Smarty_Internal_Template $template template object * @param boolean &$repeat repeat flag * @return string content re-formatted * @author Monte Ohrt */ function smarty_block_textformat($params, $content, $template, &$repeat) { if (is_null($content)) { return; } $style = null; $indent = 0; $indent_first = 0; $indent_char = ' '; $wrap = 80; $wrap_char = "\n"; $wrap_cut = false; $assign = null; foreach ($params as $_key => $_val) { switch ($_key) { case 'style': case 'indent_char': case 'wrap_char': case 'assign': $$_key = (string)$_val; break; case 'indent': case 'indent_first': case 'wrap': $$_key = (int)$_val; break; case 'wrap_cut': $$_key = (bool)$_val; break; default: trigger_error("textformat: unknown attribute '$_key'"); } } if ($style == 'email') { $wrap = 72; } // split into paragraphs $_paragraphs = preg_split('![\r\n]{2}!', $content); $_output = ''; foreach ($_paragraphs as &$_paragraph) { if (!$_paragraph) { continue; } // convert mult. spaces & special chars to single space $_paragraph = preg_replace(array('!\s+!' . Smarty::$_UTF8_MODIFIER, '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER), array(' ', ''), $_paragraph); // indent first line if ($indent_first > 0) { $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; } // wordwrap sentences if (Smarty::$_MBSTRING) { require_once(SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'); $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); } else { $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); } // indent lines if ($indent > 0) { $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); } } $_output = implode($wrap_char . $wrap_char, $_paragraphs); if ($assign) { $template->assign($assign, $_output); } else { return $_output; } } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/plugins/function.counter.php ================================================ * Name: counter
* Purpose: print out a counter value * * @author Monte Ohrt * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} * (Smarty online manual) * @param array $params parameters * @param Smarty_Internal_Template $template template object * @return string|null */ function smarty_function_counter($params, $template) { static $counters = array(); $name = (isset($params['name'])) ? $params['name'] : 'default'; if (!isset($counters[$name])) { $counters[$name] = array( 'start'=>1, 'skip'=>1, 'direction'=>'up', 'count'=>1 ); } $counter =& $counters[$name]; if (isset($params['start'])) { $counter['start'] = $counter['count'] = (int)$params['start']; } if (!empty($params['assign'])) { $counter['assign'] = $params['assign']; } if (isset($counter['assign'])) { $template->assign($counter['assign'], $counter['count']); } if (isset($params['print'])) { $print = (bool)$params['print']; } else { $print = empty($counter['assign']); } if ($print) { $retval = $counter['count']; } else { $retval = null; } if (isset($params['skip'])) { $counter['skip'] = $params['skip']; } if (isset($params['direction'])) { $counter['direction'] = $params['direction']; } if ($counter['direction'] == "down") $counter['count'] -= $counter['skip']; else $counter['count'] += $counter['skip']; return $retval; } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/plugins/function.cycle.php ================================================ * Name: cycle
* Date: May 3, 2002
* Purpose: cycle through given values
* Params: *
 * - name      - name of cycle (optional)
 * - values    - comma separated list of values to cycle, or an array of values to cycle
 *               (this can be left out for subsequent calls)
 * - reset     - boolean - resets given var to true
 * - print     - boolean - print var or not. default is true
 * - advance   - boolean - whether or not to advance the cycle
 * - delimiter - the value delimiter, default is ","
 * - assign    - boolean, assigns to template var instead of printed.
 * 
* Examples:
*
 * {cycle values="#eeeeee,#d0d0d0d"}
 * {cycle name=row values="one,two,three" reset=true}
 * {cycle name=row}
 * 
* * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} * (Smarty online manual) * @author Monte Ohrt * @author credit to Mark Priatel * @author credit to Gerard * @author credit to Jason Sweat * @version 1.3 * @param array $params parameters * @param Smarty_Internal_Template $template template object * @return string|null */ function smarty_function_cycle($params, $template) { static $cycle_vars; $name = (empty($params['name'])) ? 'default' : $params['name']; $print = (isset($params['print'])) ? (bool)$params['print'] : true; $advance = (isset($params['advance'])) ? (bool)$params['advance'] : true; $reset = (isset($params['reset'])) ? (bool)$params['reset'] : false; if (!isset($params['values'])) { if(!isset($cycle_vars[$name]['values'])) { trigger_error("cycle: missing 'values' parameter"); return; } } else { if(isset($cycle_vars[$name]['values']) && $cycle_vars[$name]['values'] != $params['values'] ) { $cycle_vars[$name]['index'] = 0; } $cycle_vars[$name]['values'] = $params['values']; } if (isset($params['delimiter'])) { $cycle_vars[$name]['delimiter'] = $params['delimiter']; } elseif (!isset($cycle_vars[$name]['delimiter'])) { $cycle_vars[$name]['delimiter'] = ','; } if(is_array($cycle_vars[$name]['values'])) { $cycle_array = $cycle_vars[$name]['values']; } else { $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']); } if(!isset($cycle_vars[$name]['index']) || $reset ) { $cycle_vars[$name]['index'] = 0; } if (isset($params['assign'])) { $print = false; $template->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); } if($print) { $retval = $cycle_array[$cycle_vars[$name]['index']]; } else { $retval = null; } if($advance) { if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) { $cycle_vars[$name]['index'] = 0; } else { $cycle_vars[$name]['index']++; } } return $retval; } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/plugins/function.fetch.php ================================================ * Name: fetch
* Purpose: fetch file, web or ftp data and display results * * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} * (Smarty online manual) * @author Monte Ohrt * @param array $params parameters * @param Smarty_Internal_Template $template template object * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable */ function smarty_function_fetch($params, $template) { if (empty($params['file'])) { trigger_error("[plugin] fetch parameter 'file' cannot be empty",E_USER_NOTICE); return; } // strip file protocol if (stripos($params['file'], 'file://') === 0) { $params['file'] = substr($params['file'], 7); } $protocol = strpos($params['file'], '://'); if ($protocol !== false) { $protocol = strtolower(substr($params['file'], 0, $protocol)); } if (isset($template->smarty->security_policy)) { if ($protocol) { // remote resource (or php stream, …) if(!$template->smarty->security_policy->isTrustedUri($params['file'])) { return; } } else { // local file if(!$template->smarty->security_policy->isTrustedResourceDir($params['file'])) { return; } } } $content = ''; if ($protocol == 'http') { // http fetch if($uri_parts = parse_url($params['file'])) { // set defaults $host = $server_name = $uri_parts['host']; $timeout = 30; $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; $agent = "Smarty Template Engine ". Smarty::SMARTY_VERSION; $referer = ""; $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; $_is_proxy = false; if(empty($uri_parts['port'])) { $port = 80; } else { $port = $uri_parts['port']; } if(!empty($uri_parts['user'])) { $user = $uri_parts['user']; } if(!empty($uri_parts['pass'])) { $pass = $uri_parts['pass']; } // loop through parameters, setup headers foreach($params as $param_key => $param_value) { switch($param_key) { case "file": case "assign": case "assign_headers": break; case "user": if(!empty($param_value)) { $user = $param_value; } break; case "pass": if(!empty($param_value)) { $pass = $param_value; } break; case "accept": if(!empty($param_value)) { $accept = $param_value; } break; case "header": if(!empty($param_value)) { if(!preg_match('![\w\d-]+: .+!',$param_value)) { trigger_error("[plugin] invalid header format '".$param_value."'",E_USER_NOTICE); return; } else { $extra_headers[] = $param_value; } } break; case "proxy_host": if(!empty($param_value)) { $proxy_host = $param_value; } break; case "proxy_port": if(!preg_match('!\D!', $param_value)) { $proxy_port = (int) $param_value; } else { trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); return; } break; case "agent": if(!empty($param_value)) { $agent = $param_value; } break; case "referer": if(!empty($param_value)) { $referer = $param_value; } break; case "timeout": if(!preg_match('!\D!', $param_value)) { $timeout = (int) $param_value; } else { trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); return; } break; default: trigger_error("[plugin] unrecognized attribute '".$param_key."'",E_USER_NOTICE); return; } } if(!empty($proxy_host) && !empty($proxy_port)) { $_is_proxy = true; $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout); } else { $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout); } if(!$fp) { trigger_error("[plugin] unable to fetch: $errstr ($errno)",E_USER_NOTICE); return; } else { if($_is_proxy) { fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n"); } else { fputs($fp, "GET $uri HTTP/1.0\r\n"); } if(!empty($host)) { fputs($fp, "Host: $host\r\n"); } if(!empty($accept)) { fputs($fp, "Accept: $accept\r\n"); } if(!empty($agent)) { fputs($fp, "User-Agent: $agent\r\n"); } if(!empty($referer)) { fputs($fp, "Referer: $referer\r\n"); } if(isset($extra_headers) && is_array($extra_headers)) { foreach($extra_headers as $curr_header) { fputs($fp, $curr_header."\r\n"); } } if(!empty($user) && !empty($pass)) { fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n"); } fputs($fp, "\r\n"); while(!feof($fp)) { $content .= fgets($fp,4096); } fclose($fp); $csplit = preg_split("!\r\n\r\n!",$content,2); $content = $csplit[1]; if(!empty($params['assign_headers'])) { $template->assign($params['assign_headers'],preg_split("!\r\n!",$csplit[0])); } } } else { trigger_error("[plugin fetch] unable to parse URL, check syntax",E_USER_NOTICE); return; } } else { $content = @file_get_contents($params['file']); if ($content === false) { throw new SmartyException("{fetch} cannot read resource '" . $params['file'] ."'"); } } if (!empty($params['assign'])) { $template->assign($params['assign'], $content); } else { return $content; } } ?> ================================================ FILE: packages/chameleon-templates/server/smarty/plugins/function.html_checkboxes.php ================================================ * Type: function
* Name: html_checkboxes
* Date: 24.Feb.2003
* Purpose: Prints out a list of checkbox input types
* Examples: *
 * {html_checkboxes values=$ids output=$names}
 * {html_checkboxes values=$ids name='box' separator='
' output=$names} * {html_checkboxes values=$ids checked=$checked separator='
' output=$names} *
* Params: *
 * - name       (optional) - string default "checkbox"
 * - values     (required) - array
 * - options    (optional) - associative array
 * - checked    (optional) - array default not set
 * - separator  (optional) - ie 
or   * - output (optional) - the output next to each checkbox * - assign (optional) - assign the output as an array to this variable * - escape (optional) - escape the content (not value), defaults to true *
* * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} * (Smarty online manual) * @author Christopher Kvarme * @author credits to Monte Ohrt * @version 1.0 * @param array $params parameters * @param object $template template object * @return string * @uses smarty_function_escape_special_chars() */ function smarty_function_html_checkboxes($params, $template) { require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); $name = 'checkbox'; $values = null; $options = null; $selected = array(); $separator = ''; $escape = true; $labels = true; $label_ids = false; $output = null; $extra = ''; foreach($params as $_key => $_val) { switch($_key) { case 'name': case 'separator': $$_key = (string) $_val; break; case 'escape': case 'labels': case 'label_ids': $$_key = (bool) $_val; break; case 'options': $$_key = (array) $_val; break; case 'values': case 'output': $$_key = array_values((array) $_val); break; case 'checked': case 'selected': if (is_array($_val)) { $selected = array(); foreach ($_val as $_sel) { if (is_object($_sel)) { if (method_exists($_sel, "__toString")) { $_sel = smarty_function_escape_special_chars((string) $_sel->__toString()); } else { trigger_error("html_checkboxes: selected attribute contains an object of class '". get_class($_sel) ."' without __toString() method", E_USER_NOTICE); continue; } } else { $_sel = smarty_function_escape_special_chars((string) $_sel); } $selected[$_sel] = true; } } elseif (is_object($_val)) { if (method_exists($_val, "__toString")) { $selected = smarty_function_escape_special_chars((string) $_val->__toString()); } else { trigger_error("html_checkboxes: selected attribute is an object of class '". get_class($_val) ."' without __toString() method", E_USER_NOTICE); } } else { $selected = smarty_function_escape_special_chars((string) $_val); } break; case 'checkboxes': trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING); $options = (array) $_val; break; case 'assign': break; case 'strict': break; case 'disabled': case 'readonly': if (!empty($params['strict'])) { if (!is_scalar($_val)) { trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute", E_USER_NOTICE); } if ($_val === true || $_val === $_key) { $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"'; } break; } // omit break; to fall through! default: if(!is_array($_val)) { $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; } else { trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); } break; } } if (!isset($options) && !isset($values)) return ''; /* raise error here? */ $_html_result = array(); if (isset($options)) { foreach ($options as $_key=>$_val) { $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); } } else { foreach ($values as $_i=>$_key) { $_val = isset($output[$_i]) ? $output[$_i] : ''; $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); } } if(!empty($params['assign'])) { $template->assign($params['assign'], $_html_result); } else { return implode("\n", $_html_result); } } function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids, $escape=true) { $_output = ''; if (is_object($value)) { if (method_exists($value, "__toString")) { $value = (string) $value->__toString(); } else { trigger_error("html_options: value is an object of class '". get_class($value) ."' without __toString() method", E_USER_NOTICE); return ''; } } else { $value = (string) $value; } if (is_object($output)) { if (method_exists($output, "__toString")) { $output = (string) $output->__toString(); } else { trigger_error("html_options: output is an object of class '". get_class($output) ."' without __toString() method", E_USER_NOTICE); return ''; } } else { $output = (string) $output; } if ($labels) { if ($label_ids) { $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_', $name . '_' . $value)); $_output .= '