UNPKG

4.36 kBJavaScriptView Raw
1/* eslint-disable no-console */
2'use strict';
3
4const VersionChecker = require('ember-cli-version-checker');
5
6const chalk = require('chalk');
7const co = require('co');
8const fs = require('fs');
9const mkdirp = require('mkdirp');
10const path = require('path');
11const strip = require('../utils').strip;
12const getConfigPath = require('../utils').getConfigPath;
13
14const FEATURES = require('../features');
15
16const 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
24const SHARED = {
25 // TODO: promisify the sync code below
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
87const 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/* This forces strip`` to start counting the indentaiton */
97const INDENT_START = '';
98
99const 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
134const 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
146const 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
158module.exports = {
159 'feature': USAGE,
160 'feature:list': LIST_FEATURES,
161 'feature:enable': ENABLE_FEATURE,
162 'feature:disable': DISABLE_FEATURE
163}