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