├── .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 [![Build Status](https://circleci.com/gh/znck/vuepack/tree/master.svg?style=shield)](https://circleci.com/gh/znck/vuepack/) [![Coverage Status](https://coveralls.io/repos/github/znck/vuepack/badge.svg)](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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 57 |

vuepack

58 |
59 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 57 |

vuepack

58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |

VuePack Build Status Coverage Status

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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
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 |
plugins: Plugin[]
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "bin/vuec"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Functions

73 | 77 |
78 |
79 |
80 |
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 | 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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/babel"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/css"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/index"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 114 |
115 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/less"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/sass"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/template"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "plugins/typescript"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 130 |
131 |
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\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 | 4 | 5 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /test/fixtures/WithCssImport.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/WithLess.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /test/fixtures/WithLessImport.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/WithSass.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /test/fixtures/WithSassImport.vue: -------------------------------------------------------------------------------- 1 | 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 | 4 | 5 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /test/fixtures/WithScssImport.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/WithTemplate.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/WithTemplateImport.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/WithTypescript.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /test/fixtures/WithTypescriptImport.vue: -------------------------------------------------------------------------------- 1 | 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(`