1 | # kopy
|
2 |
|
3 | [![NPM version](https://img.shields.io/npm/v/kopy.svg?style=flat)](https://npmjs.com/package/kopy) [![NPM downloads](https://img.shields.io/npm/dm/kopy.svg?style=flat)](https://npmjs.com/package/kopy) [![Build Status](https://img.shields.io/circleci/project/egoist/kopy/master.svg?style=flat)](https://circleci.com/gh/egoist/kopy)
|
4 |
|
5 | > Gracefully copy a directory and render templates.
|
6 |
|
7 | ## Why is this useful?
|
8 |
|
9 | This could be used to build a scaffolding tool like [yeoman](https://github.com/yeoman/yeoman) or [vue-cli](https://github.com/vuejs/vue-cli), and it's actually used by [SAO](https://github.com/saojs/sao).
|
10 |
|
11 | ## Install
|
12 |
|
13 | ```bash
|
14 | $ npm install --save kopy
|
15 | ```
|
16 |
|
17 | ## Usage
|
18 |
|
19 | ```js
|
20 | const copy = require('kopy')
|
21 |
|
22 | copy('./template', './dest', {
|
23 | data: {
|
24 | foo: 'bar'
|
25 | }
|
26 | }).then(({files}) => {
|
27 | console.log(files) // array of filenames in './dest'
|
28 | }).catch(err => {
|
29 | console.log(err.stack)
|
30 | })
|
31 | ```
|
32 |
|
33 | ## Template Syntax
|
34 |
|
35 | Templates could use [ejs](http://ejs.co) syntax or any template engine supported by [jstransformer](https://github.com/jstransformers)
|
36 |
|
37 | ## API
|
38 |
|
39 | ### copy(src, dest, options)
|
40 |
|
41 | Returns a Promise resolving the [`majo`](https://github.com/egoist/majo) instance we use.
|
42 |
|
43 | ```js
|
44 | copy(...args)
|
45 | .then(stream => {
|
46 | // stream is a majo instance
|
47 | // answers for prompts (if any)
|
48 | stream.meta.answers
|
49 | // options.data basically
|
50 | stream.meta.data
|
51 | // merged 'answers' and 'data'
|
52 | stream.meta.merged
|
53 | })
|
54 | ```
|
55 |
|
56 | #### src
|
57 |
|
58 | Type: `string`<br>
|
59 | Required: `true`
|
60 |
|
61 | Source directory. Could be a relative or absolute path.
|
62 |
|
63 | #### dest
|
64 |
|
65 | Type: `string`<br>
|
66 | Required: `true`
|
67 |
|
68 | Destination directory.
|
69 |
|
70 | #### options
|
71 |
|
72 | ##### glob
|
73 |
|
74 | Type: `Array` `string`<br>
|
75 | Default: `['**', '!**/node_modules/**']`
|
76 |
|
77 | Use the glob pattern(s) to find files in `src` directory.
|
78 |
|
79 | ##### template
|
80 |
|
81 | Type: `object`<br>
|
82 | Default: `require('jstransformer-ejs')`
|
83 |
|
84 | You can use a custom template engine, like [handlebars]:
|
85 |
|
86 | ```js
|
87 | copy(src, dest, {
|
88 | template: require('jstransformer-handlebars')
|
89 | })
|
90 | ```
|
91 |
|
92 | ##### templateOptions
|
93 |
|
94 | Type: `object` `function`
|
95 |
|
96 | The template engine options.
|
97 |
|
98 | If it's a function we use the return value as `templateOptions`, and the first argument is `{ answers, data, merged }`.
|
99 |
|
100 | ##### clean
|
101 |
|
102 | Type: `boolean`<br>
|
103 | Default: `false`
|
104 |
|
105 | Whether to clean destination directory before writing to it.
|
106 |
|
107 | ##### cwd
|
108 |
|
109 | Type: `string`<br>
|
110 | Default: `process.cwd()`
|
111 |
|
112 | Current working directory.
|
113 |
|
114 | ##### data
|
115 |
|
116 | Type: `object` `function`<br>
|
117 | Default: `undefined`
|
118 |
|
119 | The data to be used in rendering templates in source directory, filter files etc.
|
120 |
|
121 | If it's a function, we use its return value as `data`, and the first arguments is `answers`.
|
122 |
|
123 | ##### prompts
|
124 |
|
125 | Type: `Array<InquirerPrompt>`<br>
|
126 | Default: `undefined`
|
127 |
|
128 | [inquirer](https://github.com/SBoudrias/Inquirer.js) prompts, the answers of prompts will be assigned to `data`
|
129 |
|
130 |
|
131 | ##### mockPrompts
|
132 |
|
133 | Type: `Object`
|
134 |
|
135 | An object of mocked prompt values, eg:
|
136 |
|
137 | ```js
|
138 | {
|
139 | prompts: [
|
140 | { name: 'foo', message: 'type foo', validate: v => v === 'foo' },
|
141 | { name: 'hey', message: 'type hey' }
|
142 | ],
|
143 | mockPrompts: {
|
144 | foo: 'bar'
|
145 | }
|
146 | }
|
147 | ```
|
148 |
|
149 | In the above case, we will not run prompts to get answers from users, instead we use set `foo`'s value to `bar` and validate it. And in this case it will throw since `'bar' !== 'foo'`. The value of `hey` would be `undefined`.
|
150 |
|
151 | ##### skipInterpolation
|
152 |
|
153 | Type: `string | Array<string> | function`<br>
|
154 | Default: `undefined` (we skip all [binary files](https://github.com/sindresorhus/is-binary-path) by default)
|
155 |
|
156 | Patterns([minimatch](https://github.com/isaacs/minimatch#features)) used to skip interpolation, eg: `./foo*/bar-*.js`
|
157 |
|
158 | It could also be a function, whose first arg is file path and second arg is file content, eg. we want to exclude all `.js` files:
|
159 |
|
160 | ```js
|
161 | copy(src, dest, {
|
162 | skipInterpolation(file, content) {
|
163 | return /\.js$/.test(file)
|
164 | }
|
165 | })
|
166 | ```
|
167 |
|
168 | ##### disableInterpolation
|
169 |
|
170 | Type: `boolean`<br>
|
171 | Default: `false`
|
172 |
|
173 | Similar to `skipInterpolation`, but `disableInterpolation` disables all template interpolation, template markup will remain the way it is.
|
174 |
|
175 | ##### filters
|
176 |
|
177 | Type: `object` `function`<br>
|
178 | Default: `undefined`
|
179 |
|
180 | An object containing file filter rules, the key of each entry is a minimatch pattern, and its value is a JavaScript expression evaluated in the context of (prompt answers) data:
|
181 |
|
182 | ```js
|
183 | copy(src, dest, {
|
184 | filters: {
|
185 | '**/*.js': 'useJavaScript',
|
186 | '**/*.ts': '!useJavaScript'
|
187 | }
|
188 | })
|
189 | ```
|
190 |
|
191 | If it's a function, the first argument of it would be the result of `data` merging with prompt answers.
|
192 |
|
193 | ##### move
|
194 |
|
195 | Type: `object` `function`<br>
|
196 | Default: `undefined`
|
197 |
|
198 | Similar to `filters`, but instead of filtering files, it just renames the file:
|
199 |
|
200 | ```js
|
201 | copy(src, dest, {
|
202 | move: {
|
203 | 'gitignore': '.gitignore',
|
204 | 'folder/file.js': 'another/file.ts'
|
205 | }
|
206 | })
|
207 | ```
|
208 |
|
209 | If it's a function, the first argument of it would be the result of `data` merging with prompt answers.
|
210 |
|
211 | The value of each entry should be a file path or a function will returns a file path:
|
212 |
|
213 | ```js
|
214 | copy(src, dest, {
|
215 | move: {
|
216 | 'foo.*': 'foo.js',
|
217 | 'bar-*.js': filepath => filepath.replace(/^bar-/, 'bar/')
|
218 | }
|
219 | })
|
220 | ```
|
221 |
|
222 | ##### skipExisting
|
223 |
|
224 | Type: `function` `boolean`<br>
|
225 | Default: `undefined`
|
226 |
|
227 | Whether to skip existing file, it could be function that takes the path to existing file as argument.
|
228 |
|
229 | ```js
|
230 | copy(src, dest, {
|
231 | skipExisting(file) {
|
232 | console.log(`${file} exists, skipped!`)
|
233 | }
|
234 | })
|
235 | ```
|
236 |
|
237 | ##### write
|
238 |
|
239 | Type: `boolean`<br>
|
240 | Default: `true`
|
241 |
|
242 | Process files and write to disk.
|
243 |
|
244 | ---
|
245 |
|
246 | **kopy** © [EGOIST](https://github.com/egoist), Released under the [MIT](https://egoist.mit-license.org/) License.<br>
|
247 | Authored and maintained by EGOIST with help from contributors ([list](https://github.com/egoist/kopy/contributors)).
|
248 |
|
249 | > [egoistian.com](https://egoistian.com) · GitHub [@egoist](https://github.com/egoist) · Twitter [@_egoistlily](https://twitter.com/_egoistlily)
|