UNPKG

5.33 kBJavaScriptView Raw
1/* eslint-disable no-console */
2'use strict';
3
4const VersionChecker = require('ember-cli-version-checker');
5
6const chalk = require('chalk');
7const fs = require('fs');
8const mkdirp = require('mkdirp');
9const path = require('path');
10const strip = require('../utils').strip;
11const getConfigPath = require('../utils').getConfigPath;
12
13const FEATURES = require('../features');
14
15const 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
25const SHARED = {
26 // TODO: promisify the sync code below
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
106const 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/* This forces strip`` to start counting the indentaiton */
119const INDENT_START = '';
120
121const 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
163const 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 // intentionally not setting a default, when the value is undefined the
174 // command will prompt the user
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
185const 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 // intentionally not setting a default, when the value is undefined the
196 // command will prompt the user
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
207module.exports = {
208 feature: USAGE,
209 'feature:list': LIST_FEATURES,
210 'feature:enable': ENABLE_FEATURE,
211 'feature:disable': DISABLE_FEATURE,
212};