提交 b177cb8c 编写于 作者: fxy060608's avatar fxy060608

chore: remove uni-cli-nvue

上级 d01d365f
......@@ -44,7 +44,7 @@ module.exports = {
// Packages targeting Node
{
files: [
'packages/{uni-cli-shared,uni-cli-nvue,uni-app-vite,uni-h5-vite,uni-mp-vite,uni-mp-compiler,vite-plugin-uni}/**',
'packages/{uni-cli-shared,uni-app-vite,uni-h5-vite,uni-mp-vite,uni-mp-compiler,vite-plugin-uni}/**',
'packages/*/vite.config.ts',
'.eslintrc.js',
'scripts/**',
......
packages/playground/*/dist
packages/size-check/dist
packages/uni-app-vite/dist
packages/uni-cli-nvue/dist
packages/uni-cli-shared/dist
packages/uni-h5-vite/dist
packages/uni-mp-vite/dist
......
......@@ -3,7 +3,6 @@ packages/uni-app-plus/lib
packages/uni-app-vite/lib
packages/uni-app-vue/lib
packages/uni-automator/lib
packages/uni-cli-nvue/lib
packages/uni-cli-shared/lib
packages/uni-components/lib
packages/uni-cloud/lib
......
......@@ -8,7 +8,7 @@
"clean": "rm -rf node_modules **/*/node_modules && pnpm install",
"build": "node scripts/build.js",
"build:h5": "node scripts/build.js uni-app uni-cli-shared uni-h5 uni-i18n uni-stat uni-shared uni-h5-vite vite-plugin-uni",
"build:app": "node scripts/build.js uni-app-plus uni-app-vite uni-app-vue uni-cli-nvue",
"build:app": "node scripts/build.js uni-app-plus uni-app-vite uni-app-vue",
"build:mp": "node scripts/build.js uni-mp-vue uni-mp-vite uni-mp-alipay uni-mp-baidu uni-mp-kuaishou uni-mp-lark uni-mp-qq uni-mp-toutiao uni-mp-weixin uni-quickapp-webview",
"size": "npm run build size-check",
"lint": "eslint packages/*/src/**/*.ts",
......
......@@ -19,7 +19,6 @@
"license": "Apache-2.0",
"gitHead": "33e807d66e1fe47e2ee08ad9c59247e37b8884da",
"dependencies": {
"@dcloudio/uni-cli-nvue": "3.0.0-alpha-3030820220114001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3030820220114001",
"@dcloudio/uni-i18n": "3.0.0-alpha-3030820220114001",
"@dcloudio/uni-shared": "3.0.0-alpha-3030820220114001",
......
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 [yyyy] [name of copyright owner]
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.
\ No newline at end of file
# [3.1.0](https://github.com/vuejs/component-compiler-utils/compare/v3.0.2...v3.1.0) (2019-12-08)
### Features
* include filename in `finalCompilerOptions` ([#74](https://github.com/vuejs/component-compiler-utils/issues/74)) ([3dda72d](https://github.com/vuejs/component-compiler-utils/commit/3dda72d))
* support AST for template compile ([#68](https://github.com/vuejs/component-compiler-utils/issues/68)) ([ed44d6f](https://github.com/vuejs/component-compiler-utils/commit/ed44d6f))
* support audio src in `transformAssetUrls` option by default ([#72](https://github.com/vuejs/component-compiler-utils/issues/72)) ([47f1341](https://github.com/vuejs/component-compiler-utils/commit/47f1341))
## [3.0.2](https://github.com/vuejs/component-compiler-utils/compare/v3.0.1...v3.0.2) (2019-11-06)
### Bug Fixes
* also include "lib" folder for type definitions ([dd42df1](https://github.com/vuejs/component-compiler-utils/commit/dd42df1)), closes [#73](https://github.com/vuejs/component-compiler-utils/issues/73)
## [3.0.1](https://github.com/vuejs/component-compiler-utils/compare/v3.0.0...v3.0.1) (2019-11-04)
### Bug Fixes
* should not crash when prettier failed ([89e7900](https://github.com/vuejs/component-compiler-utils/commit/89e7900))
* unpin prettier version ([59a01bb](https://github.com/vuejs/component-compiler-utils/commit/59a01bb))
# [3.0.0](https://github.com/vuejs/component-compiler-utils/compare/v2.6.0...v3.0.0) (2019-04-11)
### Features
* Replace node-sass to sass ([#56](https://github.com/vuejs/component-compiler-utils/issues/56)) ([3e4e3fc](https://github.com/vuejs/component-compiler-utils/commit/3e4e3fc)), closes [#50](https://github.com/vuejs/component-compiler-utils/issues/50)
### BREAKING CHANGES
* Using `sass` instead of `node-sass` package.
# [2.6.0](https://github.com/vuejs/component-compiler-utils/compare/v2.5.2...v2.6.0) (2019-02-21)
### Features
* implement ::v-deep as a shadow piercing combinator ([#54](https://github.com/vuejs/component-compiler-utils/issues/54)) ([8b2c646](https://github.com/vuejs/component-compiler-utils/commit/8b2c646))
# [2.6.0](https://github.com/vuejs/component-compiler-utils/compare/v2.5.2...v2.6.0) (2019-02-21)
### Features
* implement ::v-deep as a shadow piercing combinator ([#54](https://github.com/vuejs/component-compiler-utils/issues/54)) ([8b2c646](https://github.com/vuejs/component-compiler-utils/commit/8b2c646))
## [2.5.2](https://github.com/vuejs/component-compiler-utils/compare/v2.5.0...v2.5.2) (2019-01-31)
### Bug Fixes
* fix sourceMap path separator on Windows, default sourceRoot to "" ([#51](https://github.com/vuejs/component-compiler-utils/issues/51)) ([df32cd9](https://github.com/vuejs/component-compiler-utils/commit/df32cd9)), closes [#47](https://github.com/vuejs/component-compiler-utils/issues/47)
* generate correct source-map when content is not padded ([#52](https://github.com/vuejs/component-compiler-utils/issues/52)) ([81be0ca](https://github.com/vuejs/component-compiler-utils/commit/81be0ca))
## [2.5.1](https://github.com/vuejs/component-compiler-utils/compare/v2.5.0...v2.5.1) (2019-01-25)
### Bug Fixes
* fix sourceMap path separator on Windows, default sourceRoot to "" ([#51](https://github.com/vuejs/component-compiler-utils/issues/51)) ([df32cd9](https://github.com/vuejs/component-compiler-utils/commit/df32cd9)), closes [#47](https://github.com/vuejs/component-compiler-utils/issues/47)
# [2.5.0](https://github.com/vuejs/component-compiler-utils/compare/v2.4.0...v2.5.0) (2019-01-08)
### Features
* add 'use' tag of SVG to 'transformAssetUrls' option as default ([#45](https://github.com/vuejs/component-compiler-utils/issues/45)) ([f4e3336](https://github.com/vuejs/component-compiler-utils/commit/f4e3336))
# [2.4.0](https://github.com/vuejs/component-compiler-utils/compare/v2.0.0...v2.4.0) (2019-01-02)
### Bug Fixes
* do not insert newline if style is already minified ([2603ee2](https://github.com/vuejs/component-compiler-utils/commit/2603ee2))
* Forward preprocessor options to less ([#25](https://github.com/vuejs/component-compiler-utils/issues/25)) ([3b19c1e](https://github.com/vuejs/component-compiler-utils/commit/3b19c1e)), closes [#24](https://github.com/vuejs/component-compiler-utils/issues/24)
* Move trim and scoped postcss plugins at the start of plugin list ([#36](https://github.com/vuejs/component-compiler-utils/issues/36)) ([0d52d86](https://github.com/vuejs/component-compiler-utils/commit/0d52d86))
* pin prettier version ([5f138a6](https://github.com/vuejs/component-compiler-utils/commit/5f138a6))
* remove space after selector when inserting scoped attribute ([5b299ed](https://github.com/vuejs/component-compiler-utils/commit/5b299ed)), closes [vue-loader/#1370](https://github.com/vuejs/component-compiler-utils/issues/1370)
* should work with variable named render (close [#23](https://github.com/vuejs/component-compiler-utils/issues/23)) ([273827b](https://github.com/vuejs/component-compiler-utils/commit/273827b))
* support standalone pseudo element selectors ([#33](https://github.com/vuejs/component-compiler-utils/issues/33)) ([d6cfbbf](https://github.com/vuejs/component-compiler-utils/commit/d6cfbbf))
* Typings for SFCDescriptor and SFCCustomBlock ([#29](https://github.com/vuejs/component-compiler-utils/issues/29)) ([bb09115](https://github.com/vuejs/component-compiler-utils/commit/bb09115))
### Features
* **scoped-css:** support leading >>> or /deep/ in selectors ([1a3b5bb](https://github.com/vuejs/component-compiler-utils/commit/1a3b5bb))
* add `prettify ` option ([#42](https://github.com/vuejs/component-compiler-utils/issues/42)) ([db3655b](https://github.com/vuejs/component-compiler-utils/commit/db3655b))
* Support `stylus` as `<style>` lang ([#18](https://github.com/vuejs/component-compiler-utils/issues/18)) ([986084e](https://github.com/vuejs/component-compiler-utils/commit/986084e))
<a name="2.3.1"></a>
## [2.3.1](https://github.com/vuejs/component-compiler-utils/compare/v2.3.0...v2.3.1) (2018-12-11)
### Bug Fixes
* do not insert newline if style is already minified ([2603ee2](https://github.com/vuejs/component-compiler-utils/commit/2603ee2))
* Move trim and scoped postcss plugins at the start of plugin list ([#36](https://github.com/vuejs/component-compiler-utils/issues/36)) ([0d52d86](https://github.com/vuejs/component-compiler-utils/commit/0d52d86))
<a name="2.3.0"></a>
# [2.3.0](https://github.com/vuejs/component-compiler-utils/compare/v2.2.0...v2.3.0) (2018-10-22)
### Bug Fixes
* support standalone pseudo element selectors ([#33](https://github.com/vuejs/component-compiler-utils/issues/33)) ([d6cfbbf](https://github.com/vuejs/component-compiler-utils/commit/d6cfbbf))
* Typings for SFCDescriptor and SFCCustomBlock ([#29](https://github.com/vuejs/component-compiler-utils/issues/29)) ([bb09115](https://github.com/vuejs/component-compiler-utils/commit/bb09115))
<a name="2.2.0"></a>
# [2.2.0](https://github.com/vuejs/component-compiler-utils/compare/v2.1.2...v2.2.0) (2018-08-16)
### Features
* **scoped-css:** support leading >>> or /deep/ in selectors ([1a3b5bb](https://github.com/vuejs/component-compiler-utils/commit/1a3b5bb))
<a name="2.1.2"></a>
## [2.1.2](https://github.com/vuejs/component-compiler-utils/compare/v2.1.1...v2.1.2) (2018-08-09)
### Bug Fixes
* pin prettier version ([5f138a6](https://github.com/vuejs/component-compiler-utils/commit/5f138a6))
<a name="2.1.1"></a>
## [2.1.1](https://github.com/vuejs/component-compiler-utils/compare/v2.1.0...v2.1.1) (2018-08-07)
### Bug Fixes
* remove space after selector when inserting scoped attribute ([5b299ed](https://github.com/vuejs/component-compiler-utils/commit/5b299ed)), closes [vue-loader/#1370](https://github.com/vuejs/component-compiler-utils/issues/1370)
<a name="2.1.0"></a>
# [2.1.0](https://github.com/vuejs/component-compiler-utils/compare/v2.0.0...v2.1.0) (2018-07-03)
### Bug Fixes
* Forward preprocessor options to less ([#25](https://github.com/vuejs/component-compiler-utils/issues/25)) ([3b19c1e](https://github.com/vuejs/component-compiler-utils/commit/3b19c1e)), closes [#24](https://github.com/vuejs/component-compiler-utils/issues/24)
* should work with variable named render (close [#23](https://github.com/vuejs/component-compiler-utils/issues/23)) ([273827b](https://github.com/vuejs/component-compiler-utils/commit/273827b))
### Features
* Support `stylus` as `<style>` lang ([#18](https://github.com/vuejs/component-compiler-utils/issues/18)) ([986084e](https://github.com/vuejs/component-compiler-utils/commit/986084e))
<a name="2.0.0"></a>
# [2.0.0](https://github.com/vuejs/component-compiler-utils/compare/v1.3.1...v2.0.0) (2018-06-03)
### Features
* Add async style compilation support ([#13](https://github.com/vuejs/component-compiler-utils/issues/13)) ([54464d6](https://github.com/vuejs/component-compiler-utils/commit/54464d6))
* allow/require compiler to be passed in for `parse` ([caa1538](https://github.com/vuejs/component-compiler-utils/commit/caa1538))
### BREAKING CHANGES
* vue template compiler must now be passed to `parse`
via options.
<a name="1.3.1"></a>
## [1.3.1](https://github.com/vuejs/component-compiler-utils/compare/v1.3.0...v1.3.1) (2018-05-28)
### Bug Fixes
* default parser was removed from prettier ([#15](https://github.com/vuejs/component-compiler-utils/issues/15)) ([598224d](https://github.com/vuejs/component-compiler-utils/commit/598224d))
<a name="1.3.0"></a>
# [1.3.0](https://github.com/vuejs/component-compiler-utils/compare/v1.2.1...v1.3.0) (2018-05-22)
### Features
* include href for <image> in transformAssetUrls (close [#12](https://github.com/vuejs/component-compiler-utils/issues/12)) ([86fddc2](https://github.com/vuejs/component-compiler-utils/commit/86fddc2))
* Provide installation instructions on missing language preprocessors ([#10](https://github.com/vuejs/component-compiler-utils/issues/10)) ([97e772c](https://github.com/vuejs/component-compiler-utils/commit/97e772c))
<a name="1.2.1"></a>
## [1.2.1](https://github.com/vuejs/component-compiler-utils/compare/v1.2.0...v1.2.1) (2018-04-26)
### Bug Fixes
* postcss import ([c845a80](https://github.com/vuejs/component-compiler-utils/commit/c845a80))
<a name="1.2.0"></a>
# [1.2.0](https://github.com/vuejs/component-compiler-utils/compare/v1.1.0...v1.2.0) (2018-04-26)
### Bug Fixes
* compile only lib directory ([#6](https://github.com/vuejs/component-compiler-utils/issues/6)) ([4f787b3](https://github.com/vuejs/component-compiler-utils/commit/4f787b3))
### Features
* accept postcss options and plugins ([#7](https://github.com/vuejs/component-compiler-utils/issues/7)) ([1456e3d](https://github.com/vuejs/component-compiler-utils/commit/1456e3d))
<a name="1.1.0"></a>
# [1.1.0](https://github.com/vuejs/component-compiler-utils/compare/9204f16...v1.1.0) (2018-04-24)
### Bug Fixes
* use more strict regex for matching css animation rules ([4644727](https://github.com/vuejs/component-compiler-utils/commit/4644727))
### Features
* adds stylus & less preprocessor ([#5](https://github.com/vuejs/component-compiler-utils/issues/5)) ([f2fd8b9](https://github.com/vuejs/component-compiler-utils/commit/f2fd8b9))
* preprocess scss/sass styles with node-sass ([#4](https://github.com/vuejs/component-compiler-utils/issues/4)) ([9204f16](https://github.com/vuejs/component-compiler-utils/commit/9204f16))
dist/compileTemplate
dist/parse
dist/parseCustomBlocks
dist/stylePlugins/scoped.js
dist/stylePlugins/remove-scoped.js
\ No newline at end of file
# @vue/component-compiler-utils [![Build Status](https://circleci.com/gh/vuejs/component-compiler-utils/tree/master.svg?style=shield)](https://circleci.com/gh/vuejs/component-compiler-utils/)
> Lower level utilities for compiling Vue single file components
This package contains lower level utilities that you can use if you are writing a plugin / transform for a bundler or module system that compiles Vue single file components into JavaScript. It is used in [vue-loader](https://github.com/vuejs/vue-loader) version 15 and above.
The API surface is intentionally minimal - the goal is to reuse as much as possible while being as flexible as possible.
## Why isn't `vue-template-compiler` a peerDependency?
Since this package is more often used as a low-level utility, it is usually a transitive dependency in an actual Vue project. It is therefore the responsibility of the higher-level package (e.g. `vue-loader`) to inject `vue-template-compiler` via options when calling the `parse` and `compileTemplate` methods.
Not listing it as a peer depedency also allows tooling authors to use a non-default template compiler instead of `vue-template-compiler` without having to include it just to fullfil the peer dep requirement.
## API
### parse(ParseOptions): SFCDescriptor
Parse raw single file component source into a descriptor with source maps. The actual compiler (`vue-template-compiler`) must be passed in via the `compiler` option so that the specific version used can be determined by the end user.
``` ts
interface ParseOptions {
source: string
filename?: string
compiler: VueTemplateCompiler
// https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#compilerparsecomponentfile-options
// default: { pad: 'line' }
compilerParseOptions?: VueTemplateCompilerParseOptions
sourceRoot?: string
needMap?: boolean
}
interface SFCDescriptor {
template: SFCBlock | null
script: SFCBlock | null
styles: SFCBlock[]
customBlocks: SFCCustomBlock[]
}
interface SFCCustomBlock {
type: string
content: string
attrs: { [key: string]: string | true }
start: number
end: number
map?: RawSourceMap
}
interface SFCBlock extends SFCCustomBlock {
lang?: string
src?: string
scoped?: boolean
module?: string | boolean
}
```
### compileTemplate(TemplateCompileOptions): TemplateCompileResults
Takes raw template source and compile it into JavaScript code. The actual compiler (`vue-template-compiler`) must be passed in via the `compiler` option so that the specific version used can be determined by the end user.
It can also optionally perform pre-processing for any templating engine supported by [consolidate](https://github.com/tj/consolidate.js/).
``` ts
interface TemplateCompileOptions {
source: string
filename: string
compiler: VueTemplateCompiler
// https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#compilercompiletemplate-options
// default: {}
compilerOptions?: VueTemplateCompilerOptions
// Template preprocessor
preprocessLang?: string
preprocessOptions?: any
// Transform asset urls found in the template into `require()` calls
// This is off by default. If set to true, the default value is
// {
// audio: 'src',
// video: ['src', 'poster'],
// source: 'src',
// img: 'src',
// image: ['xlink:href', 'href'],
// use: ['xlink:href', 'href']
// }
transformAssetUrls?: AssetURLOptions | boolean
// For vue-template-es2015-compiler, which is a fork of Buble
transpileOptions?: any
isProduction?: boolean // default: false
isFunctional?: boolean // default: false
optimizeSSR?: boolean // default: false
// Whether prettify compiled render function or not (development only)
// default: true
prettify?: boolean
}
interface TemplateCompileResult {
ast: Object | undefined
code: string
source: string
tips: string[]
errors: string[]
}
interface AssetURLOptions {
[name: string]: string | string[]
}
```
#### Handling the Output
The resulting JavaScript code will look like this:
``` js
var render = function (h) { /* ... */}
var staticRenderFns = [function (h) { /* ... */}, function (h) { /* ... */}]
```
It **does NOT** assume any module system. It is your responsibility to handle the exports, if needed.
### compileStyle(StyleCompileOptions)
Take input raw CSS and applies scoped CSS transform. It does NOT handle pre-processors. If the component doesn't use scoped CSS then this step can be skipped.
``` ts
interface StyleCompileOptions {
source: string
filename: string
id: string
map?: any
scoped?: boolean
trim?: boolean
preprocessLang?: string
preprocessOptions?: any
postcssOptions?: any
postcssPlugins?: any[]
}
interface StyleCompileResults {
code: string
map: any | void
rawResult: LazyResult | void // raw lazy result from PostCSS
errors: string[]
}
```
### compileStyleAsync(StyleCompileOptions)
Same as `compileStyle(StyleCompileOptions)` but it returns a Promise resolving to `StyleCompileResults`. It can be used with async postcss plugins.
import { LazyResult } from 'postcss';
export interface StyleCompileOptions {
source: string;
filename: string;
id: string;
map?: any;
scoped?: boolean;
trim?: boolean;
preprocessLang?: string;
preprocessOptions?: any;
postcssOptions?: any;
postcssPlugins?: any[];
}
export interface AsyncStyleCompileOptions extends StyleCompileOptions {
isAsync?: boolean;
}
export interface StyleCompileResults {
code: string;
map: any | void;
rawResult: LazyResult | void;
errors: string[];
}
export declare function compileStyle(options: StyleCompileOptions): StyleCompileResults;
export declare function compileStyleAsync(options: StyleCompileOptions): Promise<StyleCompileResults>;
export declare function doCompileStyle(options: AsyncStyleCompileOptions): StyleCompileResults;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const postcss = require('postcss');
const trim_1 = __importDefault(require("./stylePlugins/trim"));
const scoped_1 = __importDefault(require("./stylePlugins/scoped"));
const styleProcessors_1 = require("./styleProcessors");
const removeScoped_1 = __importDefault(require("./stylePlugins/remove-scoped"));
let comments;
try{
comments = require('postcss-discard-comments')
}catch(e){}
function compileStyle(options) {
return doCompileStyle(Object.assign({}, options, { isAsync: false }));
}
exports.compileStyle = compileStyle;
function compileStyleAsync(options) {
return Promise.resolve(doCompileStyle(Object.assign({}, options, { isAsync: true })));
}
exports.compileStyleAsync = compileStyleAsync;
function doCompileStyle(options) {
const { filename, id, scoped = true, trim = true, preprocessLang, postcssOptions, postcssPlugins } = options;
const preprocessor = preprocessLang && styleProcessors_1.processors[preprocessLang];
const preProcessedSource = preprocessor && preprocess(options, preprocessor);
const map = preProcessedSource ? preProcessedSource.map : options.map;
const source = preProcessedSource ? preProcessedSource.code : options.source;
const plugins = (postcssPlugins || []).slice();
if (trim) {
plugins.push(trim_1.default());
}
if (scoped) {
plugins.push(scoped_1.default(id));
} else { // fixed by xxxxxx
plugins.push(removeScoped_1.default(id));
}
// fixed by xxxxxx
if (process.env.UNI_PLATFORM === 'app-plus' && process.env.NODE_ENV === 'production') {
if (comments) {
plugins.push(comments);
}
}
const postCSSOptions = Object.assign({}, postcssOptions, { to: filename, from: filename });
if (map) {
postCSSOptions.map = {
inline: false,
annotation: false,
prev: map
};
}
let result, code, outMap;
const errors = [];
if (preProcessedSource && preProcessedSource.errors.length) {
errors.push(...preProcessedSource.errors);
}
try {
result = postcss(plugins).process(source, postCSSOptions);
// In async mode, return a promise.
if (options.isAsync) {
return result
.then((result) => ({
code: result.css || '',
map: result.map && result.map.toJSON(),
errors,
rawResult: result
}))
.catch((error) => ({
code: '',
map: undefined,
errors: [...errors, error.message],
rawResult: undefined
}));
}
// force synchronous transform (we know we only have sync plugins)
code = result.css;
outMap = result.map;
}
catch (e) {
errors.push(e);
}
return {
code: code || ``,
map: outMap && outMap.toJSON(),
errors,
rawResult: result
};
}
exports.doCompileStyle = doCompileStyle;
function preprocess(options, preprocessor) {
return preprocessor.render(options.source, options.map, Object.assign({
filename: options.filename
}, options.preprocessOptions));
}
import { VueTemplateCompiler, VueTemplateCompilerOptions, ErrorWithRange } from './types';
import { AssetURLOptions } from './templateCompilerModules/assetUrl';
export interface TemplateCompileOptions {
source: string;
filename: string;
compiler: VueTemplateCompiler;
compilerOptions?: VueTemplateCompilerOptions;
transformAssetUrls?: AssetURLOptions | boolean;
preprocessLang?: string;
preprocessOptions?: any;
transpileOptions?: any;
isProduction?: boolean;
isFunctional?: boolean;
optimizeSSR?: boolean;
prettify?: boolean;
}
export interface TemplateCompileResult {
ast: Object | undefined;
code: string;
source: string;
tips: (string | ErrorWithRange)[];
errors: (string | ErrorWithRange)[];
}
export declare function compileTemplate(options: TemplateCompileOptions): TemplateCompileResult;
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assetUrl_1 = __importDefault(require("./templateCompilerModules/assetUrl"));
const srcset_1 = __importDefault(require("./templateCompilerModules/srcset"));
const consolidate = require('consolidate');
const transpile = require('vue-template-es2015-compiler');
function compileTemplate(options) {
const { preprocessLang } = options;
const preprocessor = preprocessLang && consolidate[preprocessLang];
if (preprocessor) {
return actuallyCompile(Object.assign({}, options, {
source: preprocess(options, preprocessor)
}));
}
else if (preprocessLang) {
return { // fixed by xxxxxx recyclableRender,components
code: `var render = function () {}\n` + `var staticRenderFns = []\n` + `var recyclableRender\n` + `var components\n`,
source: options.source,
tips: [
`Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
],
errors: [
`Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
]
};
}
else {
return actuallyCompile(options);
}
}
exports.compileTemplate = compileTemplate;
function preprocess(options, preprocessor) {
const { source, filename, preprocessOptions } = options;
const finalPreprocessOptions = Object.assign({
filename
}, preprocessOptions);
// Consolidate exposes a callback based API, but the callback is in fact
// called synchronously for most templating engines. In our case, we have to
// expose a synchronous API so that it is usable in Jest transforms (which
// have to be sync because they are applied via Node.js require hooks)
let res, err;
preprocessor.render(source, finalPreprocessOptions, (_err, _res) => {
if (_err)
err = _err;
res = _res;
});
if (err)
throw err;
return res;
}
function actuallyCompile(options) {
const { source, compiler, compilerOptions = {}, transpileOptions = {}, transformAssetUrls, isProduction = process.env.NODE_ENV === 'production', isFunctional = false, optimizeSSR = false, prettify = true } = options;
const compile = optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile;
let finalCompilerOptions = compilerOptions;
if (transformAssetUrls) {
const builtInModules = [
transformAssetUrls === true
? assetUrl_1.default()
: assetUrl_1.default(transformAssetUrls),
srcset_1.default()
];
finalCompilerOptions = Object.assign({}, compilerOptions, {
modules: [...builtInModules, ...(compilerOptions.modules || [])]
});
}
// fixed by xxxxxx
const { render, staticRenderFns, tips, errors, '@render': recyclableRender, components } = compile(source, finalCompilerOptions);
if (errors && errors.length) {
return { // fixed by xxxxxx recyclableRender,components
code: `var render = function () {}\n` + `var staticRenderFns = []\n` + `var recyclableRender\n` + `var components\n`,
source,
tips,
errors
};
}
else {
const finalTranspileOptions = Object.assign({}, transpileOptions, {
transforms: Object.assign({}, transpileOptions.transforms, {
stripWithFunctional: isFunctional
})
});
const toFunction = (code) => {
return `function (${isFunctional ? `_h,_vm` : ``}) {${code}}`;
};
// fixed by xxxxxx
// transpile code with vue-template-es2015-compiler, which is a forked
// version of Buble that applies ES2015 transforms + stripping `with` usage
let code = transpile(`var __render__ = ${toFunction(render)}\n` +
`var __recyclableRender__ = ${recyclableRender ? toFunction(recyclableRender) : 'false'}\n` +
`var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`, finalTranspileOptions) + `\n`;
// #23 we use __render__ to avoid `render` not being prefixed by the
// transpiler when stripping with, but revert it back to `render` to
// maintain backwards compat
code = code.replace(/\s__(render|recyclableRender|staticRenderFns)__\s/g, ' $1 ');
code = (components || '') + '\n' + code // fixed by xxxxxx
if (!isProduction) {
// mark with stripped (this enables Vue to use correct runtime proxy
// detection)
code += `render._withStripped = true`;
if (prettify) {
code = require('prettier').format(code, {
semi: false,
parser: 'babel'
});
}
}
return {
code,
source,
tips,
errors
};
}
}
import { parse, SFCBlock, SFCCustomBlock, SFCDescriptor } from './parse';
import { compileTemplate, TemplateCompileOptions, TemplateCompileResult } from './compileTemplate';
import { compileStyle, compileStyleAsync, StyleCompileOptions, StyleCompileResults } from './compileStyle';
export { parse, compileTemplate, compileStyle, compileStyleAsync };
export { SFCBlock, SFCCustomBlock, SFCDescriptor, TemplateCompileOptions, TemplateCompileResult, StyleCompileOptions, StyleCompileResults };
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const parse_1 = require("./parse");
exports.parse = parse_1.parse;
const compileTemplate_1 = require("./compileTemplate");
exports.compileTemplate = compileTemplate_1.compileTemplate;
const compileStyle_1 = require("./compileStyle");
exports.compileStyle = compileStyle_1.compileStyle;
exports.compileStyleAsync = compileStyle_1.compileStyleAsync;
import { RawSourceMap, VueTemplateCompiler, VueTemplateCompilerParseOptions } from './types';
export interface ParseOptions {
source: string;
filename?: string;
compiler: VueTemplateCompiler;
compilerParseOptions?: VueTemplateCompilerParseOptions;
sourceRoot?: string;
needMap?: boolean;
}
export interface SFCCustomBlock {
type: string;
content: string;
attrs: {
[key: string]: string | true;
};
start: number;
end: number;
map?: RawSourceMap;
}
export interface SFCBlock extends SFCCustomBlock {
lang?: string;
src?: string;
scoped?: boolean;
module?: string | boolean;
}
export interface SFCDescriptor {
template: SFCBlock | null;
script: SFCBlock | null;
styles: SFCBlock[];
customBlocks: SFCCustomBlock[];
}
export declare function parse(options: ParseOptions): SFCDescriptor;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const source_map_1 = require("source-map");
const hash = require('hash-sum');
const cache = require('lru-cache')(100);
const splitRE = /\r?\n/g;
const emptyRE = /^(?:\/\/)?\s*$/;
function parse(options) {
const { source, filename = '', compiler, compilerParseOptions = { pad: 'line' }, sourceRoot = '', needMap = true } = options;
// fixed by xxxxxx (添加isAppNVue标记,防止nvue与vue引用相同组件时,走了相同的cache)
const cacheKey = hash(String(options.isAppNVue) + filename + source);
let output = cache.get(cacheKey);
if (output)
return output;
// fixed by xxxxxx
output = require('./parseCustomBlocks')(compiler.parseComponent(source, compilerParseOptions), options);
if (needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(filename, source, output.script.content, sourceRoot, compilerParseOptions.pad);
}
if (output.styles) {
output.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(filename, source, style.content, sourceRoot, compilerParseOptions.pad);
}
});
}
}
cache.set(cacheKey, output);
return output;
}
exports.parse = parse;
function generateSourceMap(filename, source, generated, sourceRoot, pad) {
const map = new source_map_1.SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
sourceRoot: sourceRoot.replace(/\\/g, '/')
});
let offset = 0;
if (!pad) {
offset =
source
.split(generated)
.shift()
.split(splitRE).length - 1;
}
map.setSourceContent(filename, source);
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
map.addMapping({
source: filename,
original: {
line: index + 1 + offset,
column: 0
},
generated: {
line: index + 1,
column: 0
}
});
}
});
return JSON.parse(map.toString());
}
module.exports = function parseCustomBlocks(descriptor, options) {
// delete customBlocks
descriptor.customBlocks.length = 0
return descriptor
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
// postcss-selector-parser does have typings but it's problematic to work with.
const selectorParser = require('postcss-selector-parser');
exports.default = () => {
return {
postcssPlugin: 'remove-scoped',
Once (root) {
root.each(function rewriteSelector(node) {
if (!node.selector) {
// handle media queries
if (node.type === 'atrule') {
if (node.name === 'media' || node.name === 'supports') {
node.each(rewriteSelector);
}
}
return;
}
node.selector = selectorParser((selectors) => {
selectors.each((selector) => {
let node = null;
// find the last child node to insert attribute selector
selector.each((n) => {
// ">>>" combinator
// and /deep/ alias for >>>, since >>> doesn't work in SASS
if (n.type === 'combinator' &&
(n.value === '>>>' || n.value === '/deep/')) {
n.value = ' ';
n.spaces.before = n.spaces.after = '';
return false;
}
// in newer versions of sass, /deep/ support is also dropped, so add a ::v-deep alias
if (n.type === 'pseudo' && n.value === '::v-deep') {
n.value = n.spaces.before = n.spaces.after = '';
return false;
}
if (n.type !== 'pseudo' && n.type !== 'combinator') {
node = n;
}
});
if (node) {
node.spaces.after = '';
} else {
// For deep selectors & standalone pseudo selectors,
// the attribute selectors are prepended rather than appended.
// So all leading spaces must be eliminated to avoid problems.
selector.first.spaces.before = '';
}
});
}).processSync(node.selector);
});
}
}
}
import * as postcss from 'postcss';
declare const _default: postcss.Plugin<any>;
export default _default;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// postcss-selector-parser does have typings but it's problematic to work with.
const selectorParser = require('postcss-selector-parser');
exports.default = (options) => {
return {
postcssPlugin: 'add-id',
Once (root) {
const id = options;
const keyframes = Object.create(null);
root.each(function rewriteSelector(node) {
if (!node.selector) {
// handle media queries
if (node.type === 'atrule') {
if (node.name === 'media' || node.name === 'supports') {
node.each(rewriteSelector);
}
else if (/-?keyframes$/.test(node.name)) {
// register keyframes
keyframes[node.params] = node.params = node.params + '-' + id;
}
}
return;
}
node.selector = selectorParser((selectors) => {
selectors.each((selector) => {
let node = null;
// find the last child node to insert attribute selector
selector.each((n) => {
// ">>>" combinator
// and /deep/ alias for >>>, since >>> doesn't work in SASS
if (n.type === 'combinator' &&
(n.value === '>>>' || n.value === '/deep/')) {
n.value = ' ';
n.spaces.before = n.spaces.after = '';
return false;
}
// in newer versions of sass, /deep/ support is also dropped, so add a ::v-deep alias
if (n.type === 'pseudo' && n.value === '::v-deep') {
n.value = n.spaces.before = n.spaces.after = '';
return false;
}
if (n.type !== 'pseudo' && n.type !== 'combinator') {
node = n;
}
});
if (node) {
node.spaces.after = '';
}
else {
// For deep selectors & standalone pseudo selectors,
// the attribute selectors are prepended rather than appended.
// So all leading spaces must be eliminated to avoid problems.
selector.first.spaces.before = '';
}
selector.insertAfter(node, selectorParser.className({
value: id
}));
});
}).processSync(node.selector);
});
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
if (Object.keys(keyframes).length) {
root.walkDecls(decl => {
// individual animation-name declaration
if (/^(-\w+-)?animation-name$/.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',');
}
// shorthand
if (/^(-\w+-)?animation$/.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/);
const i = vals.findIndex(val => keyframes[val]);
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]]);
return vals.join(' ');
}
else {
return v;
}
})
.join(',');
}
});
}
}
}
}
import * as postcss from 'postcss';
declare const _default: postcss.Plugin<unknown>;
export default _default;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = () => {
return {
postcssPlugin: 'trim',
Once (css) {
css.walk(({ type, raws }) => {
if (type === 'rule' || type === 'atrule') {
if (raws.before)
raws.before = '\n';
if (raws.after)
raws.after = '\n';
}
});
}
}
}
\ No newline at end of file
export interface StylePreprocessor {
render(source: string, map: any | null, options: any): StylePreprocessorResults;
}
export interface StylePreprocessorResults {
code: string;
map?: any;
errors: Array<Error>;
}
export declare const processors: {
[key: string]: StylePreprocessor;
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const merge = require('merge-source-map');
// .scss/.sass processor
const scss = {
render(source, map, options) {
const nodeSass = require('sass');
const finalOptions = Object.assign({}, options, {
data: source,
file: options.filename,
outFile: options.filename,
sourceMap: !!map
});
try {
const result = nodeSass.renderSync(finalOptions);
if (map) {
return {
code: result.css.toString(),
map: merge(map, JSON.parse(result.map.toString())),
errors: []
};
}
return { code: result.css.toString(), errors: [] };
}
catch (e) {
return { code: '', errors: [e] };
}
}
};
const sass = {
render(source, map, options) {
return scss.render(source, map, Object.assign({}, options, { indentedSyntax: true }));
}
};
// .less
const less = {
render(source, map, options) {
const nodeLess = require('less');
let result;
let error = null;
nodeLess.render(source, Object.assign({}, options, { syncImport: true }), (err, output) => {
error = err;
result = output;
});
if (error)
return { code: '', errors: [error] };
if (map) {
return {
code: result.css.toString(),
map: merge(map, result.map),
errors: []
};
}
return { code: result.css.toString(), errors: [] };
}
};
// .styl
const styl = {
render(source, map, options) {
const nodeStylus = require('stylus');
try {
const ref = nodeStylus(source);
Object.keys(options).forEach(key => ref.set(key, options[key]));
if (map)
ref.set('sourcemap', { inline: false, comment: false });
const result = ref.render();
if (map) {
return {
code: result,
map: merge(map, ref.sourcemap),
errors: []
};
}
return { code: result, errors: [] };
}
catch (e) {
return { code: '', errors: [e] };
}
}
};
exports.processors = {
less,
sass,
scss,
styl,
stylus: styl
};
import { ASTNode } from './utils';
export interface AssetURLOptions {
[name: string]: string | string[];
}
declare const _default: (userOptions?: AssetURLOptions | undefined) => {
postTransformNode: (node: ASTNode) => void;
};
export default _default;
"use strict";
// vue compiler module for transforming `<tag>:<attribute>` to `require`
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
const defaultOptions = {
audio: 'src',
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
};
exports.default = (userOptions) => {
const options = userOptions
? Object.assign({}, defaultOptions, userOptions)
: defaultOptions;
return {
postTransformNode: (node) => {
transform(node, options);
}
};
};
function transform(node, options) {
for (const tag in options) {
if ((tag === '*' || node.tag === tag) && node.attrs) {
const attributes = options[tag];
if (typeof attributes === 'string') {
node.attrs.some(attr => rewrite(attr, attributes));
}
else if (Array.isArray(attributes)) {
attributes.forEach(item => node.attrs.some(attr => rewrite(attr, item)));
}
}
}
}
function rewrite(attr, name) {
if (attr.name === name) {
const value = attr.value;
// only transform static URLs
if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
attr.value = utils_1.urlToRequire(value.slice(1, -1));
return true;
}
}
return false;
}
import { ASTNode } from './utils';
declare const _default: () => {
postTransformNode: (node: ASTNode) => void;
};
export default _default;
"use strict";
// vue compiler module for transforming `img:srcset` to a number of `require`s
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
exports.default = () => ({
postTransformNode: (node) => {
transform(node);
}
});
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g;
function transform(node) {
const tags = ['img', 'source'];
if (tags.indexOf(node.tag) !== -1 && node.attrs) {
node.attrs.forEach(attr => {
if (attr.name === 'srcset') {
// same logic as in transform-require.js
const value = attr.value;
const isStatic = value.charAt(0) === '"' && value.charAt(value.length - 1) === '"';
if (!isStatic) {
return;
}
const imageCandidates = value
.substr(1, value.length - 2)
.split(',')
.map(s => {
// The attribute value arrives here with all whitespace, except
// normal spaces, represented by escape sequences
const [url, descriptor] = s
.replace(escapedSpaceCharacters, ' ')
.trim()
.split(' ', 2);
return { require: utils_1.urlToRequire(url), descriptor };
});
// "require(url1)"
// "require(url1) 1x"
// "require(url1), require(url2)"
// "require(url1), require(url2) 2x"
// "require(url1) 1x, require(url2)"
// "require(url1) 1x, require(url2) 2x"
const code = imageCandidates
.map(({ require, descriptor }) => `${require} + "${descriptor ? ' ' + descriptor : ''}, " + `)
.join('')
.slice(0, -6)
.concat('"')
.replace(/ \+ ""$/, '');
attr.value = code;
}
});
}
}
export interface Attr {
name: string;
value: string;
}
export interface ASTNode {
tag: string;
attrs: Attr[];
}
export declare function urlToRequire(url: string): string;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const url_1 = require("url");
function urlToRequire(url) {
const returnValue = `"${url}"`;
// same logic as in transform-require.js
const firstChar = url.charAt(0);
if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
if (firstChar === '~') {
const secondChar = url.charAt(1);
url = url.slice(secondChar === '/' ? 2 : 1);
}
const uriParts = parseUriParts(url);
if (!uriParts.hash) {
return `require("${url}")`;
}
else {
// support uri fragment case by excluding it from
// the require and instead appending it as string;
// assuming that the path part is sufficient according to
// the above caseing(t.i. no protocol-auth-host parts expected)
return `require("${uriParts.path}") + "${uriParts.hash}"`;
}
}
return returnValue;
}
exports.urlToRequire = urlToRequire;
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
* @param urlString an url as a string
*/
function parseUriParts(urlString) {
// initialize return value
const returnValue = url_1.parse('');
if (urlString) {
// A TypeError is thrown if urlString is not a string
// @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
if ('string' === typeof urlString) {
// check is an uri
return url_1.parse(urlString); // take apart the uri
}
}
return returnValue;
}
import { SFCDescriptor } from './parse';
export interface StartOfSourceMap {
file?: string;
sourceRoot?: string;
}
export interface RawSourceMap extends StartOfSourceMap {
version: string;
sources: string[];
names: string[];
sourcesContent?: string[];
mappings: string;
}
export interface VueTemplateCompiler {
parseComponent(source: string, options?: any): SFCDescriptor;
compile(template: string, options: VueTemplateCompilerOptions): VueTemplateCompilerResults;
ssrCompile(template: string, options: VueTemplateCompilerOptions): VueTemplateCompilerResults;
}
export interface VueTemplateCompilerOptions {
modules?: Object[];
outputSourceRange?: boolean;
whitespace?: 'preserve' | 'condense';
directives?: {
[key: string]: Function;
};
}
export interface VueTemplateCompilerParseOptions {
pad?: 'line' | 'space';
}
export interface ErrorWithRange {
msg: string;
start: number;
end: number;
}
export interface VueTemplateCompilerResults {
ast: Object | undefined;
render: string;
staticRenderFns: string[];
errors: (string | ErrorWithRange)[];
tips: (string | ErrorWithRange)[];
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const postcss = require('postcss')
import { ProcessOptions, LazyResult } from 'postcss'
import trimPlugin from './stylePlugins/trim'
import scopedPlugin from './stylePlugins/scoped'
import {
processors,
StylePreprocessor,
StylePreprocessorResults
} from './styleProcessors'
export interface StyleCompileOptions {
source: string
filename: string
id: string
map?: any
scoped?: boolean
trim?: boolean
preprocessLang?: string
preprocessOptions?: any
postcssOptions?: any
postcssPlugins?: any[]
}
export interface AsyncStyleCompileOptions extends StyleCompileOptions {
isAsync?: boolean
}
export interface StyleCompileResults {
code: string
map: any | void
rawResult: LazyResult | void
errors: string[]
}
export function compileStyle(
options: StyleCompileOptions
): StyleCompileResults {
return doCompileStyle({ ...options, isAsync: false })
}
export function compileStyleAsync(
options: StyleCompileOptions
): Promise<StyleCompileResults> {
return Promise.resolve(doCompileStyle({ ...options, isAsync: true }))
}
export function doCompileStyle(
options: AsyncStyleCompileOptions
): StyleCompileResults {
const {
filename,
id,
scoped = true,
trim = true,
preprocessLang,
postcssOptions,
postcssPlugins
} = options
const preprocessor = preprocessLang && processors[preprocessLang]
const preProcessedSource = preprocessor && preprocess(options, preprocessor)
const map = preProcessedSource ? preProcessedSource.map : options.map
const source = preProcessedSource ? preProcessedSource.code : options.source
const plugins = (postcssPlugins || []).slice()
if (trim) {
plugins.push(trimPlugin())
}
if (scoped) {
plugins.push(scopedPlugin(id))
}
const postCSSOptions: ProcessOptions = {
...postcssOptions,
to: filename,
from: filename
}
if (map) {
postCSSOptions.map = {
inline: false,
annotation: false,
prev: map
}
}
let result, code, outMap
const errors: any[] = []
if (preProcessedSource && preProcessedSource.errors.length) {
errors.push(...preProcessedSource.errors)
}
try {
result = postcss(plugins).process(source, postCSSOptions)
// In async mode, return a promise.
if (options.isAsync) {
return result
.then(
(result: LazyResult): StyleCompileResults => ({
code: result.css || '',
map: result.map && result.map.toJSON(),
errors,
rawResult: result
})
)
.catch(
(error: Error): StyleCompileResults => ({
code: '',
map: undefined,
errors: [...errors, error.message],
rawResult: undefined
})
)
}
// force synchronous transform (we know we only have sync plugins)
code = result.css
outMap = result.map
} catch (e) {
errors.push(e)
}
return {
code: code || ``,
map: outMap && outMap.toJSON(),
errors,
rawResult: result
}
}
function preprocess(
options: StyleCompileOptions,
preprocessor: StylePreprocessor
): StylePreprocessorResults {
return preprocessor.render(
options.source,
options.map,
Object.assign(
{
filename: options.filename
},
options.preprocessOptions
)
)
}
import {
VueTemplateCompiler,
VueTemplateCompilerOptions,
ErrorWithRange
} from './types'
import assetUrlsModule, {
AssetURLOptions
} from './templateCompilerModules/assetUrl'
import srcsetModule from './templateCompilerModules/srcset'
const consolidate = require('consolidate')
const transpile = require('vue-template-es2015-compiler')
export interface TemplateCompileOptions {
source: string
filename: string
compiler: VueTemplateCompiler
compilerOptions?: VueTemplateCompilerOptions
transformAssetUrls?: AssetURLOptions | boolean
preprocessLang?: string
preprocessOptions?: any
transpileOptions?: any
isProduction?: boolean
isFunctional?: boolean
optimizeSSR?: boolean
prettify?: boolean
}
export interface TemplateCompileResult {
ast: Object | undefined
code: string
source: string
tips: (string | ErrorWithRange)[]
errors: (string | ErrorWithRange)[]
}
export function compileTemplate(
options: TemplateCompileOptions
): TemplateCompileResult {
const { preprocessLang } = options
const preprocessor = preprocessLang && consolidate[preprocessLang]
if (preprocessor) {
return actuallyCompile(
Object.assign({}, options, {
source: preprocess(options, preprocessor)
})
)
} else if (preprocessLang) {
return {
ast: {},
code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
source: options.source,
tips: [
`Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
],
errors: [
`Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
]
}
} else {
return actuallyCompile(options)
}
}
function preprocess(
options: TemplateCompileOptions,
preprocessor: any
): string {
const { source, filename, preprocessOptions } = options
const finalPreprocessOptions = Object.assign(
{
filename
},
preprocessOptions
)
// Consolidate exposes a callback based API, but the callback is in fact
// called synchronously for most templating engines. In our case, we have to
// expose a synchronous API so that it is usable in Jest transforms (which
// have to be sync because they are applied via Node.js require hooks)
let res: any, err
preprocessor.render(
source,
finalPreprocessOptions,
(_err: Error | null, _res: string) => {
if (_err) err = _err
res = _res
}
)
if (err) throw err
return res
}
function actuallyCompile(
options: TemplateCompileOptions
): TemplateCompileResult {
const {
source,
compiler,
compilerOptions = {},
transpileOptions = {},
transformAssetUrls,
isProduction = process.env.NODE_ENV === 'production',
isFunctional = false,
optimizeSSR = false,
prettify = true
} = options
const compile =
optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile
let finalCompilerOptions = compilerOptions
if (transformAssetUrls) {
const builtInModules = [
transformAssetUrls === true
? assetUrlsModule()
: assetUrlsModule(transformAssetUrls),
srcsetModule()
]
finalCompilerOptions = Object.assign({}, compilerOptions, {
modules: [...builtInModules, ...(compilerOptions.modules || [])],
filename: options.filename
})
}
const { ast, render, staticRenderFns, tips, errors } = compile(
source,
finalCompilerOptions
)
if (errors && errors.length) {
return {
ast,
code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
source,
tips,
errors
}
} else {
const finalTranspileOptions = Object.assign({}, transpileOptions, {
transforms: Object.assign({}, transpileOptions.transforms, {
stripWithFunctional: isFunctional
})
})
const toFunction = (code: string): string => {
return `function (${isFunctional ? `_h,_vm` : ``}) {${code}}`
}
// transpile code with vue-template-es2015-compiler, which is a forked
// version of Buble that applies ES2015 transforms + stripping `with` usage
let code =
transpile(
`var __render__ = ${toFunction(render)}\n` +
`var __staticRenderFns__ = [${staticRenderFns.map(toFunction)}]`,
finalTranspileOptions
) + `\n`
// #23 we use __render__ to avoid `render` not being prefixed by the
// transpiler when stripping with, but revert it back to `render` to
// maintain backwards compat
code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ')
if (!isProduction) {
// mark with stripped (this enables Vue to use correct runtime proxy
// detection)
code += `render._withStripped = true`
if (prettify) {
try {
code = require('prettier').format(code, {
semi: false,
parser: 'babel'
})
} catch (e) {
tips.push(
`Failed to prettify component ${options.filename} template source after compilation.`
)
}
}
}
return {
ast,
code,
source,
tips,
errors
}
}
}
import { parse, SFCBlock, SFCCustomBlock, SFCDescriptor } from './parse'
import {
compileTemplate,
TemplateCompileOptions,
TemplateCompileResult
} from './compileTemplate'
import {
compileStyle,
compileStyleAsync,
StyleCompileOptions,
StyleCompileResults
} from './compileStyle'
// API
export { parse, compileTemplate, compileStyle, compileStyleAsync }
// types
export {
SFCBlock,
SFCCustomBlock,
SFCDescriptor,
TemplateCompileOptions,
TemplateCompileResult,
StyleCompileOptions,
StyleCompileResults
}
import { SourceMapGenerator } from 'source-map'
import {
RawSourceMap,
VueTemplateCompiler,
VueTemplateCompilerParseOptions
} from './types'
const hash = require('hash-sum')
const cache = require('lru-cache')(100)
const splitRE = /\r?\n/g
const emptyRE = /^(?:\/\/)?\s*$/
export interface ParseOptions {
source: string
filename?: string
compiler: VueTemplateCompiler
compilerParseOptions?: VueTemplateCompilerParseOptions
sourceRoot?: string
needMap?: boolean
}
export interface SFCCustomBlock {
type: string
content: string
attrs: { [key: string]: string | true }
start: number
end: number
map?: RawSourceMap
}
export interface SFCBlock extends SFCCustomBlock {
lang?: string
src?: string
scoped?: boolean
module?: string | boolean
}
export interface SFCDescriptor {
template: SFCBlock | null
script: SFCBlock | null
styles: SFCBlock[]
customBlocks: SFCCustomBlock[]
}
export function parse(options: ParseOptions): SFCDescriptor {
const {
source,
filename = '',
compiler,
compilerParseOptions = { pad: 'line' } as VueTemplateCompilerParseOptions,
sourceRoot = '',
needMap = true
} = options
const cacheKey = hash(filename + source)
let output: SFCDescriptor = cache.get(cacheKey)
if (output) return output
output = compiler.parseComponent(source, compilerParseOptions)
if (needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(
filename,
source,
output.script.content,
sourceRoot,
compilerParseOptions.pad
)
}
if (output.styles) {
output.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(
filename,
source,
style.content,
sourceRoot,
compilerParseOptions.pad
)
}
})
}
}
cache.set(cacheKey, output)
return output
}
function generateSourceMap(
filename: string,
source: string,
generated: string,
sourceRoot: string,
pad?: 'line' | 'space'
): RawSourceMap {
const map = new SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
sourceRoot: sourceRoot.replace(/\\/g, '/')
})
let offset = 0
if (!pad) {
offset =
source
.split(generated)
.shift()!
.split(splitRE).length - 1
}
map.setSourceContent(filename, source)
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
map.addMapping({
source: filename,
original: {
line: index + 1 + offset,
column: 0
},
generated: {
line: index + 1,
column: 0
}
})
}
})
return JSON.parse(map.toString())
}
import { Root } from 'postcss'
import * as postcss from 'postcss'
// postcss-selector-parser does have typings but it's problematic to work with.
const selectorParser = require('postcss-selector-parser')
export default postcss.plugin('add-id', (options: any) => (root: Root) => {
const id: string = options
const keyframes = Object.create(null)
root.each(function rewriteSelector(node: any) {
if (!node.selector) {
// handle media queries
if (node.type === 'atrule') {
if (node.name === 'media' || node.name === 'supports') {
node.each(rewriteSelector)
} else if (/-?keyframes$/.test(node.name)) {
// register keyframes
keyframes[node.params] = node.params = node.params + '-' + id
}
}
return
}
node.selector = selectorParser((selectors: any) => {
selectors.each((selector: any) => {
let node: any = null
// find the last child node to insert attribute selector
selector.each((n: any) => {
// ">>>" combinator
// and /deep/ alias for >>>, since >>> doesn't work in SASS
if (
n.type === 'combinator' &&
(n.value === '>>>' || n.value === '/deep/')
) {
n.value = ' '
n.spaces.before = n.spaces.after = ''
return false
}
// in newer versions of sass, /deep/ support is also dropped, so add a ::v-deep alias
if (n.type === 'pseudo' && n.value === '::v-deep') {
n.value = n.spaces.before = n.spaces.after = ''
return false
}
if (n.type !== 'pseudo' && n.type !== 'combinator') {
node = n
}
})
if (node) {
node.spaces.after = ''
} else {
// For deep selectors & standalone pseudo selectors,
// the attribute selectors are prepended rather than appended.
// So all leading spaces must be eliminated to avoid problems.
selector.first.spaces.before = ''
}
selector.insertAfter(
node,
selectorParser.attribute({
attribute: id
})
)
})
}).processSync(node.selector)
})
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
if (Object.keys(keyframes).length) {
root.walkDecls(decl => {
// individual animation-name declaration
if (/^(-\w+-)?animation-name$/.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',')
}
// shorthand
if (/^(-\w+-)?animation$/.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/)
const i = vals.findIndex(val => keyframes[val])
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]])
return vals.join(' ')
} else {
return v
}
})
.join(',')
}
})
}
})
import { Root } from 'postcss'
import * as postcss from 'postcss'
export default postcss.plugin('trim', () => (css: Root) => {
css.walk(({ type, raws }) => {
if (type === 'rule' || type === 'atrule') {
if (raws.before) raws.before = '\n'
if (raws.after) raws.after = '\n'
}
})
})
const merge = require('merge-source-map')
export interface StylePreprocessor {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults
}
export interface StylePreprocessorResults {
code: string
map?: any
errors: Array<Error>
}
// .scss/.sass processor
const scss: StylePreprocessor = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
const nodeSass = require('sass')
const finalOptions = Object.assign({}, options, {
data: source,
file: options.filename,
outFile: options.filename,
sourceMap: !!map
})
try {
const result = nodeSass.renderSync(finalOptions)
if (map) {
return {
code: result.css.toString(),
map: merge(map, JSON.parse(result.map.toString())),
errors: []
}
}
return { code: result.css.toString(), errors: [] }
} catch (e) {
return { code: '', errors: [e] }
}
}
}
const sass = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
return scss.render(
source,
map,
Object.assign({}, options, { indentedSyntax: true })
)
}
}
// .less
const less = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
const nodeLess = require('less')
let result: any
let error: Error | null = null
nodeLess.render(
source,
Object.assign({}, options, { syncImport: true }),
(err: Error | null, output: any) => {
error = err
result = output
}
)
if (error) return { code: '', errors: [error] }
if (map) {
return {
code: result.css.toString(),
map: merge(map, result.map),
errors: []
}
}
return { code: result.css.toString(), errors: [] }
}
}
// .styl
const styl = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
const nodeStylus = require('stylus')
try {
const ref = nodeStylus(source)
Object.keys(options).forEach(key => ref.set(key, options[key]))
if (map) ref.set('sourcemap', { inline: false, comment: false })
const result = ref.render()
if (map) {
return {
code: result,
map: merge(map, ref.sourcemap),
errors: []
}
}
return { code: result, errors: [] }
} catch (e) {
return { code: '', errors: [e] }
}
}
}
export const processors: { [key: string]: StylePreprocessor } = {
less,
sass,
scss,
styl,
stylus: styl
}
// vue compiler module for transforming `<tag>:<attribute>` to `require`
import { urlToRequire, ASTNode, Attr } from './utils'
export interface AssetURLOptions {
[name: string]: string | string[]
}
const defaultOptions: AssetURLOptions = {
audio: 'src',
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
}
export default (userOptions?: AssetURLOptions) => {
const options = userOptions
? Object.assign({}, defaultOptions, userOptions)
: defaultOptions
return {
postTransformNode: (node: ASTNode) => {
transform(node, options)
}
}
}
function transform(node: ASTNode, options: AssetURLOptions) {
for (const tag in options) {
if ((tag === '*' || node.tag === tag) && node.attrs) {
const attributes = options[tag]
if (typeof attributes === 'string') {
node.attrs.some(attr => rewrite(attr, attributes))
} else if (Array.isArray(attributes)) {
attributes.forEach(item => node.attrs.some(attr => rewrite(attr, item)))
}
}
}
}
function rewrite(attr: Attr, name: string) {
if (attr.name === name) {
const value = attr.value
// only transform static URLs
if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
attr.value = urlToRequire(value.slice(1, -1))
return true
}
}
return false
}
// vue compiler module for transforming `img:srcset` to a number of `require`s
import { urlToRequire, ASTNode } from './utils'
interface ImageCandidate {
require: string
descriptor: string
}
export default () => ({
postTransformNode: (node: ASTNode) => {
transform(node)
}
})
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
function transform(node: ASTNode) {
const tags = ['img', 'source']
if (tags.indexOf(node.tag) !== -1 && node.attrs) {
node.attrs.forEach(attr => {
if (attr.name === 'srcset') {
// same logic as in transform-require.js
const value = attr.value
const isStatic =
value.charAt(0) === '"' && value.charAt(value.length - 1) === '"'
if (!isStatic) {
return
}
const imageCandidates: ImageCandidate[] = value
.substr(1, value.length - 2)
.split(',')
.map(s => {
// The attribute value arrives here with all whitespace, except
// normal spaces, represented by escape sequences
const [url, descriptor] = s
.replace(escapedSpaceCharacters, ' ')
.trim()
.split(' ', 2)
return { require: urlToRequire(url), descriptor }
})
// "require(url1)"
// "require(url1) 1x"
// "require(url1), require(url2)"
// "require(url1), require(url2) 2x"
// "require(url1) 1x, require(url2)"
// "require(url1) 1x, require(url2) 2x"
const code = imageCandidates
.map(
({ require, descriptor }) =>
`${require} + "${descriptor ? ' ' + descriptor : ''}, " + `
)
.join('')
.slice(0, -6)
.concat('"')
.replace(/ \+ ""$/, '')
attr.value = code
}
})
}
}
export interface Attr {
name: string
value: string
}
export interface ASTNode {
tag: string
attrs: Attr[]
}
import { UrlWithStringQuery, parse as uriParse } from 'url'
export function urlToRequire(url: string): string {
const returnValue = `"${url}"`
// same logic as in transform-require.js
const firstChar = url.charAt(0)
if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
if (firstChar === '~') {
const secondChar = url.charAt(1)
url = url.slice(secondChar === '/' ? 2 : 1)
}
const uriParts = parseUriParts(url)
if (!uriParts.hash) {
return `require("${url}")`
} else {
// support uri fragment case by excluding it from
// the require and instead appending it as string;
// assuming that the path part is sufficient according to
// the above caseing(t.i. no protocol-auth-host parts expected)
return `require("${uriParts.path}") + "${uriParts.hash}"`
}
}
return returnValue
}
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
* @param urlString an url as a string
*/
function parseUriParts(urlString: string): UrlWithStringQuery {
// initialize return value
const returnValue: UrlWithStringQuery = uriParse('')
if (urlString) {
// A TypeError is thrown if urlString is not a string
// @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
if ('string' === typeof urlString) {
// check is an uri
return uriParse(urlString) // take apart the uri
}
}
return returnValue
}
import { SFCDescriptor } from './parse'
export interface StartOfSourceMap {
file?: string
sourceRoot?: string
}
export interface RawSourceMap extends StartOfSourceMap {
version: string
sources: string[]
names: string[]
sourcesContent?: string[]
mappings: string
}
export interface VueTemplateCompiler {
parseComponent(source: string, options?: any): SFCDescriptor
compile(
template: string,
options: VueTemplateCompilerOptions
): VueTemplateCompilerResults
ssrCompile(
template: string,
options: VueTemplateCompilerOptions
): VueTemplateCompilerResults
}
// we'll just shim this much for now - in the future these types
// should come from vue-template-compiler directly, or this package should be
// part of the vue monorepo.
export interface VueTemplateCompilerOptions {
modules?: Object[]
outputSourceRange?: boolean
whitespace?: 'preserve' | 'condense'
directives?: { [key: string]: Function }
}
export interface VueTemplateCompilerParseOptions {
pad?: 'line' | 'space'
}
export interface ErrorWithRange {
msg: string
start: number
end: number
}
export interface VueTemplateCompilerResults {
ast: Object | undefined
render: string
staticRenderFns: string[]
errors: (string | ErrorWithRange)[]
tips: (string | ErrorWithRange)[]
}
{
"name": "@vue/component-compiler-utils",
"version": "3.1.0",
"description": "Lower level utilities for compiling Vue single file components",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist",
"lib"
],
"scripts": {
"lint": "prettier --write \"{lib,test}/**/*.ts\"",
"test": "prettier --list-different \"{lib,test}/**/*.ts\" && jest --coverage",
"build": "rm -rf dist && tsc",
"prepublishOnly": "yarn build && conventional-changelog -p angular -r 2 -i CHANGELOG.md -s"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{ts,js}": [
"prettier --write",
"git add"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/component-compiler-utils.git"
},
"keywords": [
"vue",
"sfc",
"component",
"compiler"
],
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/component-compiler-utils/issues"
},
"homepage": "https://github.com/vuejs/component-compiler-utils#readme",
"devDependencies": {
"@types/jest": "^22.2.3",
"@types/node": "^10.12.20",
"conventional-changelog-cli": "^2.0.11",
"jest": "^24.0.0",
"less": "^3.9.0",
"lint-staged": "^8.1.1",
"pug": "^2.0.3",
"sass": "^1.17.3",
"stylus": "^0.54.5",
"ts-jest": "^24.0.0",
"typescript": "^3.3.0",
"vue": "^2.6.6",
"vue-template-compiler": "^2.6.6",
"yorkie": "^2.0.0"
},
"dependencies": {
"consolidate": "^0.15.1",
"hash-sum": "^1.0.2",
"lru-cache": "^4.1.2",
"merge-source-map": "^1.1.0",
"postcss": "^7.0.14",
"postcss-selector-parser": "^5.0.0",
"prettier": "^1.18.2",
"source-map": "~0.6.1",
"vue-template-es2015-compiler": "^1.9.0"
}
}
export default function getCurrentSubNVue() {
return uni.getSubNVueById(plus.webview.currentWebview().id, true)
}
export default function requireNativePlugin(name) {
return weex.requireModule(name)
}
const qs = require('querystring')
const { attrsToQuery } = require('./utils')
module.exports = function genCustomBlocksCode (
blocks,
resourcePath,
resourceQuery,
stringifyRequest
) {
return `\n/* custom blocks */\n` + blocks.map((block, i) => {
const src = block.attrs.src || resourcePath
const attrsQuery = attrsToQuery(block.attrs)
const issuerQuery = block.attrs.src ? `&issuerPath=${qs.escape(resourcePath)}` : ''
const inheritQuery = resourceQuery ? `&${resourceQuery.slice(1)}` : ''
const query = `?vue&type=custom&index=${i}&blockType=${qs.escape(block.type)}${issuerQuery}${attrsQuery}${inheritQuery}`
return (
`import block${i} from ${stringifyRequest(src + query)}\n` +
`if (typeof block${i} === 'function') block${i}(component)`
)
}).join(`\n`) + `\n`
}
const hotReloadAPIPath = JSON.stringify(require.resolve('vue-hot-reload-api'))
const genTemplateHotReloadCode = (id, request) => {
return `
module.hot.accept(${request}, function () {
api.rerender('${id}', {
render: render,
staticRenderFns: staticRenderFns
})
})
`.trim()
}
exports.genHotReloadCode = (id, functional, templateRequest) => {
return `
/* hot reload */
if (module.hot) {
var api = require(${hotReloadAPIPath})
api.install(require('vue'))
if (api.compatible) {
module.hot.accept()
if (!api.isRecorded('${id}')) {
api.createRecord('${id}', component.options)
} else {
api.${functional ? 'rerender' : 'reload'}('${id}', component.options)
}
${templateRequest ? genTemplateHotReloadCode(id, templateRequest) : ''}
}
}
`.trim()
}
const { attrsToQuery } = require('./utils')
const hotReloadAPIPath = JSON.stringify(require.resolve('vue-hot-reload-api'))
const nonWhitespaceRE = /\S+/
module.exports = function genStyleInjectionCode (
loaderContext,
styles,
id,
resourcePath,
stringifyRequest,
needsHotReload,
needsExplicitInjection
) {
let styleImportsCode = ``
let styleInjectionCode = ``
let cssModulesHotReloadCode = ``
let hasCSSModules = false
const cssModuleNames = new Map()
function genStyleRequest (style, i) {
const src = style.src || resourcePath
const attrsQuery = attrsToQuery(style.attrs, 'css')
const inheritQuery = `&${loaderContext.resourceQuery.slice(1)}`
// make sure to only pass id when necessary so that we don't inject
// duplicate tags when multiple components import the same css file
const idQuery = style.scoped ? `&id=${id}` : ``
const query = `?vue&type=style&index=${i}${idQuery}${attrsQuery}${inheritQuery}`
return stringifyRequest(src + query)
}
function genCSSModulesCode (style, request, i) {
hasCSSModules = true
const moduleName = style.module === true ? '$style' : style.module
if (cssModuleNames.has(moduleName)) {
loaderContext.emitError(`CSS module name ${moduleName} is not unique!`)
}
cssModuleNames.set(moduleName, true)
// `(vue-)style-loader` exports the name-to-hash map directly
// `css-loader` exports it in `.locals`
const locals = `(style${i}.locals || style${i})`
const name = JSON.stringify(moduleName)
if (!needsHotReload) {
styleInjectionCode += `this[${name}] = ${locals}\n`
} else {
styleInjectionCode += `
cssModules[${name}] = ${locals}
Object.defineProperty(this, ${name}, {
configurable: true,
get: function () {
return cssModules[${name}]
}
})
`
cssModulesHotReloadCode += `
module.hot && module.hot.accept([${request}], function () {
var oldLocals = cssModules[${name}]
if (oldLocals) {
var newLocals = require(${request})
if (JSON.stringify(newLocals) !== JSON.stringify(oldLocals)) {
cssModules[${name}] = newLocals
require(${hotReloadAPIPath}).rerender("${id}")
}
}
})
`
}
}
// empty styles: with no `src` specified or only contains whitespaces
const isNotEmptyStyle = style => style.src || nonWhitespaceRE.test(style.content)
// explicit injection is needed in SSR (for critical CSS collection)
// or in Shadow Mode (for injection into shadow root)
// In these modes, vue-style-loader exports objects with the __inject__
// method; otherwise we simply import the styles.
if (!needsExplicitInjection) {
styles.forEach((style, i) => {
// do not generate requests for empty styles
if (isNotEmptyStyle(style)) {
const request = genStyleRequest(style, i)
styleImportsCode += `import style${i} from ${request}\n`
if (style.module) genCSSModulesCode(style, request, i)
}
})
} else {// fixed by xxxxxx nvue style
styleInjectionCode = `if(!this.options.style){this.options.style = {}}
Vue.prototype.__merge_style(Vue.prototype.__$appStyle__, this.options.style)
`
styles.forEach((style, i) => {
if (isNotEmptyStyle(style)) {
const request = genStyleRequest(style, i)
styleInjectionCode += `Vue.prototype.__merge_style(require(${request}).default, this.options.style)\n`
//fixed by xxxxxx 简单处理,与 weex-vue-loader 保持一致
//`var style${i} = require(${request})\n` +
//`if (style${i}.__inject__) style${i}.__inject__(context)\n`
if (style.module) genCSSModulesCode(style, request, i)
}
})
}
if (!needsExplicitInjection && !hasCSSModules) {
return styleImportsCode
}
return `
${styleImportsCode}
${hasCSSModules && needsHotReload ? `var cssModules = {}` : ``}
${needsHotReload ? `var disposed = false` : ``}
function injectStyles (context) {
${needsHotReload ? `if (disposed) return` : ``}
${styleInjectionCode}
}
${needsHotReload ? `
module.hot && module.hot.dispose(function (data) {
disposed = true
})
` : ``}
${cssModulesHotReloadCode}
`.trim()
}
const qs = require('querystring')
// these are built-in query parameters so should be ignored
// if the user happen to add them as attrs
const ignoreList = [
'id',
'index',
'src',
'type'
]
// transform the attrs on a SFC block descriptor into a resourceQuery string
exports.attrsToQuery = (attrs, langFallback) => {
let query = ``
for (const name in attrs) {
const value = attrs[name]
if (!ignoreList.includes(name)) {
query += `&${qs.escape(name)}=${value ? qs.escape(value) : ``}`
}
}
if (langFallback && !(`lang` in attrs)) {
query += `&lang=${langFallback}`
}
return query
}
// import { Plugin } from 'webpack'
import { VueTemplateCompiler } from '../../@vue/component-compiler-utils/dist/types'
import { CompilerOptions } from 'vue-template-compiler'
declare namespace VueLoader {
class VueLoaderPlugin extends Plugin {}
interface VueLoaderOptions {
transformAssetUrls?: { [tag: string]: string | Array<string> }
compiler?: VueTemplateCompiler
compilerOptions?: CompilerOptions
transpileOptions?: Object
optimizeSSR?: boolean
hotReload?: boolean
productionMode?: boolean
shadowMode?: boolean
cacheDirectory?: string
cacheIdentifier?: string
prettify?: boolean
exposeFilename?: boolean
}
}
export = VueLoader
const path = require('path')
const hash = require('hash-sum')
const qs = require('querystring')
const plugin = require('./plugin')
const selectBlock = require('./select')
const loaderUtils = require('loader-utils')
const { attrsToQuery } = require('./codegen/utils')
const { parse } = require('@vue/component-compiler-utils')
const genStylesCode = require('./codegen/styleInjection')
const { genHotReloadCode } = require('./codegen/hotReload')
const genCustomBlocksCode = require('./codegen/customBlocks')
const componentNormalizerPath = require.resolve('./runtime/componentNormalizer')
const { NS } = require('./plugin')
let errorEmitted = false
function loadTemplateCompiler (loaderContext) {
try {
return require('vue-template-compiler')
} catch (e) {
if (/version mismatch/.test(e.toString())) {
loaderContext.emitError(e)
} else {
loaderContext.emitError(new Error(
`[vue-loader] vue-template-compiler must be installed as a peer dependency, ` +
`or a compatible compiler implementation must be passed via options.`
))
}
}
}
module.exports = function (source) {
const loaderContext = this
if (!errorEmitted && !loaderContext['thread-loader'] && !loaderContext[NS]) {
loaderContext.emitError(new Error(
`vue-loader was used without the corresponding plugin. ` +
`Make sure to include VueLoaderPlugin in your webpack config.`
))
errorEmitted = true
}
const stringifyRequest = r => loaderUtils.stringifyRequest(loaderContext, r)
const {
target,
request,
minimize,
sourceMap,
rootContext,
resourcePath,
resourceQuery = ''
} = loaderContext
const rawQuery = resourceQuery.slice(1)
const inheritQuery = `&${rawQuery}`
const incomingQuery = qs.parse(rawQuery)
const options = loaderUtils.getOptions(loaderContext) || {}
const isServer = target === 'node'
const isShadow = !!options.shadowMode
const isProduction = options.productionMode || minimize || process.env.NODE_ENV === 'production'
const filename = path.basename(resourcePath)
const context = rootContext || process.cwd()
const sourceRoot = path.dirname(path.relative(context, resourcePath))
const descriptor = parse({
source,
compiler: options.compiler || loadTemplateCompiler(loaderContext),
filename,
sourceRoot,
needMap: sourceMap
})
// if the query has a type field, this is a language block request
// e.g. foo.vue?type=template&id=xxxxx
// and we will return early
if (incomingQuery.type) {
return selectBlock(
descriptor,
loaderContext,
incomingQuery,
!!options.appendExtension
)
}
// module id for scoped CSS & hot-reload
const rawShortFilePath = path
.relative(context, resourcePath)
.replace(/^(\.\.[\/\\])+/, '')
const shortFilePath = rawShortFilePath.replace(/\\/g, '/') + resourceQuery
const id = hash(
isProduction
? (shortFilePath + '\n' + source.replace(/\r\n/g, '\n'))
: shortFilePath
)
// feature information
const hasScoped = descriptor.styles.some(s => s.scoped)
const hasFunctional = descriptor.template && descriptor.template.attrs.functional
const needsHotReload = (
!isServer &&
!isProduction &&
(descriptor.script || descriptor.template) &&
options.hotReload !== false
)
// template
let templateImport = `var render, staticRenderFns`
let templateRequest
// fixed by xxxxxx (recyclable,auto components)
templateImport += `, recyclableRender, components`
const recyclable = descriptor.template && !!(descriptor.template.attrs && descriptor.template.attrs.recyclable)
if (descriptor.template) {
const src = descriptor.template.src || resourcePath
const idQuery = `&id=${id}`
const scopedQuery = hasScoped ? `&scoped=true` : ``
const attrsQuery = attrsToQuery(descriptor.template.attrs)
const query = `?vue&type=template${idQuery}${scopedQuery}${attrsQuery}${inheritQuery}`
const request = templateRequest = stringifyRequest(src + query)
// templateImport = `import { render, staticRenderFns } from ${request}`
// fixed by xxxxxx (auto components)
templateImport = `import { render, staticRenderFns, recyclableRender, components } from ${request}`
}
// script
let scriptImport = `var script = {}`
if (descriptor.script) {
const src = descriptor.script.src || resourcePath
const attrsQuery = attrsToQuery(descriptor.script.attrs, 'js')
const query = `?vue&type=script${attrsQuery}${inheritQuery}`
const request = stringifyRequest(src + query)
scriptImport = (
`import script from ${request}\n` +
`export * from ${request}` // support named exports
)
}
// styles fixed by xxxxxx
let stylesCode = ``
if (descriptor.styles.length) {
stylesCode = genStylesCode(
loaderContext,
descriptor.styles,
id,
resourcePath,
stringifyRequest,
needsHotReload,
true // isServer || isShadow // needs explicit injection?
)
}
// fixed by xxxxxx (injectStyles,auto components)
let code = `
${templateImport}
${scriptImport}
${stylesCode}
/* normalize component */
import normalizer from ${stringifyRequest(`!${componentNormalizerPath}`)}
var component = normalizer(
script,
render,
staticRenderFns,
${hasFunctional ? `true` : `false`},
${`null`/* fixed by xxxxxx */},
${hasScoped ? JSON.stringify(id) : `null`},
${isServer ? JSON.stringify(hash(request)) : `null`},
${isShadow ? `,true` : `false`},
components
)
`.trim() + `\n`
if (descriptor.customBlocks && descriptor.customBlocks.length) {
code += genCustomBlocksCode(
descriptor.customBlocks,
resourcePath,
resourceQuery,
stringifyRequest
)
}
if (needsHotReload) {
code += `\n` + genHotReloadCode(id, hasFunctional, templateRequest)
}
// fixed by xxxxxx (app-nvue injectStyles)
if (/injectStyles/.test(stylesCode)) {
code +=`\ninjectStyles.call(component)`
}
// Expose filename. This is used by the devtools and Vue runtime warnings.
if (!isProduction) {
// Expose the file's full path in development, so that it can be opened
// from the devtools.
code += `\ncomponent.options.__file = ${JSON.stringify(rawShortFilePath.replace(/\\/g, '/'))}`
} else if (options.exposeFilename) {
// Libraries can opt-in to expose their components' filenames in production builds.
// For security reasons, only expose the file's basename in production.
code += `\ncomponent.options.__file = ${JSON.stringify(filename)}`
}
if (recyclable) { // fixed by xxxxxx app-plus recyclable
code += `\nrecyclableRender && (component.options["@render"] = recyclableRender)` // fixed by xxxxxx
}
code += `\nexport default component.exports`
return code
}
module.exports.VueLoaderPlugin = plugin
const qs = require('querystring')
const loaderUtils = require('loader-utils')
const hash = require('hash-sum')
const selfPath = require.resolve('../index')
const templateLoaderPath = require.resolve('./templateLoader')
const stylePostLoaderPath = require.resolve('./stylePostLoader')
const isESLintLoader = l => /(\/|\\|@)eslint-loader/.test(l.path)
const isNullLoader = l => /(\/|\\|@)null-loader/.test(l.path)
const isCSSLoader = l => /(\/|\\|@)css-loader/.test(l.path)
const isCacheLoader = l => /(\/|\\|@)cache-loader/.test(l.path)
const isPitcher = l => l.path !== __filename
const isPreLoader = l => !l.pitchExecuted
const isPostLoader = l => l.pitchExecuted
const dedupeESLintLoader = loaders => {
const res = []
let seen = false
loaders.forEach(l => {
if (!isESLintLoader(l)) {
res.push(l)
} else if (!seen) {
seen = true
res.push(l)
}
})
return res
}
const shouldIgnoreCustomBlock = loaders => {
const actualLoaders = loaders.filter(loader => {
// vue-loader
if (loader.path === selfPath) {
return false
}
// cache-loader
if (isCacheLoader(loader)) {
return false
}
return true
})
return actualLoaders.length === 0
}
module.exports = code => code
// This pitching loader is responsible for intercepting all vue block requests
// and transform it into appropriate requests.
module.exports.pitch = function (remainingRequest) {
const options = loaderUtils.getOptions(this)
const { cacheDirectory, cacheIdentifier } = options
const query = qs.parse(this.resourceQuery.slice(1))
let loaders = this.loaders
// if this is a language block request, eslint-loader may get matched
// multiple times
if (query.type) {
// if this is an inline block, since the whole file itself is being linted,
// remove eslint-loader to avoid duplicate linting.
if (/\.vue$/.test(this.resourcePath)) {
loaders = loaders.filter(l => !isESLintLoader(l))
} else {
// This is a src import. Just make sure there's not more than 1 instance
// of eslint present.
loaders = dedupeESLintLoader(loaders)
}
}
// remove self
loaders = loaders.filter(isPitcher)
// do not inject if user uses null-loader to void the type (#1239)
if (loaders.some(isNullLoader)) {
return
}
const genRequest = loaders => {
// Important: dedupe since both the original rule
// and the cloned rule would match a source import request.
// also make sure to dedupe based on loader path.
// assumes you'd probably never want to apply the same loader on the same
// file twice.
// Exception: in Vue CLI we do need two instances of postcss-loader
// for user config and inline minification. So we need to dedupe baesd on
// path AND query to be safe.
const seen = new Map()
const loaderStrings = []
loaders.forEach(loader => {
const identifier = typeof loader === 'string'
? loader
: (loader.path + loader.query)
const request = typeof loader === 'string' ? loader : loader.request
if (!seen.has(identifier)) {
seen.set(identifier, true)
// loader.request contains both the resolved loader path and its options
// query (e.g. ??ref-0)
loaderStrings.push(request)
}
})
return loaderUtils.stringifyRequest(this, '-!' + [
...loaderStrings,
this.resourcePath + this.resourceQuery
].join('!'))
}
// Inject style-post-loader before css-loader for scoped CSS and trimming
if (query.type === `style`) {
const cssLoaderIndex = loaders.findIndex(isCSSLoader)
if (cssLoaderIndex > -1) {
const afterLoaders = loaders.slice(0, cssLoaderIndex + 1)
const beforeLoaders = loaders.slice(cssLoaderIndex + 1)
const request = genRequest([
...afterLoaders,
stylePostLoaderPath,
...beforeLoaders
])
// console.log(request)
return query.module
? `export { default } from ${request}; export * from ${request}`
: `export * from ${request}`
}
}
// for templates: inject the template compiler & optional cache
if (query.type === `template`) {
const path = require('path')
const cacheLoader = cacheDirectory && cacheIdentifier
? [`${require.resolve('cache-loader')}?${JSON.stringify({
// For some reason, webpack fails to generate consistent hash if we
// use absolute paths here, even though the path is only used in a
// comment. For now we have to ensure cacheDirectory is a relative path.
cacheDirectory: (path.isAbsolute(cacheDirectory)
? path.relative(process.cwd(), cacheDirectory)
: cacheDirectory).replace(/\\/g, '/'),
cacheIdentifier: hash(cacheIdentifier) + '-vue-loader-template'
})}`]
: []
const preLoaders = loaders.filter(isPreLoader)
const postLoaders = loaders.filter(isPostLoader)
const request = genRequest([
...cacheLoader,
...postLoaders,
templateLoaderPath + `??vue-loader-options`,
...preLoaders
])
// console.log(request)
// the template compiler uses esm exports
return `export * from ${request}`
}
// if a custom block has no other matching loader other than vue-loader itself
// or cache-loader, we should ignore it
if (query.type === `custom` && shouldIgnoreCustomBlock(loaders)) {
return ``
}
// When the user defines a rule that has only resourceQuery but no test,
// both that rule and the cloned rule will match, resulting in duplicated
// loaders. Therefore it is necessary to perform a dedupe here.
const request = genRequest(loaders)
return `import mod from ${request}; export default mod; export * from ${request}`
}
const qs = require('querystring')
const { compileStyle } = require('@vue/component-compiler-utils')
// This is a post loader that handles scoped CSS transforms.
// Injected right before css-loader by the global pitcher (../pitch.js)
// for any <style scoped> selection requests initiated from within vue files.
module.exports = function (source, inMap) {
const query = qs.parse(this.resourceQuery.slice(1))
const { code, map, errors } = compileStyle({
source,
filename: this.resourcePath,
id: `data-v-${query.id}`,
map: inMap,
scoped: !!query.scoped,
trim: true
})
if (errors.length) {
this.callback(errors[0])
} else {
this.callback(null, code, map)
}
}
const qs = require('querystring')
const loaderUtils = require('loader-utils')
const { compileTemplate } = require('@vue/component-compiler-utils')
// Loader that compiles raw template into JavaScript functions.
// This is injected by the global pitcher (../pitch) for template
// selection requests initiated from vue files.
module.exports = function (source) {
const loaderContext = this
const query = qs.parse(this.resourceQuery.slice(1))
// although this is not the main vue-loader, we can get access to the same
// vue-loader options because we've set an ident in the plugin and used that
// ident to create the request for this loader in the pitcher.
const options = loaderUtils.getOptions(loaderContext) || {}
const { id } = query
const isServer = loaderContext.target === 'node'
const isProduction = options.productionMode || loaderContext.minimize || process.env.NODE_ENV === 'production'
const isFunctional = query.functional
// allow using custom compiler via options
const compiler = options.compiler || require('vue-template-compiler')
const compilerOptions = Object.assign({
outputSourceRange: true
}, options.compilerOptions, {
scopeId: query.scoped ? `data-v-${id}` : null,
comments: query.comments
})
// for vue-component-compiler
const finalOptions = {
source,
filename: this.resourcePath,
compiler,
compilerOptions,
// allow customizing behavior of vue-template-es2015-compiler
transpileOptions: options.transpileOptions,
transformAssetUrls: options.transformAssetUrls || true,
isProduction,
isFunctional,
optimizeSSR: isServer && options.optimizeSSR !== false,
prettify: options.prettify
}
const compiled = compileTemplate(finalOptions)
// tips
if (compiled.tips && compiled.tips.length) {
compiled.tips.forEach(tip => {
loaderContext.emitWarning(typeof tip === 'object' ? tip.msg : tip)
})
}
// errors
if (compiled.errors && compiled.errors.length) {
// 2.6 compiler outputs errors as objects with range
if (compiler.generateCodeFrame && finalOptions.compilerOptions.outputSourceRange) {
// TODO account for line offset in case template isn't placed at top
// of the file
loaderContext.emitError(
`\n\n Errors compiling template:\n\n` +
compiled.errors.map(({ msg, start, end }) => {
const frame = compiler.generateCodeFrame(source, start, end)
return ` ${msg}\n\n${pad(frame)}`
}).join(`\n\n`) +
'\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
)
} else {
loaderContext.emitError(
`\n Error compiling template:\n${pad(compiled.source)}\n` +
compiled.errors.map(e => ` - ${e}`).join('\n') +
'\n at ' + finalOptions.filename + ':0' // fixed by xxxxxx
)
}
}
const { code } = compiled
// fixed by xxxxxx recyclableRender, components
// finish with ESM exports
return code + `\nexport { render, staticRenderFns, recyclableRender, components }`
}
function pad (source) {
return source
.split(/\r?\n/)
.map(line => ` ${line}`)
.join('\n')
}
const qs = require('querystring')
const RuleSet = require('webpack/lib/RuleSet')
const id = 'vue-loader-plugin'
const NS = 'vue-loader'
class VueLoaderPlugin {
apply (compiler) {
// add NS marker so that the loader can detect and report missing plugin
if (compiler.hooks) {
// webpack 4
compiler.hooks.compilation.tap(id, compilation => {
const normalModuleLoader = compilation.hooks.normalModuleLoader
normalModuleLoader.tap(id, loaderContext => {
loaderContext[NS] = true
})
})
} else {
// webpack < 4
compiler.plugin('compilation', compilation => {
compilation.plugin('normal-module-loader', loaderContext => {
loaderContext[NS] = true
})
})
}
// use webpack's RuleSet utility to normalize user rules
const rawRules = compiler.options.module.rules
const { rules } = new RuleSet(rawRules)
// find the rule that applies to vue files
let vueRuleIndex = rawRules.findIndex(createMatcher(`foo.vue`))
if (vueRuleIndex < 0) {
vueRuleIndex = rawRules.findIndex(createMatcher(`foo.vue.html`))
}
const vueRule = rules[vueRuleIndex]
if (!vueRule) {
throw new Error(
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n` +
`Make sure there is at least one root-level rule that matches .vue or .vue.html files.`
)
}
if (vueRule.oneOf) {
throw new Error(
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
)
}
// get the normlized "use" for vue files
const vueUse = vueRule.use
// get vue-loader options
const vueLoaderUseIndex = vueUse.findIndex(u => {
return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
})
if (vueLoaderUseIndex < 0) {
throw new Error(
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n` +
`Make sure the rule matching .vue files include vue-loader in its use.`
)
}
// make sure vue-loader options has a known ident so that we can share
// options by reference in the template-loader by using a ref query like
// template-loader??vue-loader-options
const vueLoaderUse = vueUse[vueLoaderUseIndex]
vueLoaderUse.ident = 'vue-loader-options'
vueLoaderUse.options = vueLoaderUse.options || {}
// for each user rule (expect the vue rule), create a cloned rule
// that targets the corresponding language blocks in *.vue files.
const clonedRules = rules
.filter(r => r !== vueRule)
.map(cloneRule)
// global pitcher (responsible for injecting template compiler loader & CSS
// post loader)
const pitcher = {
loader: require.resolve('./loaders/pitcher'),
resourceQuery: query => {
const parsed = qs.parse(query.slice(1))
return parsed.vue != null
},
options: {
cacheDirectory: vueLoaderUse.options.cacheDirectory,
cacheIdentifier: vueLoaderUse.options.cacheIdentifier
}
}
// replace original rules
compiler.options.module.rules = [
pitcher,
...clonedRules,
...rules
]
}
}
function createMatcher (fakeFile) {
return (rule, i) => {
// #1201 we need to skip the `include` check when locating the vue rule
const clone = Object.assign({}, rule)
delete clone.include
const normalized = RuleSet.normalizeRule(clone, {}, '')
return (
!rule.enforce &&
normalized.resource &&
normalized.resource(fakeFile)
)
}
}
function cloneRule (rule) {
const { resource, resourceQuery } = rule
// Assuming `test` and `resourceQuery` tests are executed in series and
// synchronously (which is true based on RuleSet's implementation), we can
// save the current resource being matched from `test` so that we can access
// it in `resourceQuery`. This ensures when we use the normalized rule's
// resource check, include/exclude are matched correctly.
let currentResource
const res = Object.assign({}, rule, {
resource: {
test: resource => {
currentResource = resource
return true
}
},
resourceQuery: query => {
const parsed = qs.parse(query.slice(1))
if (parsed.vue == null) {
return false
}
if (resource && parsed.lang == null) {
return false
}
const fakeResourcePath = `${currentResource}.${parsed.lang}`
if (resource && !resource(fakeResourcePath)) {
return false
}
if (resourceQuery && !resourceQuery(query)) {
return false
}
return true
}
})
if (rule.rules) {
res.rules = rule.rules.map(cloneRule)
}
if (rule.oneOf) {
res.oneOf = rule.oneOf.map(cloneRule)
}
return res
}
VueLoaderPlugin.NS = NS
module.exports = VueLoaderPlugin
const qs = require('querystring')
const id = 'vue-loader-plugin'
const NS = 'vue-loader'
const BasicEffectRulePlugin = require('webpack/lib/rules/BasicEffectRulePlugin')
const BasicMatcherRulePlugin = require('webpack/lib/rules/BasicMatcherRulePlugin')
const RuleSetCompiler = require('webpack/lib/rules/RuleSetCompiler')
const UseEffectRulePlugin = require('webpack/lib/rules/UseEffectRulePlugin')
const objectMatcherRulePlugins = []
try {
const ObjectMatcherRulePlugin = require('webpack/lib/rules/ObjectMatcherRulePlugin')
objectMatcherRulePlugins.push(
new ObjectMatcherRulePlugin('assert', 'assertions'),
new ObjectMatcherRulePlugin('descriptionData')
)
} catch (e) {
const DescriptionDataMatcherRulePlugin = require('webpack/lib/rules/DescriptionDataMatcherRulePlugin')
objectMatcherRulePlugins.push(new DescriptionDataMatcherRulePlugin())
}
const ruleSetCompiler = new RuleSetCompiler([
new BasicMatcherRulePlugin('test', 'resource'),
new BasicMatcherRulePlugin('mimetype'),
new BasicMatcherRulePlugin('dependency'),
new BasicMatcherRulePlugin('include', 'resource'),
new BasicMatcherRulePlugin('exclude', 'resource', true),
new BasicMatcherRulePlugin('conditions'),
new BasicMatcherRulePlugin('resource'),
new BasicMatcherRulePlugin('resourceQuery'),
new BasicMatcherRulePlugin('resourceFragment'),
new BasicMatcherRulePlugin('realResource'),
new BasicMatcherRulePlugin('issuer'),
new BasicMatcherRulePlugin('compiler'),
...objectMatcherRulePlugins,
new BasicEffectRulePlugin('type'),
new BasicEffectRulePlugin('sideEffects'),
new BasicEffectRulePlugin('parser'),
new BasicEffectRulePlugin('resolve'),
new BasicEffectRulePlugin('generator'),
new UseEffectRulePlugin()
])
class VueLoaderPlugin {
apply (compiler) {
const normalModule = compiler.webpack
? compiler.webpack.NormalModule
: require('webpack/lib/NormalModule')
// add NS marker so that the loader can detect and report missing plugin
compiler.hooks.compilation.tap(id, compilation => {
const normalModuleLoader = normalModule.getCompilationHooks(compilation).loader
normalModuleLoader.tap(id, loaderContext => {
loaderContext[NS] = true
})
})
const rules = compiler.options.module.rules
let rawVueRules
let vueRules = []
for (const rawRule of rules) {
// skip rules with 'enforce'. eg. rule for eslint-loader
if (rawRule.enforce) {
continue
}
// skip the `include` check when locating the vue rule
const clonedRawRule = Object.assign({}, rawRule)
delete clonedRawRule.include
const ruleSet = ruleSetCompiler.compile([{
rules: [clonedRawRule]
}])
vueRules = ruleSet.exec({
resource: 'foo.vue'
})
if (!vueRules.length) {
vueRules = ruleSet.exec({
resource: 'foo.vue.html'
})
}
if (vueRules.length > 0) {
if (rawRule.oneOf) {
throw new Error(
`[VueLoaderPlugin Error] vue-loader 15 currently does not support vue rules with oneOf.`
)
}
rawVueRules = rawRule
break
}
}
if (!vueRules.length) {
throw new Error(
`[VueLoaderPlugin Error] No matching rule for .vue files found.\n` +
`Make sure there is at least one root-level rule that matches .vue or .vue.html files.`
)
}
// get the normlized "use" for vue files
const vueUse = vueRules.filter(rule => rule.type === 'use').map(rule => rule.value)
// get vue-loader options
const vueLoaderUseIndex = vueUse.findIndex(u => {
return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
})
if (vueLoaderUseIndex < 0) {
throw new Error(
`[VueLoaderPlugin Error] No matching use for vue-loader is found.\n` +
`Make sure the rule matching .vue files include vue-loader in its use.`
)
}
// make sure vue-loader options has a known ident so that we can share
// options by reference in the template-loader by using a ref query like
// template-loader??vue-loader-options
const vueLoaderUse = vueUse[vueLoaderUseIndex]
vueLoaderUse.ident = 'vue-loader-options'
vueLoaderUse.options = vueLoaderUse.options || {}
// for each user rule (expect the vue rule), create a cloned rule
// that targets the corresponding language blocks in *.vue files.
const refs = new Map()
const clonedRules = rules
.filter(r => r !== rawVueRules)
.map((rawRule) => cloneRule(rawRule, refs))
// fix conflict with config.loader and config.options when using config.use
delete rawVueRules.loader
delete rawVueRules.options
rawVueRules.use = vueUse
// global pitcher (responsible for injecting template compiler loader & CSS
// post loader)
const pitcher = {
loader: require.resolve('./loaders/pitcher'),
resourceQuery: query => {
if (!query) { return false }
const parsed = qs.parse(query.slice(1))
return parsed.vue != null
},
options: {
cacheDirectory: vueLoaderUse.options.cacheDirectory,
cacheIdentifier: vueLoaderUse.options.cacheIdentifier
}
}
// replace original rules
compiler.options.module.rules = [
pitcher,
...clonedRules,
...rules
]
}
}
let uid = 0
function cloneRule (rawRule, refs) {
const rules = ruleSetCompiler.compileRules(`clonedRuleSet-${++uid}`, [{
rules: [rawRule]
}], refs)
let currentResource
const conditions = rules[0].rules
.map(rule => rule.conditions)
// shallow flat
.reduce((prev, next) => prev.concat(next), [])
// do not process rule with enforce
if (!rawRule.enforce) {
const ruleUse = rules[0].rules
.map(rule => rule.effects
.filter(effect => effect.type === 'use')
.map(effect => effect.value)
)
// shallow flat
.reduce((prev, next) => prev.concat(next), [])
// fix conflict with config.loader and config.options when using config.use
delete rawRule.loader
delete rawRule.options
rawRule.use = ruleUse
}
const res = Object.assign({}, rawRule, {
resource: resources => {
currentResource = resources
return true
},
resourceQuery: query => {
if (!query) { return false }
const parsed = qs.parse(query.slice(1))
if (parsed.vue == null) {
return false
}
if (!conditions) {
return false
}
const fakeResourcePath = `${currentResource}.${parsed.lang}`
for (const condition of conditions) {
// add support for resourceQuery
const request = condition.property === 'resourceQuery' ? query : fakeResourcePath
if (condition && !condition.fn(request)) {
return false
}
}
return true
}
})
delete res.test
if (rawRule.rules) {
res.rules = rawRule.rules.map(rule => cloneRule(rule, refs))
}
if (rawRule.oneOf) {
res.oneOf = rawRule.oneOf.map(rule => cloneRule(rule, refs))
}
return res
}
VueLoaderPlugin.NS = NS
module.exports = VueLoaderPlugin
const webpack = require('webpack')
let VueLoaderPlugin = null
if (webpack.version && webpack.version[0] > 4) {
// webpack5 and upper
VueLoaderPlugin = require('./plugin-webpack5')
} else {
// webpack4 and lower
VueLoaderPlugin = require('./plugin-webpack4')
}
module.exports = VueLoaderPlugin
/* globals __VUE_SSR_CONTEXT__ */
// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
// This module is a runtime utility for cleaner component module output and will
// be included in the final webpack user bundle.
export default function normalizeComponent (
scriptExports,
render,
staticRenderFns,
functionalTemplate,
injectStyles,
scopeId,
moduleIdentifier, /* server only */
shadowMode, /* vue-cli only */
components, // fixed by xxxxxx auto components
) {
// Vue.extend constructor export interop
var options = typeof scriptExports === 'function'
? scriptExports.options
: scriptExports
// fixed by xxxxxx auto components
if (components) {
if (!options.components) {
options.components = {}
}
var hasOwn = Object.prototype.hasOwnProperty
for (var name in components) {
if (hasOwn.call(components, name) && !hasOwn.call(options.components, name)) {
options.components[name] = components[name]
}
}
}
// render functions
if (render) {
options.render = render
options.staticRenderFns = staticRenderFns
options._compiled = true
}
// functional template
if (functionalTemplate) {
options.functional = true
}
// scopedId
if (scopeId) {
options._scopeId = 'data-v-' + scopeId
}
var hook
if (moduleIdentifier) { // server build
hook = function (context) {
// 2.3 injection
context =
context || // cached call
(this.$vnode && this.$vnode.ssrContext) || // stateful
(this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
// 2.2 with runInNewContext: true
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
context = __VUE_SSR_CONTEXT__
}
// inject component styles
if (injectStyles) {
injectStyles.call(this, context)
}
// register component module identifier for async chunk inferrence
if (context && context._registeredComponents) {
context._registeredComponents.add(moduleIdentifier)
}
}
// used by ssr in case component is cached and beforeCreate
// never gets called
options._ssrRegister = hook
} else if (injectStyles) {
hook = shadowMode
? function () {
injectStyles.call(
this,
(options.functional ? this.parent : this).$root.$options.shadowRoot
)
}
: injectStyles
}
if (hook) {
if (options.functional) {
// for template-only hot-reload because in that case the render fn doesn't
// go through the normalizer
options._injectStyles = hook
// register for functional component in vue file
var originalRender = options.render
options.render = function renderWithStyleInjection (h, context) {
hook.call(context)
return originalRender(h, context)
}
} else {
// inject component registration as beforeCreate hook
var existing = options.beforeCreate
options.beforeCreate = existing
? [].concat(existing, hook)
: [hook]
}
}
return {
exports: scriptExports,
options: options
}
}
module.exports = function selectBlock (
descriptor,
loaderContext,
query,
appendExtension
) {
// template
if (query.type === `template`) {
if (appendExtension) {
loaderContext.resourcePath += '.' + (descriptor.template.lang || 'html')
}
loaderContext.callback(
null,
descriptor.template.content,
descriptor.template.map
)
return
}
// script
if (query.type === `script`) {
if (appendExtension) {
loaderContext.resourcePath += '.' + (descriptor.script.lang || 'js')
}
loaderContext.callback(
null,
descriptor.script.content,
descriptor.script.map
)
return
}
// styles
if (query.type === `style` && query.index != null) {
const style = descriptor.styles[query.index]
if (appendExtension) {
loaderContext.resourcePath += '.' + (style.lang || 'css')
}
loaderContext.callback(
null,
style.content,
style.map
)
return
}
// custom
if (query.type === 'custom' && query.index != null) {
const block = descriptor.customBlocks[query.index]
loaderContext.callback(
null,
block.content,
block.map
)
return
}
}
{
"name": "vue-loader",
"version": "15.9.8",
"description": "Vue single-file component loader for Webpack",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"test": "yarn lint && jest --env node",
"lint": "eslint lib test --fix",
"build": "webpack --config example/webpack.config.js --hide-modules",
"dev": "webpack-dev-server --config example/webpack.config.js --inline --hot",
"docs": "vuepress dev docs",
"docs:build": "vuepress build docs",
"prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s"
},
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/vue-loader/issues"
},
"homepage": "https://github.com/vuejs/vue-loader",
"repository": {
"type": "git",
"url": "https://github.com/vuejs/vue-loader.git"
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"lib/**/*.js": [
"eslint --fix",
"git add"
],
"test/**/*.js": [
"eslint --fix",
"git add"
]
},
"peerDependencies": {
"css-loader": "*",
"webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0"
},
"peerDependenciesMeta": {
"cache-loader": {
"optional": true
},
"vue-template-compiler": {
"optional": true
}
},
"dependencies": {
"@vue/component-compiler-utils": "^3.1.0",
"hash-sum": "^1.0.2",
"loader-utils": "^1.1.0",
"vue-hot-reload-api": "^2.3.0",
"vue-style-loader": "^4.1.0"
},
"devDependencies": {
"@types/webpack": "^4.4.27",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"cache-loader": "^2.0.1",
"conventional-changelog-cli": "^1.3.22",
"css-loader": "^1.0.0",
"eslint": "^4.19.0",
"eslint-plugin-vue-libs": "^2.1.0",
"file-loader": "^1.1.11",
"html-webpack-plugin": "4.0.0-beta.14",
"javascript-stringify": "^1.6.0",
"jest": "^23.5.0",
"jsdom": "^11.6.2",
"json": "^9.0.6",
"lint-staged": "^7.0.0",
"markdown-loader": "^2.0.2",
"memfs": "^3.1.2",
"mini-css-extract-plugin": "^0.4.1",
"node-sass": "^4.7.2",
"normalize-newline": "^3.0.0",
"null-loader": "^0.1.1",
"postcss-loader": "^2.1.2",
"pug": "^2.0.1",
"pug-plain-loader": "^1.0.0",
"raw-loader": "^0.5.1",
"sass-loader": "^6.0.7",
"source-map": "^0.5.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"sugarss": "^1.0.1",
"ts-loader": "^4.2.0",
"typescript": "^3.9.5",
"url-loader": "^1.0.1",
"vue": "^2.5.16",
"vue-server-renderer": "^2.5.16",
"vue-template-compiler": "^2.5.16",
"vuepress": "^0.14.2",
"vuepress-theme-vue": "^1.1.0",
"webpack": "^4.1.0",
"webpack-cli": "^3.2.0",
"webpack-dev-server": "^3.1.1",
"webpack-merge": "^4.1.2",
"yorkie": "^1.0.3"
}
}
The MIT License (MIT)
Copyright (c) 2015-present, songsiqi
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.
# Weex `<style>` Transformer
[![NPM version][npm-image]][npm-url]
[![Build status][circle-image]][circle-url]
[![Downloads][downloads-image]][downloads-url]
[npm-image]: https://img.shields.io/npm/v/weex-styler.svg?style=flat-square
[npm-url]: https://npmjs.org/package/weex-styler
[circle-image]: https://circleci.com/gh/alibaba/weex_toolchain.svg?style=svg
[circle-url]: https://circleci.com/gh/alibaba/weex_toolchain/tree/master
[downloads-image]: https://img.shields.io/npm/dm/weex-styler.svg?style=flat-square
[downloads-url]: https://npmjs.org/package/weex-styler
## Features
- convert a `<style>` element to JSON format
- autofix common mistakes
- friendly warnings
## API
- `parse(code, done)`
- `validate(json, done)`
- `validateItem(name, value)`
### util api
- `util.hyphenedToCamelCase(value)`
- `util.camelCaseToHyphened(value)`
```javascript
/**
* Parse `<style>` code to a JSON Object and log errors & warnings
*
* @param {string} code
* @param {function} done
*/
function parse(code, done) {}
/**
* Validate a JSON Object and log errors & warnings
*
* @param {object} json
* @param {function} done
*/
function validate(json, done) {}
/**
* Result callback
*
* data
* - jsonStyle{}: `classname.propname.value`-like object
* - log[{line, column, reason}]
*
* @param {Error} err
* @param {object} data
*/
function done(err, data) {}
/**
* Validate a single name-value pair
*
* @param {string} name camel cased
* @param {string} value
* @return {object}
* - value
* - log{reason}
*/
function validateItem(name, value) {}
```
## Validation
- rule check: only common rule type supported, othres will be ignored
- selector check: only single-classname selector is supported, others will be ignored
- prop name check: out-of-defined prop name will be warned but preserved
- prop value check: common prop value mistakes will be autofixed or ignored
+ color type: keywords, `#xxx` -> warning: `#xxxxxx`
+ color type: `transparent` -> error: not supported
+ length type: `100px` -> warning: `100`
## Demo
```javascript
var styler = require('weex-styler')
var code = 'html {color: #000000;} .foo {color: red; -webkit-transform: rotate(90deg); width: 200px;}'
styler.parse(code, function (err, data) {
// syntax error
// format: {line, column, reason, ...}
err
// result
// {foo: {color: '#ff0000', webkitTransform: 'rotate(90deg)', width: 200}}
data.jsonStyle
// format: {line, column, reason}
// - Error: Selector `html` is not supported. Weex only support single-classname selector
// - Warning: prop value `red` is autofixed to `#ff0000`
// - Warning: prop name `-webkit-transform` is not supported
// - Warning: prop value `200px` is autofixed to `200`
data.log[]
})
var jsonStyle = {
foo: {
color: 'red',
webkitTransform: 'rotate(90deg)',
width: '200px'
}
}
styler.validate(json, function (err, data) {
// syntax error
err
// result
// {foo: {color: '#ff0000', webkitTransform: 'rotate(90deg)', width: 200}}
data.jsonStyle
// format: {reason}
// - Warning: prop value `red` is autofixed to `#ff0000`
// - Warning: prop name `-webkit-transform` is not supported
// - Warning: prop value `200px` is autofixed to `200`
data.log[]
})
```
var gulp = require('gulp')
var jscs = require('gulp-jscs')
var mocha = require('gulp-mocha')
gulp.task('default', ['test'], function() {
console.log('done')
})
gulp.task('watch', function () {
return gulp.watch('**/*.js', ['test'])
})
gulp.task('test', ['jscs', 'mocha'], function () {
console.log('test done')
})
gulp.task('mocha', function () {
return gulp.src([
'test/*.js'
]).pipe(mocha())
})
gulp.task('jscs', function () {
return gulp.src('**/*.js').pipe(jscs())
})
'use strict'
var css = require('css')
var util = require('./lib/util')
var validateItem = require('./lib/validator').validate
var shorthandParser = require('./lib/shorthand-parser')
// padding & margin shorthand parsing
function convertLengthShorthand (rule, prop) {
for (var i = 0; i < rule.declarations.length; i++) {
var declaration = rule.declarations[i]
if (declaration.property === prop) {
var values = declaration.value.split(/\s+/)
// values[0] = values[0] || 0
values[1] = values[1] || values[0]
values[2] = values[2] || values[0]
values[3] = values[3] || values[1]
rule.declarations.splice(i, 1)
rule.declarations.splice(i, 0, { type: 'declaration', property: prop + '-left', value: values[3], position: declaration.position })
rule.declarations.splice(i, 0, { type: 'declaration', property: prop + '-bottom', value: values[2], position: declaration.position })
rule.declarations.splice(i, 0, { type: 'declaration', property: prop + '-right', value: values[1], position: declaration.position })
rule.declarations.splice(i, 0, { type: 'declaration', property: prop + '-top', value: values[0], position: declaration.position })
// break
}
}
}
/**
* mergeStyle
* @param {*} object
* @param {*} classNames
* @param {*} preClassNames
* @param {*} ruleResult
* @param {*} prop
* @param {*} index
*/
function mergeStyle (object, classNames, preClassNames, ruleResult, prop, index) {
if (process.env.UNI_NVUE_STYLE_COMPILER !== 'uni-app') {
object[classNames] = object[classNames] || {}
object[classNames][prop] = ruleResult[prop]
return
}
classNames = classNames.split('.').map(str => '.' + str).slice(1)
var className = classNames.find(className => className in object) || classNames[0]
// 假设选择器已经去重简化
preClassNames += classNames.filter(str => str !== className).sort().join('')
var rules = object[className] = object[className] || {}
var style = rules[preClassNames] = rules[preClassNames] || {}
// 增加其他权重信息
style[prop] = [...ruleResult[prop], preClassNames.split('.').length - 1, index]
}
/**
* Parse `<style>` code to a JSON Object and log errors & warnings
*
* @param {string} code
* @param {function} done which will be called with
* - err:Error
* - data.jsonStyle{}: `classname.propname.value`-like object
* - data.log[{line, column, reason}]
*/
function parse (code, done) {
var ast, err, jsonStyle = {}, log = []
// css parse
ast = css.parse(code, { silent: true })
// catch syntax error
if (ast.stylesheet.parsingErrors && ast.stylesheet.parsingErrors.length) {
err = ast.stylesheet.parsingErrors
err.forEach(function (error) {
log.push({ line: error.line, column: error.column, reason: error.toString().replace('Error', 'ERROR') })
})
}
// walk all
/* istanbul ignore else */
if (ast && ast.type === 'stylesheet' && ast.stylesheet &&
ast.stylesheet.rules && ast.stylesheet.rules.length) {
ast.stylesheet.rules.forEach(function (rule, index) {
var type = rule.type
var ruleResult = {}
var ruleLog = []
if (type === 'rule') {
if (rule.declarations && rule.declarations.length) {
rule.declarations = shorthandParser(rule.declarations)
// padding & margin shorthand parsing
convertLengthShorthand(rule, 'padding')
convertLengthShorthand(rule, 'margin')
rule.declarations.forEach(function (declaration) {
var subType = declaration.type
var name, value, line, column, subResult, camelCasedName
/* istanbul ignore if */
if (subType !== 'declaration') {
return
}
name = declaration.property
value = declaration.value
var newValue = value.replace(/\s*!important/g, '')
var importantWeight = Number(value !== newValue)
value = newValue
// validate declarations and collect them to result
camelCasedName = util.hyphenedToCamelCase(name)
subResult = validateItem(camelCasedName, value)
/* istanbul ignore else */
if (typeof subResult.value === 'number' || typeof subResult.value === 'string') {
if (process.env.UNI_NVUE_STYLE_COMPILER === 'uni-app') {
var oldValue = ruleResult[camelCasedName]
// 增加 important 权重信息
ruleResult[camelCasedName] = Array.isArray(oldValue) && oldValue[1] > importantWeight ? oldValue : [subResult.value, importantWeight]
} else {
ruleResult[camelCasedName] = subResult.value
}
}
if (subResult.log) {
subResult.log.line = declaration.position.start.line
subResult.log.column = declaration.position.start.column
ruleLog.push(subResult.log)
}
})
rule.selectors.forEach(function (selector) {
selector = selector.replace(/\s*([\+\~\>])\s*/g, '$1').replace(/\s+/, ' ')
// 支持组合选择器
const res = selector.match(process.env.UNI_NVUE_STYLE_COMPILER === 'uni-app' ? /^((?:(?:\.[A-Za-z0-9_\-]+)+[\+\~\> ])*)((?:\.[A-Za-z0-9_\-\:]+)+)$/ : /^(\.)([A-Za-z0-9_\-:]+)$/)
if (res) {
var preClassNames = res[1]
var classNames = res[2]
// handle pseudo class
var pseudoIndex = classNames.indexOf(':')
if (pseudoIndex > -1) {
var pseudoCls = classNames.slice(pseudoIndex)
classNames = classNames.slice(0, pseudoIndex)
var pseudoRuleResult = {}
Object.keys(ruleResult).forEach(function (prop) {
pseudoRuleResult[prop + pseudoCls] = ruleResult[prop]
})
ruleResult = pseudoRuleResult
}
// merge style
Object.keys(ruleResult).forEach(function (prop) {
// // handle transition
// if (prop.indexOf('transition') === 0 && prop !== 'transition') {
// var realProp = prop.replace('transition', '')
// realProp = realProp[0].toLowerCase() + realProp.slice(1)
// var object = jsonStyle['@TRANSITION'] = jsonStyle['@TRANSITION'] || {}
// mergeStyle(object, classNames, preClassNames, ruleResult, prop, index)
// }
mergeStyle(jsonStyle, classNames, preClassNames, ruleResult, prop, index)
})
}
else {
log.push({
line: rule.position.start.line,
column: rule.position.start.column,
reason: 'ERROR: Selector `' + selector + '` is not supported. Weex only support classname selector'
})
}
})
log = log.concat(ruleLog)
}
}
/* istanbul ignore else */
else if (type === 'font-face') {
/* istanbul ignore else */
if (rule.declarations && rule.declarations.length) {
rule.declarations.forEach(function (declaration) {
/* istanbul ignore if */
if (declaration.type !== 'declaration') {
return
}
var name = util.hyphenedToCamelCase(declaration.property)
var value = declaration.value
if (name === 'fontFamily' && '\"\''.indexOf(value[0]) > -1) { // FIXME: delete leading and trailing quotes
value = value.slice(1, value.length - 1)
}
ruleResult[name] = value
})
if (!jsonStyle['@FONT-FACE']) {
jsonStyle['@FONT-FACE'] = []
}
jsonStyle['@FONT-FACE'].push(ruleResult)
}
}
})
}
jsonStyle['@VERSION'] = 2
done(err, { jsonStyle: jsonStyle, log: log })
}
/**
* Validate a JSON Object and log errors & warnings
*
* @param {object} json
* @param {function} done which will be called with
* - err:Error
* - data.jsonStyle{}: `classname.propname.value`-like object
* - data.log[{reason}]
*/
function validate (json, done) {
var log = []
var err
try {
json = JSON.parse(JSON.stringify(json))
}
catch (e) {
err = e
json = {}
}
Object.keys(json).forEach(function (selector) {
var declarations = json[selector]
Object.keys(declarations).forEach(function (name) {
var value = declarations[name]
var result = validateItem(name, value)
if (typeof result.value === 'number' || typeof result.value === 'string') {
declarations[name] = result.value
}
else {
delete declarations[name]
}
if (result.log) {
log.push(result.log)
}
})
})
done(err, {
jsonStyle: json,
log: log
})
}
module.exports = {
parse: parse,
validate: validate,
validateItem: validateItem,
util: util
}
function generateDeclaration (property, value, important, position) {
return {
type: 'declaration',
property,
value: value + (important ? ' !important' : ''),
position
}
}
function clearImportant (value) {
var newValue = value.replace(/\s*!important/g, '')
return {
value: newValue,
important: value !== newValue
}
}
function transition (declaration) {
var CHUNK_REGEXP = /^(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?\s*(\S*)?\s*(\d*\.?\d+(?:ms|s)?)?$/
var { value, important } = clearImportant(declaration.value)
var match = value.match(CHUNK_REGEXP)
var result = []
var position = declaration.position
match[1] && result.push(generateDeclaration('transition-property', match[1], important, position))
match[2] && result.push(generateDeclaration('transition-duration', match[2], important, position))
match[3] && result.push(generateDeclaration('transition-timing-function', match[3], important, position))
match[4] && result.push(generateDeclaration('transition-delay', match[4], important, position))
return result
}
function margin (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
var splitResult = value.split(/\s+/)
var result = []
switch (splitResult.length) {
case 1:
splitResult.push(splitResult[0], splitResult[0], splitResult[0])
break
case 2:
splitResult.push(splitResult[0], splitResult[1])
break
case 3:
splitResult.push(splitResult[1])
break
}
result.push(
generateDeclaration('margin-top', splitResult[0], important, position),
generateDeclaration('margin-right', splitResult[1], important, position),
generateDeclaration('margin-bottom', splitResult[2], important, position),
generateDeclaration('margin-left', splitResult[3], important, position)
)
return result
}
function border (declaration) {
var { value, important } = clearImportant(declaration.value)
var property = declaration.property
var position = declaration.position
var splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/)
var result = [/^[\d\.]+\S*$/, /^(solid|dashed|dotted)$/, /\S+/].map(item => {
var index = splitResult.findIndex(str => item.test(str))
return index < 0 ? null : splitResult.splice(index, 1)[0]
})
if (splitResult.length) {
return declaration
}
return [
generateDeclaration(property + '-width', (result[0] || '0').trim(), important, position),
generateDeclaration(property + '-style', (result[1] || 'solid').trim(), important, position),
generateDeclaration(property + '-color', (result[2] || '#000000').trim(), important, position)
]
}
function borderProperty (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
var property = declaration.property.split('-')[1]
var splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/)
var result = []
switch (splitResult.length) {
case 1:
return declaration
case 2:
splitResult.push(splitResult[0], splitResult[1])
break
case 3:
splitResult.push(splitResult[1])
break
}
result.push(
generateDeclaration('border-top-' + property, splitResult[0], important, position),
generateDeclaration('border-right-' + property, splitResult[1], important, position),
generateDeclaration('border-bottom-' + property, splitResult[2], important, position),
generateDeclaration('border-left-' + property, splitResult[3], important, position)
)
return result
}
function borderRadius (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
var splitResult = value.split(/\s+/)
var result = []
if (value.includes('/')) {
return declaration
}
switch (splitResult.length) {
case 1:
return declaration
case 2:
splitResult.push(splitResult[0], splitResult[1])
break
case 3:
splitResult.push(splitResult[1])
break
}
result.push(
generateDeclaration('border-top-left-radius', splitResult[0], important, position),
generateDeclaration('border-top-right-radius', splitResult[1], important, position),
generateDeclaration('border-bottom-right-radius', splitResult[2], important, position),
generateDeclaration('border-bottom-left-radius', splitResult[3], important, position)
)
return result
}
function flexFlow (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
var splitResult = value.split(/\s+/)
var result = [/^(column|column-reverse|row|row-reverse)$/, /^(nowrap|wrap|wrap-reverse)$/].map(item => {
var index = splitResult.findIndex(str => item.test(str))
return index < 0 ? null : splitResult.splice(index, 1)[0]
})
if (splitResult.length) {
return declaration
}
return [
generateDeclaration('flex-direction', result[0] || 'column', important, position),
generateDeclaration('flex-wrap', result[1] || 'nowrap', important, position)
]
}
function font (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
var splitResult = value.replace(/,\s*/g, ',').replace(/\s*\/\s*/, '/').replace(/['"].+?['"]/g, str => str.replace(/\s+/g, '#')).split(/\s+/)
var result = []
var styleValues = ['normal', 'italic', 'oblique']
result.push(generateDeclaration('font-style', styleValues[Math.max(0, styleValues.indexOf(splitResult[0]))], important, position))
var weight = splitResult.slice(0, -2).find(str => /normal|bold|lighter|bolder|\d+/.test(str))
if (weight) {
result.push(generateDeclaration('font-weight', weight, important, position))
}
splitResult = splitResult.slice(-2)
if (/[\d\.]+\S*(\/[\d\.]+\S*)?/.test(splitResult[0])) {
var [size, height] = splitResult[0].split('/')
result.push(
generateDeclaration('font-size', size, important, position),
generateDeclaration('line-height', height || 'normal', important, position),
generateDeclaration('font-family', splitResult[1].replace(/#/g, ' '), important, position)
)
return result
}
return []
}
function background (declaration) {
var { value, important } = clearImportant(declaration.value)
var position = declaration.position
if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) {
return generateDeclaration('background-color', value, important, position)
} else if (/^linear-gradient(.+)$/.test(value)) {
return generateDeclaration('background-image', value, important, position)
} else {
return declaration
}
}
var parserCollection = {
transition,
margin,
border,
'border-top': border,
'border-right': border,
'border-bottom': border,
'border-left': border,
'border-style': borderProperty,
'border-width': borderProperty,
'border-color': borderProperty,
'border-radius': borderRadius,
'flex-flow': flexFlow,
font,
background
}
module.exports = function (declarations) {
return declarations.reduce((result, declaration) => {
var parser = parserCollection[declaration.property]
if (parser) {
return result.concat(parser(declaration))
} else {
result.push(declaration)
return result
}
}, [])
}
/**
* rules:
* - abc-def -> abcDef
* - -abc-def -> AbcDef
*
* @param {string} value
* @return {string}
*/
exports.hyphenedToCamelCase = function hyphenedToCamelCase(value) {
return value.replace(/-([a-z])/g, function(s, m) {
return m.toUpperCase()
})
}
/**
* rules:
* - abcDef -> abc-def
* - AbcDef -> -abc-def
*
* @param {string} value
* @return {string}
*/
exports.camelCaseToHyphened = function camelCaseToHyphened(value) {
return value.replace(/([A-Z])/g, function(s, m) {
if (typeof m === 'string') {
return '-' + m.toLowerCase()
}
return m
})
}
{
"_args": [
[
"weex-styler@0.3.1",
"/Users/fxy/Documents/DCloud/HBuilderX/uniapp-cli"
]
],
"_development": true,
"_from": "weex-styler@0.3.1",
"_id": "weex-styler@0.3.1",
"_inBundle": false,
"_integrity": "sha512-xkX5/wS/QLiJXKwbdpeytbLN0kHviQwj9CLdvBxqu+RRZABZpTniKZr1oxjh9Q0+n/aRC+smwFpQpUKvXh9V1g==",
"_location": "/weex-styler",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "weex-styler@0.3.1",
"name": "weex-styler",
"escapedName": "weex-styler",
"rawSpec": "0.3.1",
"saveSpec": null,
"fetchSpec": "0.3.1"
},
"_requiredBy": [
"/weex-vue-loader"
],
"_resolved": "https://registry.npmjs.org/weex-styler/-/weex-styler-0.3.1.tgz",
"_spec": "0.3.1",
"_where": "/Users/fxy/Documents/DCloud/HBuilderX/uniapp-cli",
"author": {
"name": "songsiqi",
"email": "songsiqi2006@126.com"
},
"bugs": {
"url": "https://github.com/weexteam/weex-styler/issues"
},
"dependencies": {
"css": "~2.2.1"
},
"description": "Weex <style> transformer",
"devDependencies": {
"chai": "~3.4.1",
"gulp": "~3.9.0",
"gulp-jscs": "~3.0.2",
"gulp-mocha": "~2.2.0",
"isparta": "~4.0.0",
"sinon": "~1.17.2",
"sinon-chai": "~2.8.0"
},
"homepage": "https://github.com/weexteam/weex-styler#readme",
"keywords": [
"weex"
],
"license": "MIT",
"main": "index.js",
"name": "weex-styler",
"repository": {
"type": "git",
"url": "git+https://github.com/weexteam/weex-styler.git"
},
"scripts": {
"cover": "node node_modules/isparta/bin/isparta cover node_modules/gulp-mocha/node_modules/.bin/_mocha -- --reporter dot",
"test": "gulp test && npm run cover"
},
"version": "0.3.1"
}
var chai = require('chai')
var sinon = require('sinon')
var sinonChai = require('sinon-chai')
var expect = chai.expect
chai.use(sinonChai)
var styler = require('../')
describe('parse', function () {
it('parse normal style code', function (done) {
var code = 'html {color: #000000;}\n\n.foo {color: red; background-color: rgba(255,255,255,0.6); -webkit-transform: rotate(90deg); width: 200px; left: 0; right: 0px; border-width: 1pt; font-weight: 100}\n\n.bar {background: red}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({foo: {color: '#FF0000', backgroundColor: 'rgba(255,255,255,0.6)', WebkitTransform: 'rotate(90deg)', width: '200px', left: 0, right: '0px', borderWidth: '1pt', fontWeight: '100'}, bar: {background: 'red'}})
expect(data.log).eql([
{line: 1, column: 1, reason: 'ERROR: Selector `html` is not supported. Weex only support single-classname selector'},
{line: 3, column: 7, reason: 'NOTE: property value `red` is autofixed to `#FF0000`'},
{line: 3, column: 60, reason: 'WARNING: `-webkit-transform` is not a standard property name (may not be supported)'},
{line: 5, column: 7, reason: 'WARNING: `background` is not a standard property name (may not be supported), suggest `background-color`'}
])
done()
})
})
it('parse and fix prop value', function (done) {
var code = '.foo {font-size: 200px;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({foo: {fontSize: '200px'}})
done()
})
})
it('parse and ensure number type value', function (done) {
var code = '.foo {line-height: 40;}\n\n .bar {line-height: 20px;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({foo: {lineHeight: 40}, bar: {lineHeight: '20px'}})
done()
})
})
it('handle complex class definition', function (done) {
var code = '.foo, .bar {font-size: 20;}\n\n .foo {color: #ff5000;}\n\n .bar {color: #000000;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({
foo: {fontSize: 20, color: '#ff5000'},
bar: {fontSize: 20, color: '#000000'}
})
done()
})
})
it('handle more complex class definition', function (done) {
var code = '.foo, .bar {font-size: 20; color: #000000}\n\n .foo, .bar, .baz {color: #ff5000; height: 30;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({
foo: {fontSize: 20, color: '#ff5000', height: 30},
bar: {fontSize: 20, color: '#ff5000', height: 30},
baz: {color: '#ff5000', height: 30}
})
done()
})
})
it('parse transition', function (done) {
var code = '.foo {transition-property: margin-top; transition-duration: 300ms; transition-delay: 0.2s; transition-timing-function: ease-in;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({foo: {property: 'marginTop', duration: 300, delay: 200, timingFunction: 'ease-in'}})
expect(data.jsonStyle.foo).eql({
transitionDelay: 200,
transitionDuration: 300,
transitionProperty: "marginTop",
transitionTimingFunction: "ease-in"
})
expect(data.log).eql([
{line: 1, column: 40, reason: 'NOTE: property value `300ms` is autofixed to `300`'},
{line: 1, column: 68, reason: 'NOTE: property value `0.2s` is autofixed to `200`'}
])
done()
})
})
it('parse transition transform', function (done) {
var code = '.foo {transition-property: transform; transition-duration: 300ms; transition-delay: 0.2s; transition-timing-function: ease-in-out;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({foo: {property: 'transform', duration: 300, delay: 200, timingFunction: 'ease-in-out'}})
expect(data.jsonStyle.foo).eql({
transitionDelay: 200,
transitionDuration: 300,
transitionProperty: "transform",
transitionTimingFunction: "ease-in-out"
})
done()
})
})
it('parse multi transition properties', function (done) {
var code = '.foo {transition-property: margin-top, height; transition-duration: 300ms; transition-delay: 0.2s; transition-timing-function: ease-in-out;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({foo: {property: 'marginTop,height', duration: 300, delay: 200, timingFunction: 'ease-in-out'}})
expect(data.jsonStyle.foo).eql({
transitionDelay: 200,
transitionDuration: 300,
transitionProperty: "marginTop,height",
transitionTimingFunction: "ease-in-out"
})
done()
})
})
it('parse complex transition', function (done) {
var code = '.foo {font-size: 20; color: #000000}\n\n .foo, .bar {color: #ff5000; height: 30; transition-property: margin-top; transition-duration: 300ms; transition-delay: 0.2s; transition-timing-function: ease-in;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({
foo: {property: 'marginTop', duration: 300, delay: 200, timingFunction: 'ease-in'},
bar: {property: 'marginTop', duration: 300, delay: 200, timingFunction: 'ease-in'}
})
expect(data.jsonStyle.foo).eql({
fontSize: 20, color: '#ff5000', height: 30,
transitionDelay: 200,
transitionDuration: 300,
transitionProperty: "marginTop",
transitionTimingFunction: "ease-in"
})
expect(data.jsonStyle.bar).eql({
color: '#ff5000', height: 30,
transitionDelay: 200,
transitionDuration: 300,
transitionProperty: "marginTop",
transitionTimingFunction: "ease-in"
})
expect(data.log).eql([
{line: 3, column: 75, reason: 'NOTE: property value `300ms` is autofixed to `300`'},
{line: 3, column: 103, reason: 'NOTE: property value `0.2s` is autofixed to `200`'}
])
done()
})
})
it('parse transition shorthand', function (done) {
var code = '.foo {font-size: 20; transition: margin-top 500ms ease-in-out 1s}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({foo: {property: 'marginTop', duration: 500, delay: 1000, timingFunction: 'ease-in-out' }})
expect(data.jsonStyle.foo).eql({
fontSize: 20,
transitionDelay: 1000,
transitionDuration: 500,
transitionProperty: "marginTop",
transitionTimingFunction: "ease-in-out"
})
expect(data.log).eql([
{line: 1, column: 22, reason: 'NOTE: property value `500ms` is autofixed to `500`'},
{line: 1, column: 22, reason: 'NOTE: property value `1s` is autofixed to `1000`'}
])
done()
})
})
it.skip('override transition shorthand', function (done) {
var code = '.foo {font-size: 32px; transition: margin-top 500ms ease-in-out 1s; transition-duration: 300ms}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle['@TRANSITION']).eql({foo: {property: 'marginTop', duration: 300, delay: 1000, timingFunction: 'ease-in-out' }})
expect(data.jsonStyle.foo).eql({
fontSize: 32,
transitionDelay: 1000,
transitionDuration: 300,
transitionProperty: "marginTop",
transitionTimingFunction: "ease-in-out"
})
done()
})
})
it('parse padding & margin shorthand', function (done) {
var code = '.foo { padding: 20px; margin: 30px 40; } .bar { margin: 10px 20 30; padding: 10 20px 30px 40;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle.foo).eql({
paddingTop: '20px',
paddingRight: '20px',
paddingBottom: '20px',
paddingLeft: '20px',
marginTop: '30px',
marginRight: 40,
marginBottom: '30px',
marginLeft: 40
})
expect(data.jsonStyle.bar).eql({
paddingTop: 10,
paddingRight: '20px',
paddingBottom: '30px',
paddingLeft: 40,
marginTop: '10px',
marginRight: 20,
marginBottom: 30,
marginLeft: 20
})
done()
})
})
it('override padding & margin shorthand', function (done) {
var code = '.foo { padding: 20px; padding-left: 30px; } .bar { margin: 10px 20; margin-bottom: 30px;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle.foo).eql({
paddingTop: '20px',
paddingRight: '20px',
paddingBottom: '20px',
paddingLeft: '30px'
})
expect(data.jsonStyle.bar).eql({
marginTop: '10px',
marginRight: 20,
marginBottom: '30px',
marginLeft: 20
})
done()
})
})
it('handle pseudo class', function (done) {
var code = '.class-a {color: #0000ff;} .class-a:last-child:focus {color: #ff0000;}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({
'class-a': {
color: '#0000ff',
'color:last-child:focus': '#ff0000'
}
})
done()
})
})
it('handle iconfont', function (done) {
var code = '@font-face {font-family: "font-family-name-1"; src: url("font file url 1-1") format("truetype");} @font-face {font-family: "font-family-name-2"; src: url("font file url 2-1") format("truetype"), url("font file url 2-2") format("woff");}'
styler.parse(code, function (err, data) {
expect(err).is.undefined
expect(data).is.an.object
expect(data.jsonStyle).eql({
'@FONT-FACE': [
{fontFamily: 'font-family-name-1', src: 'url("font file url 1-1") format("truetype")'},
{fontFamily: 'font-family-name-2', src: 'url("font file url 2-1") format("truetype"), url("font file url 2-2") format("woff")'}
]
})
done()
})
})
it('handle syntax error', function (done) {
var code = 'asdf'
styler.parse(code, function (err, data) {
expect(err).is.an.array
expect(err[0].toString()).eql('Error: undefined:1:5: missing \'{\'')
expect(err[0].reason).eql('missing \'{\'')
expect(err[0].filename).eql(undefined)
expect(err[0].line).eql(1)
expect(err[0].column).eql(5)
expect(err[0].source).eql('')
expect(data.log).eql([{line: 1, column: 5, reason: 'ERROR: undefined:1:5: missing \'{\''}])
done()
})
})
})
# weex-template-compiler
> This package is auto-generated. For pull requests please see [src/platforms/weex/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/weex/entry-compiler.js).
try {
var vueVersion = require('weex-vue-framework').version
} catch (e) {}
var packageName = require('./package.json').name
var packageVersion = require('./package.json').version
if (vueVersion && vueVersion !== packageVersion) {
throw new Error(
'\n\nVue packages version mismatch:\n\n' +
'- vue@' + vueVersion + '\n' +
'- ' + packageName + '@' + packageVersion + '\n\n' +
'This may cause things to work incorrectly. Make sure to use the same version for both.\n' +
'If you are using weex-vue-loader, re-installing them should bump ' + packageName + ' to the latest.\n'
)
}
module.exports = require('./build')
此差异已折叠。
/**
* 注意:该包的依赖包含了 lib 中三方包的依赖
*/
export * from './webpack'
export { default as plugins } from './plugin'
此差异已折叠。
export interface NVueCompilerOptions {}
export * from './path'
export * from './style'
import path from 'path'
export function resolveLib(filepath: string) {
return path.resolve(__dirname, '../../lib', filepath)
}
此差异已折叠。
此差异已折叠。
import moduleAlias from 'module-alias'
import { resolveLib } from '../utils'
const MODULES = [
'weex-styler',
'weex-template-compiler',
'@vue/component-compiler-utils',
'@vue/component-compiler-utils/package.json',
]
export function initModuleAlias() {
MODULES.forEach((name) => moduleAlias.addAlias(name, resolveLib(name)))
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册