Skip to content

Commit d6c4e47

Browse files
authored
Merge pull request #227 from vuejs/feat/support-custom-i18n-blocks-natively
feat: add support for custom blocks by default
2 parents da2e575 + a71a99b commit d6c4e47

File tree

10 files changed

+2643
-2986
lines changed

10 files changed

+2643
-2986
lines changed

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,65 @@ vue-jest compiles the script and template of SFCs into a JavaScript file that Je
6767

6868
You can change the behavior of `vue-jest` by using `jest.globals`.
6969

70+
#### Supporting custom blocks
71+
72+
A great feature of the Vue SFC compiler is that it can support custom blocks. You might want to use those blocks in your tests. To render out custom blocks for testing purposes, you'll need to write a transformer. Once you have your transformer, you'll add an entry to vue-jest's transform map. This is how [vue-i18n's](https://github.com/kazupon/vue-i18n) `<i18n>` custom blocks are supported in unit tests.
73+
74+
A `package.json` Example
75+
76+
```json
77+
{
78+
"jest": {
79+
"moduleFileExtensions": ["js", "json", "vue"],
80+
"transform": {
81+
"^.+\\.js$": "babel-jest",
82+
"^.+\\.vue$": "vue-jest"
83+
},
84+
"globals": {
85+
"vue-jest": {
86+
"transform": {
87+
"your-custom-block": "./custom-block-processor.js"
88+
}
89+
}
90+
}
91+
}
92+
}
93+
```
94+
7095
> _Tip:_ Need programmatic configuration? Use the [--config](https://jestjs.io/docs/en/cli.html#config-path) option in Jest CLI, and export a `.js` file
7196

97+
A `jest.config.js` Example - If you're using a dedicated configuration file like you can reference and require your processor in the config file instead of using a file reference.
98+
99+
```js
100+
module.exports = {
101+
globals: {
102+
'vue-jest': {
103+
transform: {
104+
'your-custom-block': require('./custom-block-processor')
105+
}
106+
}
107+
}
108+
}
109+
```
110+
111+
#### Writing a processor
112+
113+
Processors must return an object with a "process" method, like so...
114+
115+
```js
116+
module.exports = {
117+
/**
118+
* Process the content inside of a custom block and prepare it for execution in a testing environment
119+
* @param {SFCCustomBlock[]} blocks All of the blocks matching your type, returned from `@vue/component-compiler-utils`
120+
* @param {string} vueOptionsNamespace The internal namespace for a component's Vue Options in vue-jest
121+
* @param {string} filename The SFC file being processed
122+
* @param {Object} config The full Jest config
123+
* @returns {string} The code to be output after processing all of the blocks matched by this type
124+
*/
125+
process(blocks, vueOptionsNamepsace, filename, config) {}
126+
}
127+
```
128+
72129
#### babelConfig
73130

74131
Provide `babelConfig` in one of the following formats:

lib/constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
vueOptionsNamespace: '__options__',
3+
defaultVueJestConfig: {
4+
transform: {}
5+
}
6+
}

lib/generate-code.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
const namespace = require('./constants').vueOptionsNamespace
2+
13
const splitRE = /\r?\n/g
24

35
module.exports = function generateCode(
46
scriptResult,
57
templateResult,
68
stylesResult,
9+
customBlocksResult,
710
isFunctional
811
) {
912
let output = ''
@@ -21,7 +24,7 @@ module.exports = function generateCode(
2124
}
2225

2326
output +=
24-
`var __options__ = typeof exports.default === 'function' ` +
27+
`var ${namespace} = typeof exports.default === 'function' ` +
2528
`? exports.default.options ` +
2629
`: exports.default\n`
2730

@@ -37,11 +40,11 @@ module.exports = function generateCode(
3740

3841
output +=
3942
`__options__.render = render\n` +
40-
`__options__.staticRenderFns = staticRenderFns\n`
43+
`${namespace}.staticRenderFns = staticRenderFns\n`
4144

4245
if (isFunctional) {
43-
output += '__options__.functional = true\n'
44-
output += '__options__._compiled = true\n'
46+
output += `${namespace}.functional = true\n`
47+
output += `${namespace}._compiled = true\n`
4548
}
4649
}
4750

@@ -59,22 +62,26 @@ module.exports = function generateCode(
5962
if (isFunctional) {
6063
output +=
6164
`;(function() {\n` +
62-
` var originalRender = __options__.render\n` +
65+
` var originalRender = ${namespace}.render\n` +
6366
` var styleFn = function () { ${styleStr} }\n` +
64-
` __options__.render = function renderWithStyleInjection (h, context) {\n` +
67+
` ${namespace}.render = function renderWithStyleInjection (h, context) {\n` +
6568
` styleFn.call(context)\n` +
6669
` return originalRender(h, context)\n` +
6770
` }\n` +
6871
`})()\n`
6972
} else {
7073
output +=
7174
`;(function() {\n` +
72-
` var beforeCreate = __options__.beforeCreate\n` +
75+
` var beforeCreate = ${namespace}.beforeCreate\n` +
7376
` var styleFn = function () { ${styleStr} }\n` +
74-
` __options__.beforeCreate = beforeCreate ? [].concat(beforeCreate, styleFn) : [styleFn]\n` +
77+
` ${namespace}.beforeCreate = beforeCreate ? [].concat(beforeCreate, styleFn) : [styleFn]\n` +
7578
`})()\n`
7679
}
7780
}
81+
82+
if (customBlocksResult) {
83+
output += `;\n ${customBlocksResult}`
84+
}
7885
return {
7986
code: output,
8087
renderFnStartLine,

lib/process-custom-blocks.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const { getVueJestConfig, getCustomTransformer } = require('./utils')
2+
const vueOptionsNamespace = require('./constants').vueOptionsNamespace
3+
4+
function applyTransformer(
5+
transformer,
6+
blocks,
7+
vueOptionsNamespace,
8+
filename,
9+
config
10+
) {
11+
return transformer.process({ blocks, vueOptionsNamespace, filename, config })
12+
}
13+
14+
function groupByType(acc, block) {
15+
acc[block.type] = acc[block.type] || []
16+
acc[block.type].push(block)
17+
return acc
18+
}
19+
20+
module.exports = function(allBlocks, filename, config) {
21+
const blocksByType = allBlocks.reduce(groupByType, {})
22+
const code = []
23+
for (const [type, blocks] of Object.entries(blocksByType)) {
24+
const transformer = getCustomTransformer(
25+
getVueJestConfig(config).transform,
26+
type
27+
)
28+
if (transformer) {
29+
const codeStr = applyTransformer(
30+
transformer,
31+
blocks,
32+
vueOptionsNamespace,
33+
filename,
34+
config
35+
)
36+
code.push(codeStr)
37+
}
38+
}
39+
40+
return code.length ? code.join('\n') : ''
41+
}

lib/process.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ const splitRE = /\r?\n/g
22

33
const VueTemplateCompiler = require('vue-template-compiler')
44
const generateSourceMap = require('./generate-source-map')
5-
const typescriptTransformer = require('./typescript-transformer')
6-
const coffeescriptTransformer = require('./coffee-transformer')
5+
const typescriptTransformer = require('./transformers/typescript')
6+
const coffeescriptTransformer = require('./transformers/coffee')
77
const _processStyle = require('./process-style')
8+
const processCustomBlocks = require('./process-custom-blocks')
89
const getVueJestConfig = require('./utils').getVueJestConfig
910
const logResultErrors = require('./utils').logResultErrors
1011
const stripInlineSourceMap = require('./utils').stripInlineSourceMap
@@ -99,6 +100,11 @@ module.exports = function(src, filename, config) {
99100
const templateResult = processTemplate(descriptor.template, filename, config)
100101
const scriptResult = processScript(descriptor.script, filename, config)
101102
const stylesResult = processStyle(descriptor.styles, filename, config)
103+
const customBlocksResult = processCustomBlocks(
104+
descriptor.customBlocks,
105+
filename,
106+
config
107+
)
102108

103109
const isFunctional =
104110
descriptor.template &&
@@ -112,6 +118,7 @@ module.exports = function(src, filename, config) {
112118
scriptResult,
113119
templateResult,
114120
stylesResult,
121+
customBlocksResult,
115122
isFunctional
116123
)
117124

lib/coffee-transformer.js renamed to lib/transformers/coffee.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
const ensureRequire = require('./ensure-require.js')
2-
const throwError = require('./utils').throwError
3-
const getBabelOptions = require('./utils').getBabelOptions
1+
const ensureRequire = require('../ensure-require.js')
2+
const throwError = require('../utils').throwError
3+
const getBabelOptions = require('../utils').getBabelOptions
44

55
module.exports = {
66
process(src, filename, config) {

lib/typescript-transformer.js renamed to lib/transformers/typescript.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
const ensureRequire = require('./ensure-require')
1+
const ensureRequire = require('../ensure-require')
22
const babelJest = require('babel-jest')
3-
const getBabelOptions = require('./utils').getBabelOptions
4-
const getTsJestConfig = require('./utils').getTsJestConfig
5-
const stripInlineSourceMap = require('./utils').stripInlineSourceMap
6-
const getCustomTransformer = require('./utils').getCustomTransformer
7-
const getVueJestConfig = require('./utils').getVueJestConfig
3+
const {
4+
getBabelOptions,
5+
getTsJestConfig,
6+
stripInlineSourceMap,
7+
getCustomTransformer,
8+
getVueJestConfig
9+
} = require('../utils')
810

911
module.exports = {
1012
process(scriptContent, filePath, config) {

lib/utils.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const constants = require('./constants')
12
const loadPartialConfig = require('@babel/core').loadPartialConfig
23
const chalk = require('chalk')
34
const path = require('path')
@@ -83,19 +84,32 @@ const getCustomTransformer = function getCustomTransformer(
8384
transform = {},
8485
lang
8586
) {
86-
let transformerPath = fetchTransformer(lang, transform)
87-
if (transformerPath) {
88-
const transformer = require(resolvePath(transformerPath))
89-
90-
if (!isValidTransformer(transformer)) {
91-
throwError(
92-
`transformer must contain at least one process, preprocess, or ` +
93-
`postprocess method`
94-
)
95-
}
96-
return transformer
87+
transform = { ...constants.defaultVueJestConfig.transform, ...transform }
88+
89+
const transformerPath = fetchTransformer(lang, transform)
90+
91+
if (!transformerPath) {
92+
return null
9793
}
98-
return null
94+
95+
let transformer
96+
if (
97+
typeof transformerPath === 'string' &&
98+
require(resolvePath(transformerPath))
99+
) {
100+
transformer = require(resolvePath(transformerPath))
101+
} else if (typeof transformerPath === 'object') {
102+
transformer = transformerPath
103+
}
104+
105+
if (!isValidTransformer(transformer)) {
106+
throwError(
107+
`transformer must contain at least one process, preprocess, or ` +
108+
`postprocess method`
109+
)
110+
}
111+
112+
return transformer
99113
}
100114

101115
const throwError = function error(msg) {

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"stylus": "^0.54.5",
5959
"typescript": "^3.2.2",
6060
"vue": "^2.4.2",
61+
"vue-i18n": "^8.15.5",
6162
"vue-template-compiler": "^2.4.2"
6263
},
6364
"peerDependencies": {
@@ -72,7 +73,8 @@
7273
"chalk": "^2.1.0",
7374
"convert-source-map": "^1.6.0",
7475
"extract-from-css": "^0.4.4",
75-
"source-map": "^0.5.6",
76+
"js-yaml": "^3.13.1",
77+
"json5": "^2.1.1",
7678
"ts-jest": "^24.0.0"
7779
},
7880
"repository": {

0 commit comments

Comments
 (0)