├── .circleci
└── config.yml
├── .github
└── ISSUE_TEMPLATE
│ ├── Bug_report.md
│ └── Feature_request.md
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs
├── assets
│ ├── css
│ │ ├── main.css
│ │ └── main.css.map
│ ├── images
│ │ ├── icons.png
│ │ ├── icons@2x.png
│ │ ├── widgets.png
│ │ └── widgets@2x.png
│ └── js
│ │ ├── main.js
│ │ └── search.js
├── classes
│ ├── _index_.componentrewriter.html
│ ├── _index_.filerewriter.html
│ ├── _index_.messagebag.html
│ ├── _plugins_babel_.babelplugin.html
│ ├── _plugins_css_.cssplugin.html
│ ├── _plugins_less_.lessplugin.html
│ ├── _plugins_sass_.sassplugin.html
│ ├── _plugins_template_.templateplugin.html
│ └── _plugins_typescript_.typescriptplugin.html
├── globals.html
├── index.html
├── interfaces
│ ├── _api_.blockplugin.html
│ ├── _api_.compileroptions.html
│ ├── _api_.compilerresult.html
│ ├── _api_.componentrewriterapi.html
│ ├── _api_.customblock.html
│ ├── _api_.customfile.html
│ ├── _api_.fileplugin.html
│ ├── _api_.filerewriterapi.html
│ ├── _api_.messagebagapi.html
│ └── _api_.processedresults.html
└── modules
│ ├── _api_.html
│ ├── _bin_vuec_.html
│ ├── _index_.html
│ ├── _plugins_babel_.html
│ ├── _plugins_css_.html
│ ├── _plugins_index_.html
│ ├── _plugins_less_.html
│ ├── _plugins_sass_.html
│ ├── _plugins_template_.html
│ ├── _plugins_typescript_.html
│ └── _utils_.html
├── jest.config.js
├── package.json
├── pnpmfile.js
├── shrinkwrap.yaml
├── src
├── api.ts
├── bin
│ └── vuec.ts
├── index.ts
├── plugins
│ ├── babel.ts
│ ├── css.ts
│ ├── index.ts
│ ├── less.ts
│ ├── sass.ts
│ ├── template.ts
│ └── typescript.ts
└── utils.ts
├── test
├── fixtures
│ ├── WithCss.vue
│ ├── WithCssImport.vue
│ ├── WithLess.vue
│ ├── WithLessImport.vue
│ ├── WithSass.vue
│ ├── WithSassImport.vue
│ ├── WithScript.vue
│ ├── WithScriptFunctional.vue
│ ├── WithScriptImport.vue
│ ├── WithScss.vue
│ ├── WithScssImport.vue
│ ├── WithTemplate.vue
│ ├── WithTemplateImport.vue
│ ├── WithTypescript.vue
│ ├── WithTypescriptImport.vue
│ ├── import-less.less
│ ├── import-sass.sass
│ ├── import-scss.scss
│ ├── import.css
│ ├── script.js
│ ├── style-less.less
│ ├── style-sass.sass
│ ├── style-scss.scss
│ ├── style.css
│ ├── template.html
│ └── typescript.ts
├── utils.spec.ts
└── vue.spec.ts
├── tsconfig.json
└── typedoc.js
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # Javascript Node CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details
4 | #
5 | version: 2
6 | jobs:
7 | build:
8 | docker:
9 | - image: circleci/node:6
10 |
11 | working_directory: ~/repo
12 |
13 | steps:
14 | - checkout
15 |
16 | - run:
17 | name: update-pnpm
18 | command: 'curl -L https://unpkg.com/@pnpm/self-installer | sudo node'
19 |
20 | # Download and cache dependencies
21 | - restore_cache:
22 | keys:
23 | - v1-dependencies-{{ checksum "package.json" }}
24 | # fallback to using the latest cache if no exact match is found
25 | - v1-dependencies-
26 |
27 | # Install dependencies.
28 | - run: pnpm install
29 |
30 | # Delete unused packages
31 | - run: pnpm store prune
32 |
33 | - save_cache:
34 | paths:
35 | - node_modules
36 | - ~/.pnpm-store
37 | key: v1-dependencies-{{ checksum "package.json" }}
38 |
39 | # run tests!
40 | - run: pnpm test -- --ci --coverage
41 |
42 | # publish coverage reports
43 | - run: cat coverage/lcov.info | pnpm run coveralls
44 |
45 | - store_test_results:
46 | path: coverage
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1.
13 |
14 | **Expected behavior**
15 | A clear and concise description of what you expected to happen.
16 |
17 | **Screenshots/Stack Trace**
18 | Add screenshots or stack trace to help explain your problem.
19 |
20 | **Environment:**
21 | - OS:
22 | - Node Version:
23 |
24 | **Additional context**
25 | Add any other context about the problem here.
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 |
3 | # Created by .ignore support plugin (hsz.mobi)
4 | ### macOS template
5 | # General
6 | .DS_Store
7 | .AppleDouble
8 | .LSOverride
9 |
10 | # Icon must end with two \r
11 | Icon
12 |
13 | # Thumbnails
14 | ._*
15 |
16 | # Files that might appear in the root of a volume
17 | .DocumentRevisions-V100
18 | .fseventsd
19 | .Spotlight-V100
20 | .TemporaryItems
21 | .Trashes
22 | .VolumeIcon.icns
23 | .com.apple.timemachine.donotpresent
24 |
25 | # Directories potentially created on remote AFP share
26 | .AppleDB
27 | .AppleDesktop
28 | Network Trash Folder
29 | Temporary Items
30 | .apdisk
31 | ### Node template
32 | # Logs
33 | logs
34 | *.log
35 | npm-debug.log*
36 | yarn-debug.log*
37 | yarn-error.log*
38 |
39 | # Runtime data
40 | pids
41 | *.pid
42 | *.seed
43 | *.pid.lock
44 |
45 | # Directory for instrumented libs generated by jscoverage/JSCover
46 | lib-cov
47 |
48 | # Coverage directory used by tools like istanbul
49 | coverage
50 |
51 | # nyc test coverage
52 | .nyc_output
53 |
54 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
55 | .grunt
56 |
57 | # Bower dependency directory (https://bower.io/)
58 | bower_components
59 |
60 | # node-waf configuration
61 | .lock-wscript
62 |
63 | # Compiled binary addons (https://nodejs.org/api/addons.html)
64 | build/Release
65 |
66 | # Dependency directories
67 | node_modules/
68 | jspm_packages/
69 |
70 | # Typescript v1 declaration files
71 | typings/
72 |
73 | # Optional npm cache directory
74 | .npm
75 |
76 | # Optional eslint cache
77 | .eslintcache
78 |
79 | # Optional REPL history
80 | .node_repl_history
81 |
82 | # Output of 'npm pack'
83 | *.tgz
84 |
85 | # Yarn Integrity file
86 | .yarn-integrity
87 |
88 | # dotenv environment variables file
89 | .env
90 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | ## [0.3.2](https://github.com/znck/vuepack/compare/v0.3.1...v0.3.2) (2018-06-01)
7 |
8 |
9 | ### Bug Fixes
10 |
11 | * Resolve babel preset path ([5e098fc](https://github.com/znck/vuepack/commit/5e098fc))
12 |
13 |
14 |
15 |
16 | ## [0.3.1](https://github.com/znck/vuepack/compare/v0.3.0...v0.3.1) (2018-05-25)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * Remove global flag from regex ([659480a](https://github.com/znck/vuepack/commit/659480a))
22 |
23 |
24 |
25 |
26 | # [0.3.0](https://github.com/znck/vuepack/compare/v0.2.0...v0.3.0) (2018-05-23)
27 |
28 |
29 | ### Bug Fixes
30 |
31 | * Do not print to standard out if single directory is provided ([48170d0](https://github.com/znck/vuepack/commit/48170d0))
32 |
33 |
34 | ### Features
35 |
36 | * Config can be function returning a object ([da10cd5](https://github.com/znck/vuepack/commit/da10cd5))
37 |
38 |
39 |
40 |
41 | # [0.2.0](https://github.com/znck/vuepack/compare/v0.1.3...v0.2.0) (2018-05-11)
42 |
43 |
44 | ### Features
45 |
46 | * Load config file from process directory ([df17f7b](https://github.com/znck/vuepack/commit/df17f7b))
47 | * Plugins architecture to slim down the core ([a75cb92](https://github.com/znck/vuepack/commit/a75cb92))
48 |
49 |
50 |
51 |
52 | ## [0.1.3](https://github.com/znck/vuepack/compare/v0.1.2...v0.1.3) (2018-05-05)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * Filter only files in glob search ([f072c1f](https://github.com/znck/vuepack/commit/f072c1f))
58 |
59 |
60 |
61 |
62 | ## [0.1.2](https://github.com/znck/vuepack/compare/v0.1.1...v0.1.2) (2018-05-05)
63 |
64 |
65 | ### Bug Fixes
66 |
67 | * Put compiled output to sub-directories if more than one input directories provided ([06485e8](https://github.com/znck/vuepack/commit/06485e8))
68 |
69 |
70 |
71 |
72 | ## [0.1.1](https://github.com/znck/vuepack/compare/v0.1.0...v0.1.1) (2018-05-05)
73 |
74 |
75 | ### Bug Fixes
76 |
77 | * Add vue-template-compiler as dependency ([f78f4fa](https://github.com/znck/vuepack/commit/f78f4fa))
78 | * change package name ([c2fa2ba](https://github.com/znck/vuepack/commit/c2fa2ba))
79 |
80 |
81 |
82 |
83 | # [0.1.0](https://github.com/znck/vuepack/compare/07db684...v0.1.0) (2018-05-05)
84 |
85 |
86 | ### Features
87 |
88 | * Comple less, scss, sass and typescript ([07db684](https://github.com/znck/vuepack/commit/07db684))
89 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Rahul Kadyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VuePack [](https://circleci.com/gh/znck/vuepack/) [](https://coveralls.io/github/znck/vuepack)
2 |
3 | > EXPERIMENTAL: DO NOT USE IN PRODUCTION.
4 |
5 | A tool to normalize `.vue` components so they can be published in NPM packages as it is.
6 |
7 | ## Usage
8 |
9 | ``` bash
10 | npm install -g vuepack
11 |
12 | vuec -h
13 | ```
14 |
--------------------------------------------------------------------------------
/docs/assets/images/icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/znck/vuepack/d9f5dfa8f5564f149f3463bf596d90d17d6d5667/docs/assets/images/icons.png
--------------------------------------------------------------------------------
/docs/assets/images/icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/znck/vuepack/d9f5dfa8f5564f149f3463bf596d90d17d6d5667/docs/assets/images/icons@2x.png
--------------------------------------------------------------------------------
/docs/assets/images/widgets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/znck/vuepack/d9f5dfa8f5564f149f3463bf596d90d17d6d5667/docs/assets/images/widgets.png
--------------------------------------------------------------------------------
/docs/assets/images/widgets@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/znck/vuepack/d9f5dfa8f5564f149f3463bf596d90d17d6d5667/docs/assets/images/widgets@2x.png
--------------------------------------------------------------------------------
/docs/globals.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
60 |
61 |
62 |
63 |
64 |
65 | Index
66 |
67 |
68 |
69 | External modules
70 |
83 |
84 |
85 |
86 |
87 |
88 |
134 |
135 |
136 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
60 |
61 |
62 |
63 |
64 |
65 |
VuePack

66 |
67 | EXPERIMENTAL: DO NOT USE IN PRODUCTION.
68 |
69 |
A tool to normalize .vue
components so they can be published in NPM packages as it is.
70 |
Usage
71 |
npm install -g vuepack
72 |
73 | vuec -h
74 |
75 |
76 |
77 |
123 |
124 |
125 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/docs/interfaces/_api_.compileroptions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CompilerOptions | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
63 |
Interface CompilerOptions
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Hierarchy
72 |
73 | -
74 | CompilerOptions
75 |
76 |
77 |
78 |
79 | Index
80 |
81 |
82 |
83 | Properties
84 |
88 |
89 |
90 |
91 |
92 |
93 | Properties
94 |
95 |
96 | Optional filename
97 | filename: undefined | string
98 |
103 |
104 |
105 |
106 | plugins
107 |
108 |
113 |
114 |
115 |
116 |
207 |
208 |
209 |
268 |
269 |
270 |
271 |
272 |
--------------------------------------------------------------------------------
/docs/interfaces/_api_.compilerresult.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CompilerResult | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
63 |
Interface CompilerResult
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Hierarchy
72 |
73 | -
74 | CompilerResult
75 |
76 |
77 |
78 |
79 | Index
80 |
81 |
82 |
83 | Properties
84 |
90 |
91 |
92 |
93 |
94 |
95 | Properties
96 |
97 |
98 | Optional code
99 | code: undefined | string
100 |
105 |
106 |
107 |
108 | errors
109 | errors: Array<string | Error>
110 |
115 |
116 |
117 |
118 | Optional map
119 | map: any
120 |
125 |
126 |
127 |
128 | tips
129 | tips: string[]
130 |
135 |
136 |
137 |
138 |
235 |
236 |
237 |
296 |
297 |
298 |
299 |
300 |
--------------------------------------------------------------------------------
/docs/interfaces/_api_.customfile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CustomFile | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
63 |
Interface CustomFile
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | Hierarchy
72 |
73 | -
74 | CustomFile
75 |
80 |
81 |
82 |
83 |
84 | Index
85 |
86 |
87 |
88 | Properties
89 |
93 |
94 |
95 |
96 |
97 |
98 | Properties
99 |
100 |
101 | content
102 | content: string
103 |
108 |
109 |
110 |
111 | filename
112 | filename: string
113 |
118 |
119 |
120 |
121 |
212 |
213 |
214 |
273 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/docs/modules/_api_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "api" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "api"
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Index
69 |
70 |
71 |
72 | Interfaces
73 |
85 |
86 |
87 | Type aliases
88 |
91 |
92 |
93 |
94 |
95 |
96 | Type aliases
97 |
98 |
99 | Plugin
100 |
101 |
106 |
107 |
108 |
109 |
188 |
189 |
190 |
249 |
250 |
251 |
252 |
253 |
--------------------------------------------------------------------------------
/docs/modules/_bin_vuec_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "bin/vuec" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "bin/vuec"
61 |
62 |
63 |
64 |
65 |
66 |
67 |
81 |
82 | Functions
83 |
84 |
85 | main
86 |
87 | - main(argv: string[]): Promise<void>
88 |
89 |
90 | -
91 |
96 |
Parameters
97 |
98 | -
99 |
argv: string[]
100 |
101 |
102 | Returns Promise<void>
103 |
104 |
105 |
106 |
107 |
108 | run
109 |
110 | - run(source: string, dest: string, files: string[], __namedParameters: object): Promise<boolean>
111 |
112 |
113 | -
114 |
119 |
Parameters
120 |
121 | -
122 |
source: string
123 |
124 | -
125 |
dest: string
126 |
127 | -
128 |
files: string[]
129 |
130 | -
131 |
__namedParameters: object
132 |
133 | -
134 |
overwrite: boolean
135 |
136 | -
137 |
138 |
139 | -
140 |
silent: boolean
141 |
142 | -
143 |
toStdOut: boolean
144 |
145 |
146 |
147 |
148 | Returns Promise<boolean>
149 |
150 |
151 |
152 |
153 |
154 |
206 |
207 |
208 |
267 |
268 |
269 |
270 |
271 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_babel_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/babel" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/babel"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_css_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/css" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/css"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_index_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/index" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/index"
61 |
62 |
63 |
64 |
116 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_less_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/less" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/less"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_sass_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/sass" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/sass"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_template_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/template" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/template"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/docs/modules/_plugins_typescript_.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | "plugins/typescript" | vuepack
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | - Preparing search index...
23 | - The search index is not available
24 |
25 |
vuepack
26 |
27 |
47 |
48 |
49 |
50 |
51 |
52 |
60 |
External module "plugins/typescript"
61 |
62 |
63 |
64 |
132 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | collectCoverageFrom: ['src/**'],
3 | moduleFileExtensions: ['js', 'ts'],
4 | transform: {
5 | '^.+\\.ts$': '/node_modules/ts-jest/preprocessor.js',
6 | },
7 | testMatch: ['**/?(*.)spec.ts'],
8 | testEnvironment: 'node'
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuepack",
3 | "version": "0.3.2",
4 | "description": "Package Vue components as they should be!",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "scripts": {
8 | "prebuild": "rm -r dist/",
9 | "build": "tsc",
10 | "test": "jest",
11 | "coveralls": "coveralls",
12 | "prepublishOnly": "npm run build",
13 | "docs": "typedoc --ignoreCompilerErrors typings src",
14 | "release": "standard-version -a"
15 | },
16 | "standard-version": {
17 | "scripts": {
18 | "postchangelog": "pnpm test && pnpm run docs && git add docs/"
19 | }
20 | },
21 | "repository": "https://github.com/znck/vuepack",
22 | "keywords": [
23 | "vue",
24 | "bundle",
25 | "package",
26 | "compiler"
27 | ],
28 | "author": "Rahul Kadyan (https://znck.me)",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/znck/vuepack/issues"
32 | },
33 | "files": [
34 | "dist"
35 | ],
36 | "bin": {
37 | "vuec": "dist/bin/vuec.js",
38 | "vuepack": "dist/bin/vuec.js"
39 | },
40 | "homepage": "https://github.com/znck/vuepack#readme",
41 | "dependencies": {
42 | "@babel/core": "^7.0.0-beta.46",
43 | "@babel/preset-env": "^7.0.0-beta.46",
44 | "@babel/preset-stage-0": "^7.0.0-beta.46",
45 | "@vue/component-compiler": "^3.1.1",
46 | "@vue/component-compiler-utils": "^1.2.1",
47 | "@znck/promised": "^1.0.0",
48 | "babel-core": "^7.0.0-bridge.0",
49 | "consola": "^1.3.0",
50 | "glob": "^7.1.2",
51 | "nopt": "^4.0.1",
52 | "postcss": "^6.0.22",
53 | "postcss-less": "^1.1.5",
54 | "postcss-sass": "^0.3.1",
55 | "postcss-scss": "^1.0.5",
56 | "prettier": "^1.12.1",
57 | "resolve": "github:zkochan/node-resolve",
58 | "sugarss": "^1.0.1",
59 | "vue-template-compiler": "^2.5.16"
60 | },
61 | "devDependencies": {
62 | "@types/babel-core": "^6.25.3",
63 | "@types/glob": "^5.0.35",
64 | "@types/jest": "^22.2.3",
65 | "@types/node": "^10.0.3",
66 | "@types/nopt": "^3.0.29",
67 | "@types/prettier": "^1.12.1",
68 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
69 | "conventional-changelog": "^1.1.24",
70 | "coveralls": "^3.0.1",
71 | "jest": "^22.4.3",
72 | "ts-jest": "^22.4.4",
73 | "typescript": "^2.8.3",
74 | "vue": "^2.5.16"
75 | },
76 | "optionalDependencies": {
77 | "less": "^3.0.2",
78 | "node-sass": "^4.9.0"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/pnpmfile.js:
--------------------------------------------------------------------------------
1 | const self = require('./package')
2 |
3 | module.exports = {
4 | hooks: {
5 | readPackage
6 | }
7 | }
8 |
9 | function readPackage (pkg) {
10 | if (pkg.dependencies && pkg.dependencies.resolve) {
11 | pkg.dependencies.resolve = 'zkochan/node-resolve'
12 | }
13 |
14 | if (pkg.name === 'ts-jest') {
15 | pkg.dependencies['source-map-support'] = '*'
16 | pkg.dependencies['babel-core'] = self.dependencies['babel-core']
17 | }
18 |
19 | return pkg
20 | }
21 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | export interface CompilerOptions {
2 | filename?: string
3 | plugins: Plugin[]
4 | }
5 |
6 | export interface CompilerResult {
7 | code?: string
8 | map?: any
9 | tips: string[]
10 | errors: Array
11 | }
12 |
13 |
14 | export interface MessageBagAPI {
15 | error(error: Error | string): void
16 |
17 | tip(tip: string): void
18 | }
19 |
20 | export interface FileRewriterAPI extends MessageBagAPI {
21 | name(filename: string): void
22 |
23 | content(content: string): void
24 | }
25 |
26 | export interface ComponentRewriterAPI extends MessageBagAPI {
27 | isFunctional: boolean
28 |
29 | defineInstanceProperty(name: string, value: string): void
30 |
31 | defineOption(name: string, value: string): void
32 |
33 | defineHook(name: string, value: string): void
34 |
35 | addBlock(block: CustomBlock): void
36 |
37 | addBlock(type: string, attrs: { [key: string]: string }, content?: string): void
38 | }
39 |
40 | export interface CustomFile {
41 | filename: string,
42 | content: string
43 | }
44 |
45 | export interface CustomBlock extends CustomFile {
46 | type: string
47 | attrs: { [key: string]: string }
48 | code?: string
49 | lang?: string
50 | }
51 |
52 | export interface ProcessedResults {
53 | filename?: string
54 | code?: string
55 | tips: string[]
56 | errors: Array
57 | }
58 |
59 | export interface BlockPlugin {
60 | name: string
61 |
62 | testBlock(type: string, srcOrLang?: string): boolean
63 |
64 | processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise
65 | }
66 |
67 | export interface FilePlugin {
68 | name: string
69 |
70 | testFile(filename: string): boolean
71 |
72 | processFile(file: CustomFile, api: FileRewriterAPI): Promise
73 | }
74 |
75 | export type Plugin = BlockPlugin & FilePlugin
--------------------------------------------------------------------------------
/src/bin/vuec.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import glob = require('glob')
4 | import nopt = require('nopt')
5 | import logger = require('consola')
6 | import * as path from 'path'
7 | import * as fs from 'fs'
8 | import promised from '@znck/promised'
9 | import DEFAULT_PLUGINS from '../plugins'
10 | import{ compile } from '../index'
11 | import { all, read, write } from '../utils'
12 | import { Plugin, BlockPlugin, FilePlugin } from '../api';
13 |
14 | async function run(
15 | source: string,
16 | dest: string,
17 | files: string[],
18 | {
19 | plugins = [],
20 | toStdOut = false,
21 | overwrite = false,
22 | silent = false
23 | }: { plugins: Array , toStdOut: boolean; overwrite: boolean; silent: boolean }
24 | ): Promise {
25 | let hasAnyErrors = false
26 | const duplicates = new Map()
27 | await all(
28 | files
29 | // Compile.
30 | .map(async (from: string): Promise => {
31 | try {
32 | const { filename, errors, tips, code } = await compile(
33 | path.resolve(source, from),
34 | await read(path.resolve(source, from)),
35 | [...DEFAULT_PLUGINS, ...plugins]
36 | )
37 |
38 | const hasErrors = errors.length > 0
39 | if (hasErrors) {
40 | hasAnyErrors = true
41 | logger.error('in ' + from)
42 | errors.forEach((error: string | Error) => console.error(error))
43 | }
44 |
45 | if (tips.length && !silent) {
46 | logger.info(
47 | 'for ' +
48 | from +
49 | '\n' +
50 | tips.map((tip: string) => ' - ' + tip.replace(/\n/g, '\n ')).join('\n')
51 | )
52 | }
53 |
54 | if (!hasErrors && filename && code) {
55 | const to = path.relative(source, filename)
56 |
57 | if (toStdOut) {
58 | console.log(code)
59 | } else if (!duplicates.has(to)) {
60 | try {
61 | await write(path.resolve(dest, to), code, overwrite)
62 | !silent && logger.log('> ' + from + ' -> ' + to)
63 | } catch (e) {
64 | logger.error(e.message)
65 | }
66 | duplicates.set(to, from)
67 | } else {
68 | logger.fatal(
69 | `Both '${duplicates.get(to)}' and ${from} are compiled to ${to}.`
70 | )
71 | logger.info('Rename one of the above files.')
72 | }
73 | }
74 | } catch (e) {
75 | logger.fatal('while processing ' + from, e.stack)
76 | }
77 | })
78 | )
79 |
80 | return hasAnyErrors
81 | }
82 |
83 | async function main(argv: string[]) {
84 | const options = nopt(
85 | { outDir: String, force: Boolean, silent: Boolean },
86 | { f: '--force', d: '--outDir', s: '--silent' },
87 | argv,
88 | 2
89 | )
90 |
91 | let config = {
92 | force: options.force,
93 | paths: options.argv.remain.length ? options.argv.remain : ['src'],
94 | plugins: [],
95 | silent: options.silent,
96 | target: options.outDir
97 | ? path.resolve(process.cwd(), options.outDir)
98 | : path.join(process.cwd(), 'dist')
99 | }
100 | const configPath = path.join(process.cwd(), 'vuec.config.js')
101 | const hasConfigFile = await promised(fs).exists(configPath)
102 | if (hasConfigFile) {
103 | logger.info('Using config from vuec.config.js.')
104 | const local = require(configPath)
105 |
106 | if (typeof local === 'function') {
107 | config = { ...config, ...local(config) }
108 | } else {
109 | config = { ...config, ...local }
110 | }
111 | }
112 | let hasAnyErrors: boolean = true
113 |
114 | for (const filename of config.paths) {
115 | const file = path.resolve(process.cwd(), filename)
116 | if (!(await promised(fs).exists(file))) {
117 | logger.error(`No such file or directory, ${filename}`)
118 | continue
119 | }
120 |
121 | const isFile = (await promised(fs).lstat(file)).isFile()
122 | const dir = isFile ? path.dirname(file) : file
123 | const dest = config.paths.length > 1 && !isFile ? path.join(config.target, filename) : config.target
124 | const files = isFile
125 | ? [file]
126 | : await promised({ glob }).glob('**', { cwd: dir, nodir: true })
127 |
128 | hasAnyErrors = await run(dir, dest, files, {
129 | plugins: config.plugins,
130 | toStdOut: !hasConfigFile && !options.outDir && config.paths.length === 1 && isFile,
131 | silent: config.silent,
132 | overwrite: config.force
133 | })
134 | }
135 |
136 | if (hasAnyErrors === true) process.exit(1)
137 | }
138 |
139 | if (process.argv.find(arg => arg === '-h' || arg === '--help')) {
140 | console.log(`
141 | Usage: vuec [options] [file ...]
142 |
143 | Examples: vuec
144 | vuec --outDir dist app.vue
145 |
146 | Options:
147 | -h, --help Output usage information.
148 | -d
149 | --outDir Redirect output structure to the directory.
150 | -f, --force Overwrite existing files.
151 | -s, --silent No console output.
152 | `)
153 | } else {
154 | main(process.argv).catch((error: Error) => logger.fatal(error))
155 | }
156 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {parse, SFCBlock, SFCCustomBlock, SFCDescriptor} from '@vue/component-compiler-utils'
2 | import {
3 | BlockPlugin,
4 | ComponentRewriterAPI,
5 | CustomBlock,
6 | CustomFile, FilePlugin,
7 | FileRewriterAPI,
8 | MessageBagAPI,
9 | Plugin,
10 | ProcessedResults
11 | } from "./api"
12 |
13 | export class MessageBag implements MessageBagAPI {
14 | errors: Array
15 | tips: string[]
16 | scope: string = ''
17 |
18 | constructor() {
19 | this.errors = []
20 | this.tips = []
21 | }
22 |
23 | setScope(name: string) {
24 | this.scope = name
25 | }
26 |
27 | error(error: Error | string): void {
28 | this.errors.push(error)
29 | }
30 |
31 | tip(tip: string): void {
32 | this.tips.push(tip)
33 | }
34 |
35 | hasError(): boolean {
36 | return this.errors.length > 0
37 | }
38 |
39 | write(): ProcessedResults {
40 | return {tips: this.tips, errors: this.errors}
41 | }
42 | }
43 |
44 | export class FileRewriter extends MessageBag implements FileRewriterAPI {
45 | input: CustomFile
46 | output: { filename?: string, content?: string } = {}
47 |
48 | constructor(input: CustomFile) {
49 | super()
50 | this.input = input
51 | }
52 |
53 | name(filename: string): void {
54 | this.output.filename = filename
55 | }
56 |
57 | content(content: string): void {
58 | this.output.content = content
59 | }
60 |
61 | write(): ProcessedResults {
62 | if (this.hasError()) return super.write()
63 |
64 | return {
65 | ...super.write(),
66 | filename: this.output.filename || this.input.filename,
67 | code: this.output.content
68 | }
69 | }
70 | }
71 |
72 | export class ComponentRewriter extends MessageBag implements ComponentRewriterAPI {
73 | static CONSTANTS = {
74 | properties: new Set(['$on'])
75 | }
76 |
77 | private mixins: Array<{ [key: string]: string }> = []
78 | private properties: { [key: string]: string } = {}
79 | private hooks: { [key: string]: string[] } = {}
80 | private currentPluginName: string
81 |
82 | filename: string | undefined
83 | descriptor: SFCDescriptor
84 | blocks: CustomBlock[] = []
85 |
86 | isFunctional: boolean
87 |
88 | constructor(filename: string | undefined, descriptor: SFCDescriptor) {
89 | super()
90 | this.filename = filename
91 | this.descriptor = descriptor
92 | this.isFunctional = !!(descriptor.template && 'functional' in descriptor.template.attrs)
93 | }
94 |
95 | async forEach(callback: (block: CustomBlock) => Promise): Promise {
96 | if (this.descriptor.template) await callback(normalizeBlock(this.descriptor.template, this.filename))
97 | if (this.descriptor.script) await callback(normalizeBlock(this.descriptor.script, this.filename))
98 |
99 | for (const style of this.descriptor.styles) {
100 | await callback(normalizeBlock(style, this.filename))
101 | }
102 |
103 | for (const block of this.descriptor.customBlocks) {
104 | await callback(normalizeBlock(block, this.filename))
105 | }
106 | }
107 |
108 | defineInstanceProperty(name: string, value: string): void {
109 | if (name in this.properties || ComponentRewriter.CONSTANTS.properties.has(name)) {
110 | this.tip(
111 | 'Plugin (' +
112 | this.currentPluginName +
113 | ') is trying to overwrite existing instance property.'
114 | )
115 | } else {
116 | this.properties[name] = value
117 | }
118 | }
119 |
120 | defineOption(name: string, value: string): void {
121 | this.mixins[this.mixins.length - 1][name] = value
122 | }
123 |
124 | defineHook(name: string, value: string): void {
125 | if (this.isFunctional) {
126 | // TODO: Handle hooks!.
127 | } else if (name in this.hooks) {
128 | this.hooks[name].push(value)
129 | } else {
130 | this.hooks[name] = [value]
131 | }
132 | }
133 |
134 | block(block: CustomBlock): void {
135 | if (block.type === 'template' || block.type === 'script') {
136 | if (this.blocks.some(it => it.type === block.type)) {
137 | throw new Error(`Only one ${block.type} block is supported.`)
138 | }
139 | }
140 |
141 | this.blocks.push(block)
142 | }
143 |
144 | createBlock(type: string, attrs: { [key: string]: string }, code?: string): void {
145 | this.block({ filename: '', type, attrs, code, content: ''})
146 | }
147 |
148 | addBlock(block: CustomBlock | string, attrs?: { [p: string]: string }, content?: string): void {
149 | if (typeof block === 'string') this.createBlock(block, attrs || {}, content)
150 | else if (block && typeof block === 'object') this.block(block)
151 | }
152 |
153 | write(): ProcessedResults {
154 | if (this.hasError()) return super.write()
155 |
156 | const code = this.blocks.map(stringifyBlock).join('\n')
157 |
158 | return {...super.write(), filename: this.filename, code}
159 | }
160 | }
161 |
162 | // -- Internal Utility Functions --
163 |
164 | function stringifyBlock(block: CustomBlock): string {
165 | const {type, code, attrs} = block
166 |
167 | const attrString = Object.keys(attrs)
168 | .map(
169 | key => (attrs[key] === '' || attrs[key] as any === true ? key : key + '=' + JSON.stringify(attrs[key]))
170 | )
171 | .join(' ')
172 |
173 | return `<${type}${attrString ? ' ' + attrString : ''}>\n${(
174 | code || ''
175 | ).trim()}\n${type}>\n`
176 | }
177 |
178 | function normalizeBlock(block: SFCCustomBlock | SFCBlock, filename?: string): CustomBlock {
179 | delete block.attrs.lang
180 | const output: any = {
181 | filename,
182 | type: block.type,
183 | attrs: block.attrs,
184 | content: block.content,
185 | start: block.start,
186 | end: block.end,
187 | map: block.map,
188 | lang: (block as any).lang
189 | }
190 | const keys = ['module', 'scope', 'src']
191 |
192 | keys.forEach((key: string) => {
193 | if (key in block) {
194 | output.attrs[key] = typeof (block as any)[key] !== 'string'
195 | ? ''
196 | : (block as any)[key]
197 | }
198 | })
199 |
200 | return output as CustomBlock
201 | }
202 |
203 | export async function compile(filename: string, content: string, plugins: Array): Promise {
204 | let isVue = /\.vue$/gi.test(filename)
205 |
206 | if (!isVue) {
207 | const file = {filename, content}
208 | const api = new FileRewriter(file)
209 |
210 | for (const plugin of plugins) {
211 | if ((plugin as FilePlugin).testFile && (plugin as FilePlugin).testFile(filename)) {
212 | await (plugin as FilePlugin).processFile(file, api)
213 | }
214 | }
215 |
216 | return api.write()
217 | } else {
218 | const api = new ComponentRewriter(filename, parse({filename: filename, source: content}))
219 |
220 | await api.forEach(async block => {
221 | for (const plugin of plugins) {
222 | if ((plugin as BlockPlugin).testBlock && (plugin as BlockPlugin).testBlock(block.type, block.attrs.src || block.lang)) {
223 | await (plugin as BlockPlugin).processBlock({...block, attrs: {...block.attrs}}, api)
224 | }
225 | }
226 | })
227 |
228 | return api.write()
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/src/plugins/babel.ts:
--------------------------------------------------------------------------------
1 | import {BlockPlugin, ComponentRewriterAPI, CustomBlock, CustomFile, FilePlugin, FileRewriterAPI} from "../api"
2 | import {transform} from 'babel-core'
3 | import promised from "@znck/promised"
4 |
5 | export default class BabelPlugin implements BlockPlugin, FilePlugin {
6 | name: string = 'babel'
7 |
8 | EXT_REGEX = /\.js$/i
9 | TYPE_REGEX = /^(js|babel|javascript)$/i
10 |
11 | testBlock(type: string, srcOrLang?: string): boolean {
12 | return type === 'script' &&
13 | (srcOrLang === undefined || this.EXT_REGEX.test(srcOrLang) || this.TYPE_REGEX.test(srcOrLang))
14 | }
15 |
16 | async processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise {
17 | if ('src' in block.attrs) {
18 | api.addBlock('script', block.attrs)
19 | return
20 | }
21 |
22 | api.addBlock('script', block.attrs, await this.compile(block.filename as string, block.content))
23 | }
24 |
25 | testFile(filename: string): boolean {
26 | return this.EXT_REGEX.test(filename)
27 | }
28 |
29 | async processFile(file: CustomFile, api: FileRewriterAPI): Promise {
30 | api.content(await this.compile(file.filename, file.content))
31 | }
32 |
33 | async compile(filename: string, source: string): Promise {
34 | const result = await promised({transform}).transform(source, {
35 | filename,
36 | presets: [[require.resolve('@babel/preset-env'), {modules: false, loose: true}]],
37 | comments: false
38 | })
39 |
40 | return result.code
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/plugins/css.ts:
--------------------------------------------------------------------------------
1 | import {BlockPlugin, ComponentRewriterAPI, CustomBlock} from "../api"
2 |
3 | export default class CSSPlugin implements BlockPlugin {
4 | name: string = 'css'
5 |
6 | EXT_REGEX = /\.css$/i
7 | TYPE_REGEX =/^(css|postcss)$/i
8 |
9 | testBlock(type: string, srcOrLang?: string): boolean {
10 | return type === 'style' &&
11 | (srcOrLang === undefined || this.EXT_REGEX.test(srcOrLang) || this.TYPE_REGEX.test(srcOrLang))
12 | }
13 |
14 | async processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise {
15 | api.addBlock('style', block.attrs, block.content)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/plugins/index.ts:
--------------------------------------------------------------------------------
1 | import LessPlugin from "./less"
2 | import SassPlugin from "./sass"
3 | import TypescriptPlugin from "./typescript"
4 | import BabelPlugin from "./babel"
5 | import TemplatePlugin from "./template"
6 | import CSSPlugin from "./css"
7 |
8 | export default [
9 | new TemplatePlugin(),
10 | new CSSPlugin(),
11 | new LessPlugin(),
12 | new SassPlugin(),
13 | new TypescriptPlugin(),
14 | new BabelPlugin()
15 | ]
--------------------------------------------------------------------------------
/src/plugins/less.ts:
--------------------------------------------------------------------------------
1 | import {ComponentRewriterAPI, CustomBlock, CustomFile, FileRewriterAPI, Plugin} from "../api"
2 |
3 | export default class LessPlugin implements Plugin {
4 | name: 'less'
5 | EXT_REGEX = /\.less$/i
6 |
7 | testFile(filename: string): boolean {
8 | return this.EXT_REGEX.test(filename)
9 | }
10 |
11 | async processFile(file: CustomFile, api: FileRewriterAPI): Promise {
12 | try {
13 | api.name(file.filename.replace(this.EXT_REGEX, '.css'))
14 | api.content(await this.compile(file.filename, file.content))
15 | } catch (e) {
16 | api.error(e)
17 | }
18 | }
19 |
20 | testBlock(type: string, srcOrLang?: string): boolean {
21 | return !!(type === 'style' && srcOrLang && (srcOrLang === 'less' || this.EXT_REGEX.test(srcOrLang)))
22 | }
23 |
24 | async processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise {
25 | if ('src' in block.attrs) {
26 | block.attrs.src = block.attrs.src.replace(this.EXT_REGEX, '.css')
27 |
28 | api.addBlock('style', block.attrs)
29 | return
30 | }
31 |
32 | try {
33 | api.addBlock('style', block.attrs, await this.compile(block.filename, block.content))
34 | } catch (e) {
35 | api.error(e)
36 | }
37 | }
38 |
39 | private async compile(filename: string, content: string): Promise {
40 | const result = await require('less').render(content, { filename })
41 |
42 | return result.css
43 | }
44 | }
--------------------------------------------------------------------------------
/src/plugins/sass.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ComponentRewriterAPI,
3 | CustomBlock,
4 | CustomFile,
5 | FileRewriterAPI,
6 | Plugin
7 | } from '../api'
8 | import promised from '@znck/promised'
9 |
10 | export default class SassPlugin implements Plugin {
11 | name: 'sass'
12 | EXT_REGEX = /\.(scss|sass)$/i
13 |
14 | testFile(filename: string): boolean {
15 | return this.EXT_REGEX.test(filename)
16 | }
17 |
18 | async processFile(file: CustomFile, api: FileRewriterAPI): Promise {
19 | const isIndented = file.filename.endsWith('.sass')
20 |
21 | try {
22 | const result = await this.compile(file.content, file.filename, isIndented)
23 | api.name(file.filename.replace(this.EXT_REGEX, '.css'))
24 | api.content(result)
25 | } catch (e) {
26 | api.error(e)
27 | }
28 | }
29 |
30 | private async compile(
31 | data: string,
32 | file: string,
33 | indentedSyntax: boolean
34 | ): Promise {
35 | const result = await promised(require('node-sass')).render({
36 | data,
37 | file,
38 | outFile: undefined,
39 | indentedSyntax,
40 | sourceMap: false,
41 | outputStyle: 'expanded'
42 | })
43 |
44 | return result.css.toString()
45 | }
46 |
47 | testBlock(type: string, srcOrLang?: string): boolean {
48 | return !!(
49 | type === 'style' &&
50 | srcOrLang &&
51 | (srcOrLang === 'scss' ||
52 | srcOrLang === 'sass' ||
53 | this.EXT_REGEX.test(srcOrLang))
54 | )
55 | }
56 |
57 | async processBlock(
58 | block: CustomBlock,
59 | api: ComponentRewriterAPI
60 | ): Promise {
61 | if ('src' in block.attrs) {
62 | block.attrs.src = block.attrs.src.replace(this.EXT_REGEX, '.css')
63 |
64 | api.addBlock('style', block.attrs)
65 | return
66 | }
67 |
68 | const isIndented = block.lang === 'sass'
69 | try {
70 | api.addBlock('style', block.attrs, await this.compile(block.content, block.filename, isIndented))
71 | } catch (e) {
72 | api.error(e)
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/plugins/template.ts:
--------------------------------------------------------------------------------
1 | import {BlockPlugin, ComponentRewriterAPI, CustomBlock} from "../api"
2 |
3 | export default class TemplatePlugin implements BlockPlugin {
4 | name: string = 'template'
5 |
6 | EXT_REGEX = /\.html$/i
7 | TYPE_REGEX =/^(html)$/i
8 |
9 | testBlock(type: string, srcOrLang?: string): boolean {
10 | return type === 'template' &&
11 | (srcOrLang === undefined || this.EXT_REGEX.test(srcOrLang) || this.TYPE_REGEX.test(srcOrLang))
12 | }
13 |
14 | async processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise {
15 | api.addBlock('template', block.attrs, block.content)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/plugins/typescript.ts:
--------------------------------------------------------------------------------
1 | import {BlockPlugin, ComponentRewriterAPI, CustomBlock, CustomFile, FilePlugin, FileRewriterAPI} from "../api"
2 | import {transform} from 'babel-core'
3 | import promised from "@znck/promised"
4 |
5 | export default class TypescriptPlugin implements BlockPlugin, FilePlugin {
6 | name: string = 'typescript'
7 |
8 | EXT_REGEX = /\.ts$/gi
9 | TYPE_REGEX = /^(ts|typescript)$/i
10 |
11 | testBlock(type: string, srcOrLang?: string): boolean {
12 | return !!(type === 'script' && srcOrLang
13 | && (this.EXT_REGEX.test(srcOrLang) || this.TYPE_REGEX.test(srcOrLang)))
14 | }
15 |
16 | async processBlock(block: CustomBlock, api: ComponentRewriterAPI): Promise {
17 | if ('src' in block.attrs) {
18 | block.attrs.src = block.attrs.src.replace(this.EXT_REGEX, '.js')
19 |
20 | api.addBlock('script', block.attrs)
21 | return
22 | }
23 |
24 | api.addBlock('script', block.attrs, await this.compile(block.filename as string, block.content))
25 | }
26 |
27 | testFile(filename: string): boolean {
28 | return this.EXT_REGEX.test(filename)
29 | }
30 |
31 | async processFile(file: CustomFile, api: FileRewriterAPI): Promise {
32 | api.name(file.filename.replace(this.EXT_REGEX, '.js'))
33 | api.content(await this.compile(file.filename, file.content))
34 | }
35 |
36 | async compile(filename: string, source: string): Promise {
37 | const result = await promised({transform}).transform(source, {
38 | filename,
39 | presets: [['@babel/preset-env', {modules: false, loose: true}]],
40 | comments: false,
41 | parserOpts: {
42 | plugins: ['typescript']
43 | }
44 | })
45 |
46 | return result.code
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import * as path from "path"
2 | import * as fs from "fs"
3 | const prettier = require('prettier')
4 |
5 | import promised from '@znck/promised'
6 |
7 | export async function all(promises: Array>): Promise> {
8 | return Promise.all(promises)
9 | }
10 |
11 | export function e(any: any): string {
12 | const prefix = 'const __ = '
13 |
14 | const source = prefix + JSON.stringify(any)
15 | const code = prettier.format(source, { semi: false, singleQuote: true }).trim()
16 |
17 | return code.substr(prefix.length)
18 | }
19 |
20 | export async function resolveExternal(context: string | undefined, query: string, extensions: string[]): Promise {
21 | if (!context) return
22 | const dir = path.dirname(context)
23 | const filename = path.resolve(dir, query)
24 |
25 | if (await promised(fs).exists(filename)) {
26 | return filename
27 | }
28 |
29 | for (const ext of extensions) {
30 | const filename = path.resolve(dir, query + '.' + ext)
31 | if (await promised(fs).exists(filename)) {
32 | return filename
33 | }
34 | }
35 | }
36 |
37 | export function flatten(args: Array): T[] {
38 | const result: T[] = []
39 |
40 | for (const arg of args) {
41 | if (Array.isArray(arg)) {
42 | result.push.apply(result, flatten(arg))
43 | } else if (arg) {
44 | result.push(arg)
45 | }
46 | }
47 |
48 | return result
49 | }
50 |
51 | export async function read(filename: string): Promise {
52 | const content = await promised(fs).readFile(filename)
53 |
54 | return content.toString()
55 | }
56 |
57 | export async function write(filename: string, content: string, overwrite: boolean = false): Promise {
58 | if (!overwrite && await promised(fs).exists(filename)) throw Error('Error: ' + filename + ' already exists.')
59 | await mkdirp(path.dirname(filename))
60 | await promised(fs).writeFile(filename, content)
61 | }
62 |
63 | export async function mkdirp(dir: string): Promise {
64 | if (!await promised(fs).exists(dir)) {
65 | await mkdirp(path.dirname(dir))
66 | try {
67 | await promised(fs).mkdir(dir)
68 | } catch (e) {
69 | if (!/file already exists/i.test(e.message)) {
70 | throw e
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/test/fixtures/WithCss.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
21 |
--------------------------------------------------------------------------------
/test/fixtures/WithCssImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/WithLess.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
23 |
--------------------------------------------------------------------------------
/test/fixtures/WithLessImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/WithSass.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/test/fixtures/WithSassImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/WithScript.vue:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/test/fixtures/WithScriptFunctional.vue:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/test/fixtures/WithScriptImport.vue:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/fixtures/WithScss.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
23 |
--------------------------------------------------------------------------------
/test/fixtures/WithScssImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/test/fixtures/WithTemplate.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
--------------------------------------------------------------------------------
/test/fixtures/WithTemplateImport.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/fixtures/WithTypescript.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
17 |
18 |
23 |
--------------------------------------------------------------------------------
/test/fixtures/WithTypescriptImport.vue:
--------------------------------------------------------------------------------
1 |
2 | Example
3 |
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/test/fixtures/import-less.less:
--------------------------------------------------------------------------------
1 | @color: red;
2 |
3 | .title {
4 | color: @color;
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/import-sass.sass:
--------------------------------------------------------------------------------
1 | $color: red;
2 |
3 | .title
4 | color: $color;
5 |
--------------------------------------------------------------------------------
/test/fixtures/import-scss.scss:
--------------------------------------------------------------------------------
1 | $color: red;
2 |
3 | .title {
4 | color: $color;
5 | }
--------------------------------------------------------------------------------
/test/fixtures/import.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --color: red;
3 | }
4 |
5 | .title {
6 | color: var(--color);
7 | }
--------------------------------------------------------------------------------
/test/fixtures/script.js:
--------------------------------------------------------------------------------
1 | export default {
2 | name: 'Simple',
3 | render(h) {
4 | return h('div', { style: 'color: red' }, 'Example')
5 | }
6 | }
--------------------------------------------------------------------------------
/test/fixtures/style-less.less:
--------------------------------------------------------------------------------
1 | @import "import-less.less";
2 |
3 | div {
4 | color: @color;
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/style-sass.sass:
--------------------------------------------------------------------------------
1 | @import "./import-sass";
2 | @import "./import-scss";
3 |
4 | div
5 | color: $color;
6 |
--------------------------------------------------------------------------------
/test/fixtures/style-scss.scss:
--------------------------------------------------------------------------------
1 | @import "style";
2 | @import "import-sass.sass";
3 | @import "./import-scss.scss" print;
4 |
5 | div {
6 | color: $color;
7 | }
8 |
--------------------------------------------------------------------------------
/test/fixtures/style.css:
--------------------------------------------------------------------------------
1 | @import "./import.css";
2 |
3 | div {
4 | color: var(--color);
5 | }
6 |
--------------------------------------------------------------------------------
/test/fixtures/template.html:
--------------------------------------------------------------------------------
1 | Example
--------------------------------------------------------------------------------
/test/fixtures/typescript.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | export default Vue.extend({
4 | name: 'Simple',
5 | methods: {
6 | foo(...args: string[]) {
7 | return args
8 | }
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/test/utils.spec.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path'
2 | import { all, e, promised, flatten, resolveExternal } from '../src/utils'
3 |
4 | describe(all.name, () => {
5 | const returnIn10 = () => new Promise(resolve => setTimeout(() => resolve('foo'), 10))
6 | const returnIn100 = () => new Promise(resolve => setTimeout(() => resolve('bar'), 100))
7 | it('should combine promises', async () => {
8 | expect(await all([returnIn10(), returnIn100()])).toEqual(['foo', 'bar'])
9 | })
10 | })
11 |
12 | describe(e.name, () => {
13 | test('string', () => expect(e('foo')).toBe("'foo'"))
14 | test('integer', () => expect(e(1)).toBe('1'))
15 | test('number', () => expect(e(1.5670)).toBe('1.567'))
16 | test('boolean', () => expect(e(false)).toBe('false'))
17 | test('undefined', () => expect(e(undefined)).toBe('undefined'))
18 | test('null', () => expect(e(null)).toBe('null'))
19 | test('object', () => expect(e({ foo: 'bar' })).toBe("{ foo: 'bar' }"))
20 | test('array', () => expect(e([{ foo: 'bar' }])).toBe("[{ foo: 'bar' }]"))
21 | })
22 |
23 | describe(flatten.name, () => {
24 | it('should flatten arrays', () => {
25 | expect(flatten(['foo', ['bar', undefined], null])).toEqual(['foo', 'bar'])
26 | })
27 | })
28 |
29 | describe(resolveExternal.name, () => {
30 | it('should not resolve without context', async () => {
31 | expect(await resolveExternal(undefined, 'foo.vue', [])).toBe(undefined)
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/test/vue.spec.ts:
--------------------------------------------------------------------------------
1 | import {compile as compileVue} from '../src/'
2 | import plugins from '../src/plugins'
3 | import * as path from 'path'
4 | import {read as _read} from '../src/utils'
5 |
6 | async function read(filename: string): Promise {
7 | return _read(path.resolve(__dirname, filename))
8 | }
9 |
10 | describe('with css', () => {
11 | let content
12 |
13 | beforeAll(async () => {
14 | content = await compileVue('Simple.vue', await read('./fixtures/WithCss.vue'), plugins)
15 | })
16 |
17 | test('compiles without error', () => expect(content.errors).toEqual([]))
18 | test('keeps template as it is', () => expect(content.code).toEqual(expect.stringContaining('Example
')))
19 | test('keeps style as it is', () => expect(content.code).toEqual(expect.stringContaining(`div {\n color: red;\n}`)))
20 | test('trans forms to es6', () => expect(content.code).toEqual(expect.stringContaining(`export default`)))
21 | test('has name as it is', () => expect(content.code).toEqual(expect.stringContaining(`name: 'Simple'`)))
22 | test('compiles rest operator', () => expect(content.code).toEqual(expect.stringContaining(`foo: function foo() {`)))
23 | })
24 |
25 | describe('with typescript', () => {
26 | let content
27 |
28 | beforeAll(async () => {
29 | content = await compileVue('WithTypescript.vue', await read('./fixtures/WithTypescript.vue'), plugins)
30 | })
31 |
32 | test('compiles without error', () => expect(content.errors).toEqual([]))
33 | test('trans forms to es6', () => expect(content.code).toEqual(expect.stringContaining(`export default`)))
34 | test('remove script language', () => expect(content.code).toEqual(expect.stringContaining(`