Skip to content

Commit 9b8100c

Browse files
Toilalzigomir
authored andcommitted
Add support for metalsmith builder hooks (#428)
* Add support for metalsmith builder hooks Close #427 * Fix style * Fix documentation sentence * Add e2e test for metalsmith custom plugin * Enhance e2e tests for metalsmith custom plugin
1 parent 182feab commit 9b8100c

File tree

7 files changed

+132
-6
lines changed

7 files changed

+132
-6
lines changed

Diff for: README.md

+34-2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ vue init ~/fs/path/to-custom-template my-project
7777
- `prompts`: used to collect user options data;
7878

7979
- `filters`: used to conditional filter files to render.
80+
81+
- `metalsmith`: used to add custom metalsmith plugins in the chain.
8082

8183
- `completeMessage`: the message to be displayed to the user when the template has been generated. You can include custom instruction here.
8284

@@ -179,6 +181,36 @@ The `skipInterpolation` field in the metadata file should be a [minimatch glob p
179181
}
180182
```
181183

184+
#### Metalsmith
185+
186+
`vue-cli` uses [metalsmith](https://github.com/segmentio/metalsmith) to generate the project.
187+
188+
You may customize the metalsmith builder created by vue-cli to register custom plugins.
189+
190+
```js
191+
{
192+
"metalsmith": function (metalsmith, opts, helpers) {
193+
function customMetalsmithPlugin (files, metalsmith, done) {
194+
// Implement something really custom here.
195+
done(null, files)
196+
}
197+
198+
metalsmith.use(customMetalsmithPlugin)
199+
}
200+
}
201+
```
202+
203+
If you need to hook metalsmith before questions are asked, you may use an object with `before` key.
204+
205+
```js
206+
{
207+
"metalsmith": {
208+
before: function (metalsmith, opts, helpers) {},
209+
after: function (metalsmith, opts, helpers) {}
210+
}
211+
}
212+
```
213+
182214
#### Additional data available in meta.{js,json}
183215

184216
- `destDirName` - destination directory name
@@ -204,7 +236,7 @@ Arguments:
204236
- `data`: the same data you can access in `completeMessage`:
205237
```js
206238
{
207-
complete(data) {
239+
complete (data) {
208240
if (!data.inPlace) {
209241
console.log(`cd ${data.destDirName}`)
210242
}
@@ -218,7 +250,7 @@ Arguments:
218250
- `files`: An array of generated files
219251
```js
220252
{
221-
complete(data, {logger, chalk}) {
253+
complete (data, {logger, chalk}) {
222254
if (!data.inPlace) {
223255
logger.log(`cd ${chalk.yellow(data.destDirName)}`)
224256
}

Diff for: lib/generate.js

+16-3
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,24 @@ module.exports = function generate (name, src, dest, done) {
4343
opts.helpers && Object.keys(opts.helpers).map(function (key) {
4444
Handlebars.registerHelper(key, opts.helpers[key])
4545
})
46-
metalsmith
47-
.use(askQuestions(opts.prompts))
46+
47+
var helpers = {chalk, logger}
48+
49+
if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
50+
opts.metalsmith.before(metalsmith, opts, helpers)
51+
}
52+
53+
metalsmith.use(askQuestions(opts.prompts))
4854
.use(filterFiles(opts.filters))
4955
.use(renderTemplateFiles(opts.skipInterpolation))
50-
.clean(false)
56+
57+
if (typeof opts.metalsmith === 'function') {
58+
opts.metalsmith(metalsmith, opts, helpers)
59+
} else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
60+
opts.metalsmith.after(metalsmith, opts, helpers)
61+
}
62+
63+
metalsmith.clean(false)
5164
.source('.') // start from template root instead of `./src` which is Metalsmith's default for `source`
5265
.destination(dest)
5366
.build(function (err, files) {

Diff for: test/e2e/mock-metalsmith-custom-before-after/meta.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module.exports = {
2+
"metalsmith": {
3+
before: function (metalsmith, opts, helpers) {
4+
metalsmith.metadata().before = "Before";
5+
},
6+
after: function (metalsmith, opts, helpers) {
7+
metalsmith.metadata().after = "After";
8+
function customMetalsmithPlugin (files, metalsmith, done) {
9+
// Implement something really custom here.
10+
11+
var readme = files['readme.md']
12+
delete files['readme.md']
13+
files['custom-before-after/readme.md'] = readme
14+
15+
done(null, files)
16+
}
17+
18+
metalsmith.use(customMetalsmithPlugin)
19+
}
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Metalsmith {{after}} and {{before}} hooks

Diff for: test/e2e/mock-metalsmith-custom/meta.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module.exports = {
2+
"metalsmith": function (metalsmith, opts, helpers) {
3+
metalsmith.metadata().custom = "Custom";
4+
function customMetalsmithPlugin (files, metalsmith, done) {
5+
// Implement something really custom here.
6+
7+
var readme = files['readme.md']
8+
delete files['readme.md']
9+
files['custom/readme.md'] = readme
10+
11+
done(null, files)
12+
}
13+
14+
metalsmith.use(customMetalsmithPlugin)
15+
}
16+
}

Diff for: test/e2e/mock-metalsmith-custom/template/readme.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Metalsmith {{custom}}

Diff for: test/e2e/test.js

+43-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ const metadata = require('../../lib/options')
1313
const { isLocalPath, getTemplatePath } = require('../../lib/local-path')
1414

1515
const MOCK_META_JSON_PATH = path.resolve('./test/e2e/mock-meta-json')
16+
const MOCK_METALSMITH_CUSTOM_PATH = path.resolve('./test/e2e/mock-metalsmith-custom')
17+
const MOCK_METALSMITH_CUSTOM_BEFORE_AFTER_PATH = path.resolve('./test/e2e/mock-metalsmith-custom-before-after')
1618
const MOCK_TEMPLATE_REPO_PATH = path.resolve('./test/e2e/mock-template-repo')
1719
const MOCK_TEMPLATE_BUILD_PATH = path.resolve('./test/e2e/mock-template-build')
1820
const MOCK_METADATA_REPO_JS_PATH = path.resolve('./test/e2e/mock-metadata-repo-js')
@@ -120,6 +122,46 @@ describe('vue-cli', () => {
120122
})
121123
})
122124

125+
it('supports custom metalsmith plugins', done => {
126+
generate('test', MOCK_METALSMITH_CUSTOM_PATH, MOCK_TEMPLATE_BUILD_PATH, err => {
127+
if (err) done(err)
128+
129+
expect(exists(`${MOCK_TEMPLATE_BUILD_PATH}/custom/readme.md`)).to.equal(true)
130+
131+
async.eachSeries([
132+
'readme.md'
133+
], function (file, next) {
134+
const template = fs.readFileSync(`${MOCK_METALSMITH_CUSTOM_PATH}/template/${file}`, 'utf8')
135+
const generated = fs.readFileSync(`${MOCK_TEMPLATE_BUILD_PATH}/custom/${file}`, 'utf8')
136+
render(template, {custom: 'Custom'}, (err, res) => {
137+
if (err) return next(err)
138+
expect(res).to.equal(generated)
139+
next()
140+
})
141+
}, done)
142+
})
143+
})
144+
145+
it('supports custom metalsmith plugins with after/before object keys', done => {
146+
generate('test', MOCK_METALSMITH_CUSTOM_BEFORE_AFTER_PATH, MOCK_TEMPLATE_BUILD_PATH, err => {
147+
if (err) done(err)
148+
149+
expect(exists(`${MOCK_TEMPLATE_BUILD_PATH}/custom-before-after/readme.md`)).to.equal(true)
150+
151+
async.eachSeries([
152+
'readme.md'
153+
], function (file, next) {
154+
const template = fs.readFileSync(`${MOCK_METALSMITH_CUSTOM_BEFORE_AFTER_PATH}/template/${file}`, 'utf8')
155+
const generated = fs.readFileSync(`${MOCK_TEMPLATE_BUILD_PATH}/custom-before-after/${file}`, 'utf8')
156+
render(template, {before: 'Before', after: 'After'}, (err, res) => {
157+
if (err) return next(err)
158+
expect(res).to.equal(generated)
159+
next()
160+
})
161+
}, done)
162+
})
163+
})
164+
123165
it('generate a vaild package.json with escaped author', done => {
124166
monkeyPatchInquirer(escapedAnswers)
125167
generate('test', MOCK_TEMPLATE_REPO_PATH, MOCK_TEMPLATE_BUILD_PATH, err => {
@@ -254,7 +296,7 @@ describe('vue-cli', () => {
254296
expect(getTemplatePath('../template')).to.equal(path.join(__dirname, '/../../../template'))
255297
})
256298

257-
it.only('points out the file in the error', done => {
299+
it('points out the file in the error', done => {
258300
monkeyPatchInquirer(answers)
259301
generate('test', MOCK_ERROR, MOCK_TEMPLATE_BUILD_PATH, err => {
260302
expect(err.message).to.match(/^\[readme\.md\] Parse error/)

0 commit comments

Comments
 (0)