1 |
|
2 | 'use strict';
|
3 |
|
4 | const VersionChecker = require('ember-cli-version-checker');
|
5 |
|
6 | const chalk = require('chalk');
|
7 | const co = require('co');
|
8 | const fs = require('fs');
|
9 | const mkdirp = require('mkdirp');
|
10 | const path = require('path');
|
11 | const strip = require('../utils').strip;
|
12 | const getConfigPath = require('../utils').getConfigPath;
|
13 |
|
14 | const FEATURES = require('../features');
|
15 |
|
16 | const USAGE_MESSAGE = strip`
|
17 | Usage:
|
18 |
|
19 | To list all available features, run ${chalk.bold('ember feature:list')}.
|
20 | To enable a feature, run ${chalk.bold('ember feature:enable some-feature')}.
|
21 | To disable a feature, run ${chalk.bold('ember feature:disable some-feature')}.
|
22 | `;
|
23 |
|
24 | const SHARED = {
|
25 |
|
26 |
|
27 | _isFeatureAvailable(feature) {
|
28 | let checker = new VersionChecker(this.project).forEmber();
|
29 | return checker.gte(`${feature.since}-beta.1`);
|
30 | },
|
31 |
|
32 | _ensureConfigFile() {
|
33 | try {
|
34 | return this.project.resolveSync('./config/optional-features.json');
|
35 | } catch(err) {
|
36 | if (err.code !== 'MODULE_NOT_FOUND') {
|
37 | throw err;
|
38 | }
|
39 | }
|
40 |
|
41 | let configPath = getConfigPath(this.project);
|
42 |
|
43 | mkdirp.sync(path.join(this.project.root, path.dirname(configPath)));
|
44 |
|
45 | fs.writeFileSync(configPath, '{}', { encoding: 'UTF-8' });
|
46 |
|
47 | return configPath;
|
48 | },
|
49 |
|
50 | _setFeature: co.wrap(function *(name, value) {
|
51 | let feature = FEATURES[name];
|
52 |
|
53 | if (feature === undefined) {
|
54 | console.log(chalk.red(`Error: ${chalk.bold(name)} is not a valid feature.\n`));
|
55 | return LIST_FEATURES.run.apply(this);
|
56 | }
|
57 |
|
58 | let configPath = this._ensureConfigFile();
|
59 | let configJSON = JSON.parse(fs.readFileSync(configPath, { encoding: 'UTF-8' }));
|
60 | if (!this._isFeatureAvailable(feature)) {
|
61 | console.log(chalk.red(`Error: ${chalk.bold(name)} is only available in Ember ${feature.since} or above.`));
|
62 | return;
|
63 | }
|
64 |
|
65 | if (typeof feature.callback === 'function') {
|
66 | yield feature.callback(this.project, value);
|
67 | }
|
68 |
|
69 | let config = {};
|
70 |
|
71 | Object.keys(FEATURES).forEach(feature => {
|
72 | if (feature === name) {
|
73 | config[feature] = value;
|
74 | } else if(configJSON[feature] !== undefined) {
|
75 | config[feature] = configJSON[feature];
|
76 | }
|
77 | });
|
78 |
|
79 | fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', { encoding: 'UTF-8' });
|
80 |
|
81 | let state = value ? 'Enabled' : 'Disabled';
|
82 |
|
83 | console.log(chalk.green(`${state} ${chalk.bold(name)}. Be sure to commit ${chalk.underline('config/optional-features.json')} to source control!`));
|
84 | })
|
85 | };
|
86 |
|
87 | const USAGE = Object.assign({
|
88 | name: 'feature',
|
89 | description: 'Prints the USAGE.',
|
90 | works: 'insideProject',
|
91 | run() {
|
92 | console.log(USAGE_MESSAGE);
|
93 | }
|
94 | }, SHARED);
|
95 |
|
96 |
|
97 | const INDENT_START = '';
|
98 |
|
99 | const LIST_FEATURES = Object.assign({
|
100 | name: 'feature:list',
|
101 | description: 'List all available features.',
|
102 | works: 'insideProject',
|
103 | run() {
|
104 | console.log(USAGE_MESSAGE);
|
105 | console.log('Available features:');
|
106 |
|
107 | let hasFeatures = false;
|
108 |
|
109 | Object.keys(FEATURES).forEach(key => {
|
110 | let feature = FEATURES[key];
|
111 |
|
112 | if (this._isFeatureAvailable(feature)) {
|
113 | console.log(strip`
|
114 | ${INDENT_START}
|
115 | ${chalk.bold(key)} ${chalk.cyan(`(Default: ${feature.default})`)}
|
116 | ${feature.description}
|
117 | ${chalk.gray(`More information: ${chalk.underline(feature.url)}`)}`);
|
118 |
|
119 | hasFeatures = true;
|
120 | }
|
121 | });
|
122 |
|
123 | if (hasFeatures) {
|
124 | console.log();
|
125 | } else {
|
126 | console.log(chalk.gray(strip`
|
127 | ${INDENT_START}
|
128 | No optional features available for your current Ember version.
|
129 | `));
|
130 | }
|
131 | }
|
132 | }, SHARED);
|
133 |
|
134 | const ENABLE_FEATURE = Object.assign({
|
135 | name: 'feature:enable',
|
136 | description: 'Enable feature.',
|
137 | works: 'insideProject',
|
138 | anonymousOptions: [
|
139 | '<feature-name>'
|
140 | ],
|
141 | run(_, args) {
|
142 | return this._setFeature(args[0], true);
|
143 | }
|
144 | }, SHARED);
|
145 |
|
146 | const DISABLE_FEATURE = Object.assign({
|
147 | name: 'feature:disable',
|
148 | description: 'Disable feature.',
|
149 | works: 'insideProject',
|
150 | anonymousOptions: [
|
151 | '<feature-name>'
|
152 | ],
|
153 | run(_, args) {
|
154 | return this._setFeature(args[0], false);
|
155 | }
|
156 | }, SHARED);
|
157 |
|
158 | module.exports = {
|
159 | 'feature': USAGE,
|
160 | 'feature:list': LIST_FEATURES,
|
161 | 'feature:enable': ENABLE_FEATURE,
|
162 | 'feature:disable': DISABLE_FEATURE
|
163 | }
|