forked from jasongardnerlv/web-component-tester
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplugin.ts
133 lines (114 loc) · 3.55 KB
/
plugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
import * as _ from 'lodash';
import * as path from 'path';
import {Config} from './config';
import {Context} from './context';
// Plugin module names can be prefixed by the following:
const PREFIXES = [
'web-component-tester-',
'wct-',
];
export interface Metadata {}
/**
* A WCT plugin. This constructor is private. Plugins can be retrieved via
* `Plugin.get`.
*/
export class Plugin {
name: string;
cliConfig: Config;
packageName: string;
metadata: Metadata;
constructor(packageName: string, metadata: Metadata) {
this.packageName = packageName;
this.metadata = metadata;
this.name = Plugin.shortName(packageName);
this.cliConfig = this.metadata['cli-options'] || {};
}
/**
* @param {!Context} context The context that this plugin should be evaluated
* within.
*/
async execute(context: Context): Promise<void> {
try {
const plugin = require(this.packageName);
plugin(context, context.pluginOptions(this.name), this);
} catch (error) {
throw `Failed to load plugin "${this.name}": ${error}`;
}
};
/**
* Retrieves a plugin by shorthand or module name (loading it as necessary).
*
* @param {string} name
*/
static async get(name: string): Promise<Plugin> {
const shortName = Plugin.shortName(name);
if (_loadedPlugins[shortName]) {
return _loadedPlugins[shortName];
}
const names = [shortName].concat(PREFIXES.map((p) => p + shortName));
const loaded = _.compact(names.map(_tryLoadPluginPackage));
if (loaded.length > 1) {
const prettyNames = loaded.map((p) => p.packageName).join(' ');
throw `Loaded conflicting WCT plugin packages: ${prettyNames}`;
}
if (loaded.length < 1) {
throw `Could not find WCT plugin named "${name}"`;
}
return loaded[0];
};
/**
* @param {string} name
* @return {string} The short form of `name`.
*/
static shortName(name: string) {
for (const prefix of PREFIXES) {
if (name.indexOf(prefix) === 0) {
return name.substr(prefix.length);
}
}
return name;
};
// HACK(rictic): Makes es6 style imports happy, so that we can do, e.g.
// import {Plugin} from './plugin';
static Plugin = Plugin;
}
// Plugin Loading
// We maintain an identity map of plugins, keyed by short name.
const _loadedPlugins: {[name: string]: Plugin} = {};
/**
* @param {string} packageName Attempts to load a package as a WCT plugin.
* @return {Plugin}
*/
function _tryLoadPluginPackage(packageName: string) {
let packageInfo: Object;
try {
packageInfo = require(path.join(packageName, 'package.json'));
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') {
console.log(error);
}
return null;
}
// Plugins must have a (truthy) wct-plugin field.
if (!packageInfo['wct-plugin']) {
return null;
}
// Allow {"wct-plugin": true} as a shorthand.
const metadata =
_.isObject(packageInfo['wct-plugin']) ? packageInfo['wct-plugin'] : {};
return new Plugin(packageName, metadata);
}
module.exports = Plugin;