Skip to content

Commit e02858a

Browse files
authored
fix(plugins): refactor instantiatePlugin from preproprocessor (#3628)
add unit test for plugin.js add comments on role of function and cache
1 parent 8d589ed commit e02858a

File tree

5 files changed

+219
-208
lines changed

5 files changed

+219
-208
lines changed

lib/plugin.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,40 @@ function resolve (plugins, emitter) {
5050
return modules
5151
}
5252

53-
exports.resolve = resolve
53+
/**
54+
Create a function to handle errors in plugin loading.
55+
@param {Object} injector, the dict of dependency injection objects.
56+
@return function closed over injector, which reports errors.
57+
*/
58+
function createInstantiatePlugin (injector) {
59+
const emitter = injector.get('emitter')
60+
// Cache to avoid report errors multiple times per plugin.
61+
const pluginInstances = new Map()
62+
return function instantiatePlugin (kind, name) {
63+
if (pluginInstances.has(name)) {
64+
return pluginInstances.get(name)
65+
}
66+
67+
let p
68+
try {
69+
p = injector.get(`${kind}:${name}`)
70+
if (!p) {
71+
log.error(`Failed to instantiate ${kind} ${name}`)
72+
emitter.emit('load_error', kind, name)
73+
}
74+
} catch (e) {
75+
if (e.message.includes(`No provider for "${kind}:${name}"`)) {
76+
log.error(`Cannot load "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
77+
} else {
78+
log.error(`Cannot load "${name}"!\n ` + e.stack)
79+
}
80+
emitter.emit('load_error', kind, name)
81+
}
82+
pluginInstances.set(name, p, `${kind}:${name}`)
83+
return p
84+
}
85+
}
86+
87+
createInstantiatePlugin.$inject = ['injector']
88+
89+
module.exports = { resolve, createInstantiatePlugin }

lib/preprocessor.js

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -70,36 +70,8 @@ async function runProcessors (preprocessors, file, content) {
7070
file.sha = CryptoUtils.sha1(content)
7171
}
7272

73-
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, injector) {
74-
const emitter = injector.get('emitter')
75-
const instances = new Map()
76-
77-
function instantiatePreprocessor (name) {
78-
if (instances.has(name)) {
79-
return instances.get(name)
80-
}
81-
82-
let p
83-
try {
84-
p = injector.get('preprocessor:' + name)
85-
if (!p) {
86-
log.error(`Failed to instantiate preprocessor ${name}`)
87-
emitter.emit('load_error', 'preprocessor', name)
88-
}
89-
} catch (e) {
90-
if (e.message.includes(`No provider for "preprocessor:${name}"`)) {
91-
log.error(`Can not load "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
92-
} else {
93-
log.error(`Can not load "${name}"!\n ` + e.stack)
94-
}
95-
emitter.emit('load_error', 'preprocessor', name)
96-
}
97-
98-
instances.set(name, p)
99-
return p
100-
}
101-
_.union.apply(_, Object.values(config)).forEach(instantiatePreprocessor)
102-
73+
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) {
74+
_.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name))
10375
return async function preprocess (file) {
10476
const buffer = await tryToRead(file.originalPath, log)
10577
let isBinary = file.isBinary
@@ -121,7 +93,7 @@ function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath
12193
.sort((a, b) => b[1] - a[1])
12294
.map((duo) => duo[0])
12395
.reduce((preProcs, name) => {
124-
const p = instantiatePreprocessor(name)
96+
const p = instantiatePlugin('preprocessor', name)
12597

12698
if (!isBinary || (p && p.handleBinaryFiles)) {
12799
preProcs.push(p)
@@ -135,5 +107,5 @@ function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath
135107
}
136108
}
137109

138-
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'injector']
110+
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin']
139111
exports.createPriorityPreprocessor = createPriorityPreprocessor

lib/server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Server extends KarmaEventEmitter {
7676
watcher: ['value', watcher],
7777
launcher: ['factory', Launcher.factory],
7878
config: ['value', config],
79+
instantiatePlugin: ['factory', plugin.createInstantiatePlugin],
7980
preprocess: ['factory', preprocessor.createPriorityPreprocessor],
8081
fileList: ['factory', FileList.factory],
8182
webServer: ['factory', createWebServer],

test/unit/plugin.spec.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use strict'
2+
3+
const createInstantiatePlugin = require('../../lib/plugin').createInstantiatePlugin
4+
5+
describe('plugin', () => {
6+
describe('createInstantiatePlugin', () => {
7+
it('creates the instantiatePlugin function', () => {
8+
const fakeGet = sinon.stub()
9+
const fakeInjector = { get: fakeGet }
10+
11+
expect(typeof createInstantiatePlugin(fakeInjector)).to.be.equal('function')
12+
expect(fakeGet).to.have.been.calledWith('emitter')
13+
})
14+
15+
it('creates the instantiatePlugin function', () => {
16+
const fakes = {
17+
emitter: { emit: sinon.stub() }
18+
}
19+
const fakeInjector = { get: (id) => fakes[id] }
20+
21+
const instantiatePlugin = createInstantiatePlugin(fakeInjector)
22+
expect(typeof instantiatePlugin('kind', 'name')).to.be.equal('undefined')
23+
expect(fakes.emitter.emit).to.have.been.calledWith('load_error', 'kind', 'name')
24+
})
25+
26+
it('caches plugins', () => {
27+
const fakes = {
28+
emitter: { emit: sinon.stub() },
29+
'kind:name': { my: 'plugin' }
30+
}
31+
const fakeInjector = {
32+
get: (id) => {
33+
return fakes[id]
34+
}
35+
}
36+
37+
const instantiatePlugin = createInstantiatePlugin(fakeInjector)
38+
expect(instantiatePlugin('kind', 'name')).to.be.equal(fakes['kind:name'])
39+
fakeInjector.get = (id) => { throw new Error('failed to cache') }
40+
expect(instantiatePlugin('kind', 'name')).to.be.equal(fakes['kind:name'])
41+
})
42+
43+
it('errors if the injector errors', () => {
44+
const fakes = {
45+
emitter: { emit: sinon.stub() }
46+
}
47+
const fakeInjector = {
48+
get: (id) => {
49+
if (id in fakes) {
50+
return fakes[id]
51+
}
52+
throw new Error('fail')
53+
}
54+
}
55+
56+
const instantiatePlugin = createInstantiatePlugin(fakeInjector)
57+
expect(typeof instantiatePlugin('unknown', 'name')).to.be.equal('undefined')
58+
expect(fakes.emitter.emit).to.have.been.calledWith('load_error', 'unknown', 'name')
59+
})
60+
})
61+
})

0 commit comments

Comments
 (0)