adapt-cli 2.1.11 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bowerrc +2 -2
- package/.eslintignore +1 -0
- package/.eslintrc.json +14 -0
- package/.travis.yml +46 -46
- package/README.md +266 -266
- package/bin/adapt.js +3 -0
- package/json/help-create/component.json +9 -9
- package/json/help-create/course.json +9 -9
- package/json/help-create/question.json +9 -0
- package/json/help-create.json +12 -11
- package/json/help-devinstall.json +9 -9
- package/json/help-install.json +10 -10
- package/json/help-ls.json +7 -7
- package/json/help-register.json +7 -7
- package/json/help-rename.json +7 -7
- package/json/help-search.json +8 -8
- package/json/help-uninstall.json +7 -7
- package/json/help-unregister.json +8 -8
- package/json/help-update.json +12 -12
- package/json/help-version.json +7 -7
- package/json/help.json +19 -19
- package/lib/api.js +260 -0
- package/lib/cli.js +69 -52
- package/lib/commands/authenticate.js +18 -0
- package/lib/commands/create/component.js +64 -81
- package/lib/commands/create/course.js +26 -87
- package/lib/commands/create/question.js +18 -0
- package/lib/commands/create.js +85 -104
- package/lib/commands/devinstall.js +35 -97
- package/lib/commands/help.js +31 -52
- package/lib/commands/install.js +16 -856
- package/lib/commands/ls.js +9 -24
- package/lib/commands/register.js +11 -201
- package/lib/commands/rename.js +14 -138
- package/lib/commands/search.js +11 -29
- package/lib/commands/uninstall.js +9 -136
- package/lib/commands/unregister.js +12 -105
- package/lib/commands/update.js +12 -889
- package/lib/commands/version.js +13 -15
- package/lib/integration/AdaptFramework/build.js +39 -0
- package/lib/integration/AdaptFramework/clone.js +27 -0
- package/lib/integration/AdaptFramework/deleteSrcCore.js +9 -0
- package/lib/integration/AdaptFramework/deleteSrcCourse.js +9 -0
- package/lib/integration/AdaptFramework/download.js +21 -0
- package/lib/integration/AdaptFramework/erase.js +34 -0
- package/lib/integration/AdaptFramework/getLatestVersion.js +79 -0
- package/lib/integration/AdaptFramework/npmInstall.js +21 -0
- package/lib/integration/AdaptFramework.js +19 -0
- package/lib/integration/Plugin.js +403 -0
- package/lib/integration/PluginManagement/autenticate.js +56 -0
- package/lib/integration/PluginManagement/install.js +222 -0
- package/lib/integration/PluginManagement/print.js +52 -0
- package/lib/integration/PluginManagement/register.js +130 -0
- package/lib/integration/PluginManagement/rename.js +101 -0
- package/lib/integration/PluginManagement/schemas.js +8 -0
- package/lib/integration/PluginManagement/search.js +46 -0
- package/lib/integration/PluginManagement/uninstall.js +141 -0
- package/lib/integration/PluginManagement/unregister.js +101 -0
- package/lib/integration/PluginManagement/update.js +224 -0
- package/lib/integration/PluginManagement.js +21 -0
- package/lib/integration/Project.js +146 -0
- package/lib/integration/Target.js +296 -0
- package/lib/integration/getBowerRegistryConfig.js +34 -0
- package/lib/logger.js +28 -0
- package/lib/util/JSONReadValidate.js +34 -0
- package/lib/util/constants.js +38 -0
- package/lib/util/createPromptTask.js +7 -0
- package/lib/util/download.js +45 -0
- package/lib/util/errors.js +58 -0
- package/lib/util/extract.js +24 -0
- package/lib/util/getDirNameFromImportMeta.js +6 -0
- package/lib/util/promises.js +36 -0
- package/package.json +40 -49
- package/TESTING.md +0 -25
- package/bin/adapt +0 -3
- package/gruntfile.js +0 -18
- package/lib/AdaptConsoleApplication.js +0 -19
- package/lib/CommandParser.js +0 -19
- package/lib/CommandTranslator.js +0 -15
- package/lib/ConsoleRenderer.js +0 -10
- package/lib/Constants.js +0 -69
- package/lib/JsonLoader.js +0 -40
- package/lib/JsonWriter.js +0 -21
- package/lib/PackageMeta.js +0 -41
- package/lib/Plugin.js +0 -53
- package/lib/PluginTypeResolver.js +0 -47
- package/lib/Project.js +0 -89
- package/lib/RendererHelpers.js +0 -51
- package/lib/RepositoryDownloader.js +0 -64
- package/lib/Slug.js +0 -5
- package/lib/VersionChecker.js +0 -7
- package/lib/commands/create/index.js +0 -6
- package/lib/commands/index.js +0 -16
- package/lib/commands/install/InstallLog.js +0 -32
- package/lib/commands/install/InstallTarget.js +0 -259
- package/lib/commands/install/extend.js +0 -31
- package/lib/download.js +0 -101
- package/lib/errors.js +0 -58
- package/lib/promise/authenticate.js +0 -64
- package/lib/promise/build.js +0 -20
- package/lib/promise/cloneInstall.js +0 -35
- package/lib/promise/confirmBuild.js +0 -7
- package/lib/promise/exec.js +0 -39
- package/lib/promise/getRepository.js +0 -26
- package/lib/promise/highest.js +0 -109
- package/lib/promise/install.js +0 -31
- package/lib/promise/installAdaptDependencies.js +0 -30
- package/lib/promise/installNodeDependencies.js +0 -28
- package/lib/promise/removeTemporaryDownload.js +0 -8
- package/lib/promise/replaceTextContent.js +0 -10
- package/lib/promise/uninstallPackage.js +0 -15
- package/lib/promise/update.js +0 -33
- package/lib/promise/util.js +0 -16
- package/test/fixtures/adapt-with-plugins.json +0 -6
- package/test/specs/command_translation_concerns.js +0 -13
- package/test/specs/create_command_concerns.js +0 -22
- package/test/specs/create_concerns.js +0 -30
- package/test/specs/install_concerns.js +0 -31
- package/test/specs/installing_compatible_plugins_concerns.js +0 -126
- package/test/specs/installing_incompatible_plugins_concerns.js +0 -103
- package/test/specs/ls_concerns.js +0 -28
- package/test/specs/plugin_name_concerns.js +0 -82
- package/test/specs/project_concerns.js +0 -128
- package/test/specs/registration_concerns.js +0 -31
- package/test/specs/repository_downloader_concerns.js +0 -55
- package/test/specs/search_concerns.js +0 -30
- package/test/specs/type_resolution_concerns.js +0 -71
- package/test/specs/uninstall_command_concerns.js +0 -64
- package/test/specs/uninstall_concerns.js +0 -31
@@ -0,0 +1,52 @@
|
|
1
|
+
import chalk from 'chalk'
|
2
|
+
import semver from 'semver'
|
3
|
+
import { ADAPT_ALLOW_PRERELEASE } from '../../util/constants.js'
|
4
|
+
const semverOptions = { includePrerelease: ADAPT_ALLOW_PRERELEASE }
|
5
|
+
|
6
|
+
function highlight (pluginname) {
|
7
|
+
return ['adapt-contrib', 'adapt-'].reduce((output, prefix) => {
|
8
|
+
if (output || !pluginname.startsWith(prefix)) return output
|
9
|
+
return chalk.reset(prefix) + chalk.yellowBright(pluginname.substring(prefix.length))
|
10
|
+
}, null) || pluginname
|
11
|
+
}
|
12
|
+
|
13
|
+
function greenIfEqual (v1, v2) {
|
14
|
+
if (v1 === '*') return chalk.greenBright(v2)
|
15
|
+
return semver.satisfies(v1, v2, semverOptions)
|
16
|
+
? chalk.greenBright(v2)
|
17
|
+
: chalk.magentaBright(v2)
|
18
|
+
}
|
19
|
+
|
20
|
+
export function versionPrinter (plugin, logger) {
|
21
|
+
const {
|
22
|
+
versionToApply,
|
23
|
+
latestCompatibleSourceVersion
|
24
|
+
} = plugin
|
25
|
+
logger?.log(highlight(plugin.packageName), latestCompatibleSourceVersion === null
|
26
|
+
? '(no version information)'
|
27
|
+
: `${chalk.greenBright(versionToApply)}${plugin.isLocalSource ? ' (local)' : ` (latest compatible version is ${greenIfEqual(versionToApply, latestCompatibleSourceVersion)})`}`
|
28
|
+
)
|
29
|
+
}
|
30
|
+
|
31
|
+
export function existingVersionPrinter (plugin, logger) {
|
32
|
+
const {
|
33
|
+
preUpdateProjectVersion,
|
34
|
+
projectVersion,
|
35
|
+
latestCompatibleSourceVersion
|
36
|
+
} = plugin
|
37
|
+
const fromTo = preUpdateProjectVersion !== null
|
38
|
+
? `from ${chalk.greenBright(preUpdateProjectVersion)} to ${chalk.greenBright(projectVersion)}`
|
39
|
+
: `${chalk.greenBright(projectVersion)}`
|
40
|
+
logger?.log(highlight(plugin.packageName), latestCompatibleSourceVersion === null
|
41
|
+
? fromTo
|
42
|
+
: `${fromTo}${plugin.isLocalSource ? ' (local)' : ` (latest compatible version is ${greenIfEqual(projectVersion, latestCompatibleSourceVersion)})`}`
|
43
|
+
)
|
44
|
+
}
|
45
|
+
|
46
|
+
export function errorPrinter (plugin, logger) {
|
47
|
+
logger?.log(highlight(plugin.packageName), plugin.installError ? '(error: ' + plugin.installError + ')' : '(unknown error)')
|
48
|
+
}
|
49
|
+
|
50
|
+
export function packageNamePrinter (plugin, logger) {
|
51
|
+
logger?.log(highlight(plugin.packageName))
|
52
|
+
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
import getBowerRegistryConfig from '../getBowerRegistryConfig.js'
|
3
|
+
import fs from 'fs-extra'
|
4
|
+
import path from 'path'
|
5
|
+
import bower from 'bower'
|
6
|
+
import chalk from 'chalk'
|
7
|
+
import inquirer from 'inquirer'
|
8
|
+
import { readValidateJSON } from '../../util/JSONReadValidate.js'
|
9
|
+
import Plugin from '../Plugin.js'
|
10
|
+
import semver from 'semver'
|
11
|
+
import { ADAPT_ALLOW_PRERELEASE } from '../../util/constants.js'
|
12
|
+
const semverOptions = { includePrerelease: ADAPT_ALLOW_PRERELEASE }
|
13
|
+
|
14
|
+
export default async function register ({
|
15
|
+
logger,
|
16
|
+
cwd = process.cwd()
|
17
|
+
} = {}) {
|
18
|
+
cwd = path.resolve(process.cwd(), cwd)
|
19
|
+
const BOWER_REGISTRY_CONFIG = getBowerRegistryConfig({ cwd })
|
20
|
+
logger?.warn('Using registry at', BOWER_REGISTRY_CONFIG.register)
|
21
|
+
try {
|
22
|
+
const bowerJSONPath = path.join(cwd, 'bower.json')
|
23
|
+
const hasBowerJSON = fs.existsSync(bowerJSONPath)
|
24
|
+
|
25
|
+
const bowerJSON = {
|
26
|
+
name: undefined,
|
27
|
+
repository: undefined,
|
28
|
+
framework: undefined,
|
29
|
+
...(hasBowerJSON ? await readValidateJSON(bowerJSONPath) : {})
|
30
|
+
}
|
31
|
+
const properties = await confirm(bowerJSON)
|
32
|
+
hasBowerJSON && await fs.writeJSON(bowerJSONPath, properties, { spaces: 2, replacer: null })
|
33
|
+
|
34
|
+
// given a package name, create two Plugin representations
|
35
|
+
// if supplied name is adapt-contrib-myPackageName do a check against this name only
|
36
|
+
// if suppled name is adapt-myPackageName check against this name and adapt-contrib-myPackageName
|
37
|
+
// becase we don't want to allow adapt-myPackageName if adapt-contrib-myPackageName exists
|
38
|
+
const plugin = new Plugin({ name: properties.name, logger })
|
39
|
+
const contribPlugin = new Plugin({ name: properties.name, isContrib: true, logger })
|
40
|
+
const contribExists = await exists(BOWER_REGISTRY_CONFIG, contribPlugin)
|
41
|
+
const pluginExists = await exists(BOWER_REGISTRY_CONFIG, plugin)
|
42
|
+
|
43
|
+
if (contribExists || pluginExists) {
|
44
|
+
logger?.warn(plugin.toString(), 'has been previously registered. Plugin names must be unique. Try again with a different name.')
|
45
|
+
return
|
46
|
+
}
|
47
|
+
|
48
|
+
const result = await registerWithBowerRepo(BOWER_REGISTRY_CONFIG, plugin, properties.repository)
|
49
|
+
if (!result) throw new Error('The plugin was unable to register.')
|
50
|
+
logger?.log(chalk.green(plugin.packageName), 'has been registered successfully.')
|
51
|
+
} catch (err) {
|
52
|
+
logger?.error(err)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
async function confirm (properties) {
|
57
|
+
const plugin = new Plugin({ name: properties.name })
|
58
|
+
const schema = [
|
59
|
+
{
|
60
|
+
name: 'name',
|
61
|
+
message: chalk.cyan('name'),
|
62
|
+
validate: v => {
|
63
|
+
return /^adapt-[\w|-]+?$/.test(v) ||
|
64
|
+
'Name must prefixed with \'adapt\' and each word separated with a hyphen(-)'
|
65
|
+
},
|
66
|
+
type: 'input',
|
67
|
+
default: plugin.toString() || 'not specified'
|
68
|
+
},
|
69
|
+
{
|
70
|
+
name: 'repositoryUrl',
|
71
|
+
message: chalk.cyan('repository URL'),
|
72
|
+
validate: v => {
|
73
|
+
return /https:\/\/([\w.@:/\-~]+)(\.git)(\/)?/.test(v) ||
|
74
|
+
'Please provide a repository URL of the form https://<domain><path>.git'
|
75
|
+
},
|
76
|
+
type: 'input',
|
77
|
+
default: properties.repository ? properties.repository.url : undefined
|
78
|
+
},
|
79
|
+
{
|
80
|
+
name: 'framework',
|
81
|
+
message: chalk.cyan('framework'),
|
82
|
+
validate: v => {
|
83
|
+
return semver.validRange(v, semverOptions) !== null ||
|
84
|
+
'Please provide a valid semver (see https://semver.org/)'
|
85
|
+
},
|
86
|
+
type: 'input',
|
87
|
+
default: properties.framework || '>=5.15'
|
88
|
+
},
|
89
|
+
{
|
90
|
+
name: 'ready',
|
91
|
+
message: chalk.cyan('Register now?'),
|
92
|
+
type: 'confirm',
|
93
|
+
default: true
|
94
|
+
}
|
95
|
+
]
|
96
|
+
const confirmation = await inquirer.prompt(schema)
|
97
|
+
if (!confirmation.ready) throw new Error('Aborted. Nothing has been registered.')
|
98
|
+
properties.name = confirmation.name
|
99
|
+
properties.repository = { type: 'git', url: confirmation.repositoryUrl }
|
100
|
+
properties.framework = confirmation.framework
|
101
|
+
return properties
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* @param {Plugin} plugin
|
106
|
+
* @returns {boolean}
|
107
|
+
*/
|
108
|
+
async function exists (BOWER_REGISTRY_CONFIG, plugin) {
|
109
|
+
const pluginName = plugin.toString().toLowerCase()
|
110
|
+
return new Promise((resolve, reject) => {
|
111
|
+
bower.commands.search(pluginName, {
|
112
|
+
registry: BOWER_REGISTRY_CONFIG.register
|
113
|
+
})
|
114
|
+
.on('end', result => {
|
115
|
+
const matches = result.filter(({ name }) => name.toLowerCase() === pluginName)
|
116
|
+
resolve(Boolean(matches.length))
|
117
|
+
})
|
118
|
+
.on('error', reject)
|
119
|
+
})
|
120
|
+
}
|
121
|
+
|
122
|
+
async function registerWithBowerRepo (BOWER_REGISTRY_CONFIG, plugin, repository) {
|
123
|
+
return new Promise((resolve, reject) => {
|
124
|
+
bower.commands.register(plugin.toString(), repository.url || repository, {
|
125
|
+
registry: BOWER_REGISTRY_CONFIG
|
126
|
+
})
|
127
|
+
.on('end', resolve)
|
128
|
+
.on('error', reject)
|
129
|
+
})
|
130
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import getBowerRegistryConfig from '../getBowerRegistryConfig.js'
|
2
|
+
import authenticate from './autenticate.js'
|
3
|
+
import bower from 'bower'
|
4
|
+
import chalk from 'chalk'
|
5
|
+
import inquirer from 'inquirer'
|
6
|
+
import request from 'request'
|
7
|
+
import path from 'path'
|
8
|
+
import Plugin from '../Plugin.js'
|
9
|
+
|
10
|
+
export default async function rename ({
|
11
|
+
logger,
|
12
|
+
oldName,
|
13
|
+
newName,
|
14
|
+
cwd = process.cwd()
|
15
|
+
} = {}) {
|
16
|
+
cwd = path.resolve(process.cwd(), cwd)
|
17
|
+
const BOWER_REGISTRY_CONFIG = getBowerRegistryConfig({ cwd })
|
18
|
+
if (!oldName || !newName) {
|
19
|
+
logger?.error('You must call rename with the following arguments: <plugin name> <new plugin name>')
|
20
|
+
return
|
21
|
+
}
|
22
|
+
// use Plugin to standardise name
|
23
|
+
newName = new Plugin({ name: newName, logger }).packageName
|
24
|
+
oldName = new Plugin({ name: oldName, logger }).packageName
|
25
|
+
logger?.warn('Using registry at', BOWER_REGISTRY_CONFIG.register)
|
26
|
+
logger?.warn(`Plugin will be renamed from ${oldName} to ${newName}`)
|
27
|
+
try {
|
28
|
+
const oldExists = await exists(BOWER_REGISTRY_CONFIG, oldName)
|
29
|
+
if (!oldExists) throw new Error(`Plugin "${oldName}" does not exist`)
|
30
|
+
const newExists = await exists(BOWER_REGISTRY_CONFIG, newName)
|
31
|
+
if (newExists) throw new Error(`Name "${newName}" already exists`)
|
32
|
+
const { username, token, type } = await authenticate({ pluginName: oldName })
|
33
|
+
logger?.log(`${username} authenticated as ${type}`)
|
34
|
+
await confirm()
|
35
|
+
await renameInBowerRepo({
|
36
|
+
username,
|
37
|
+
token,
|
38
|
+
oldName,
|
39
|
+
newName,
|
40
|
+
BOWER_REGISTRY_CONFIG
|
41
|
+
})
|
42
|
+
logger?.log(chalk.green('The plugin was successfully renamed.'))
|
43
|
+
} catch (err) {
|
44
|
+
logger?.error(err)
|
45
|
+
logger?.error('The plugin was not renamed.')
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
async function confirm () {
|
50
|
+
const schema = [
|
51
|
+
{
|
52
|
+
name: 'ready',
|
53
|
+
message: chalk.cyan('Confirm rename now?'),
|
54
|
+
type: 'confirm',
|
55
|
+
default: true
|
56
|
+
}
|
57
|
+
]
|
58
|
+
const confirmation = await inquirer.prompt(schema)
|
59
|
+
if (!confirmation.ready) throw new Error('Aborted. Nothing has been renamed.')
|
60
|
+
}
|
61
|
+
|
62
|
+
async function renameInBowerRepo ({
|
63
|
+
username,
|
64
|
+
token,
|
65
|
+
oldName,
|
66
|
+
newName,
|
67
|
+
BOWER_REGISTRY_CONFIG
|
68
|
+
}) {
|
69
|
+
const path = 'packages/rename/' + username + '/' + oldName + '/' + newName
|
70
|
+
const query = '?access_token=' + token
|
71
|
+
return new Promise((resolve, reject) => {
|
72
|
+
request({
|
73
|
+
url: BOWER_REGISTRY_CONFIG.register + path + query,
|
74
|
+
method: 'GET',
|
75
|
+
headers: { 'User-Agent': 'adapt-cli' },
|
76
|
+
followRedirect: false
|
77
|
+
}, (err, res) => {
|
78
|
+
if (err) return reject(err)
|
79
|
+
if (res.statusCode !== 201) reject(new Error(`The server responded with ${res.statusCode}`))
|
80
|
+
resolve()
|
81
|
+
})
|
82
|
+
})
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* @param {Plugin} plugin
|
87
|
+
* @returns {boolean}
|
88
|
+
*/
|
89
|
+
async function exists (BOWER_REGISTRY_CONFIG, plugin) {
|
90
|
+
const pluginName = plugin.toString().toLowerCase()
|
91
|
+
return new Promise((resolve, reject) => {
|
92
|
+
bower.commands.search(pluginName, {
|
93
|
+
registry: BOWER_REGISTRY_CONFIG.register
|
94
|
+
})
|
95
|
+
.on('end', result => {
|
96
|
+
const matches = result.filter(({ name }) => name.toLowerCase() === pluginName)
|
97
|
+
resolve(Boolean(matches.length))
|
98
|
+
})
|
99
|
+
.on('error', reject)
|
100
|
+
})
|
101
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import Project from '../Project.js'
|
2
|
+
import path from 'path'
|
3
|
+
|
4
|
+
export default async function schemas ({ cwd = process.cwd() } = {}) {
|
5
|
+
cwd = path.resolve(process.cwd(), cwd)
|
6
|
+
const project = new Project({ cwd })
|
7
|
+
return await project.getSchemaPaths({ cwd })
|
8
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import getBowerRegistryConfig from '../getBowerRegistryConfig.js'
|
2
|
+
import chalk from 'chalk'
|
3
|
+
import request from 'request'
|
4
|
+
import path from 'path'
|
5
|
+
|
6
|
+
export default async function search ({
|
7
|
+
logger,
|
8
|
+
searchTerm,
|
9
|
+
cwd = process.cwd()
|
10
|
+
} = {}) {
|
11
|
+
cwd = path.resolve(process.cwd(), cwd)
|
12
|
+
const BOWER_REGISTRY_CONFIG = getBowerRegistryConfig({ cwd })
|
13
|
+
try {
|
14
|
+
const uniqueResults = {}
|
15
|
+
for (const serverURI of BOWER_REGISTRY_CONFIG.search) {
|
16
|
+
try {
|
17
|
+
const immediateResults = await new Promise((resolve, reject) => {
|
18
|
+
request({
|
19
|
+
uri: `${serverURI}packages/search/${searchTerm}`,
|
20
|
+
method: 'GET',
|
21
|
+
headers: { 'User-Agent': 'adapt-cli' },
|
22
|
+
followRedirect: false
|
23
|
+
}, (err, res, body) => {
|
24
|
+
if (err) return reject(err)
|
25
|
+
if (res.statusCode !== 200) reject(new Error(`The server responded with ${res.statusCode}`))
|
26
|
+
try {
|
27
|
+
resolve(JSON.parse(body))
|
28
|
+
} catch (err) {
|
29
|
+
reject(err)
|
30
|
+
}
|
31
|
+
})
|
32
|
+
})
|
33
|
+
immediateResults?.forEach(result => (uniqueResults[result.name] = uniqueResults[result.name] ?? result))
|
34
|
+
} catch (err) {}
|
35
|
+
}
|
36
|
+
const results = Object.values(uniqueResults)
|
37
|
+
if (!results.length) {
|
38
|
+
logger?.warn(`no plugins found containing: ${searchTerm}`)
|
39
|
+
}
|
40
|
+
results.forEach(function (result) {
|
41
|
+
logger?.log(chalk.cyan(result.name) + ' ' + result.url)
|
42
|
+
})
|
43
|
+
} catch (err) {
|
44
|
+
logger?.error("Oh dear, something went wrong. I'm terribly sorry.", err)
|
45
|
+
}
|
46
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
|
2
|
+
import chalk from 'chalk'
|
3
|
+
import Project from '../Project.js'
|
4
|
+
import Target from '../Target.js'
|
5
|
+
import { eachOfLimitProgress } from '../../util/promises.js'
|
6
|
+
import { createPromptTask } from '../../util/createPromptTask.js'
|
7
|
+
import { errorPrinter, packageNamePrinter } from './print.js'
|
8
|
+
import { intersection } from 'lodash-es'
|
9
|
+
import path from 'path'
|
10
|
+
|
11
|
+
export default async function uninstall ({
|
12
|
+
plugins,
|
13
|
+
isInteractive = true,
|
14
|
+
cwd = process.cwd(),
|
15
|
+
logger = null
|
16
|
+
}) {
|
17
|
+
cwd = path.resolve(process.cwd(), cwd)
|
18
|
+
const project = new Project({ cwd, logger })
|
19
|
+
project.tryThrowInvalidPath()
|
20
|
+
|
21
|
+
logger?.log(chalk.cyan('uninstalling adapt dependencies...'))
|
22
|
+
|
23
|
+
const targets = await getUninstallTargets({ logger, project, plugins, isInteractive })
|
24
|
+
if (!targets?.length) return targets
|
25
|
+
|
26
|
+
await loadPluginData({ logger, targets })
|
27
|
+
await eachOfLimitProgress(
|
28
|
+
targets.filter(target => target.isToBeUninstalled),
|
29
|
+
target => target.uninstall(),
|
30
|
+
percentage => logger?.logProgress?.(`${chalk.bold.cyan('<info>')} Uninstalling plugins ${percentage}% complete`)
|
31
|
+
)
|
32
|
+
logger?.log(`${chalk.bold.cyan('<info>')} Uninstalling plugins 100% complete`)
|
33
|
+
const installedDependencies = await project.getInstalledDependencies()
|
34
|
+
await updateManifest({ project, targets, installedDependencies, isInteractive })
|
35
|
+
await summariseUninstallation({ logger, targets })
|
36
|
+
return targets
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* @param {Object} options
|
41
|
+
* @param {Project} options.project
|
42
|
+
* @param {[Target]} options.targets
|
43
|
+
*/
|
44
|
+
async function getUninstallTargets ({ logger, project, plugins, isInteractive }) {
|
45
|
+
if (typeof plugins === 'string') plugins = [plugins]
|
46
|
+
/** whether adapt.json is being used to compile the list of targets to install */
|
47
|
+
const isEmpty = !plugins?.length
|
48
|
+
if (isEmpty && isInteractive) {
|
49
|
+
const shouldContinue = await createPromptTask({
|
50
|
+
message: chalk.reset('This command will attempt to uninstall all installed plugins. Do you wish to continue?'),
|
51
|
+
type: 'confirm'
|
52
|
+
})
|
53
|
+
if (!shouldContinue) return
|
54
|
+
}
|
55
|
+
|
56
|
+
/** a list of plugin name/version pairs */
|
57
|
+
const itinerary = isEmpty
|
58
|
+
? await project.getInstalledDependencies()
|
59
|
+
: plugins.reduce((itinerary, arg) => {
|
60
|
+
const [name, version = '*'] = arg.split(/[#@]/)
|
61
|
+
// Duplicates are removed by assigning to object properties
|
62
|
+
itinerary[name] = version
|
63
|
+
return itinerary
|
64
|
+
}, {})
|
65
|
+
const pluginNames = Object.entries(itinerary).map(([name, version]) => `${name}@${version}`)
|
66
|
+
|
67
|
+
/** @type {[Target]} */
|
68
|
+
const targets = pluginNames
|
69
|
+
? pluginNames.map(nameVersion => {
|
70
|
+
const [name] = nameVersion.split(/[#@]/)
|
71
|
+
return new Target({ name, project, logger })
|
72
|
+
})
|
73
|
+
: await project.getUninstallTargets()
|
74
|
+
return targets
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @param {Object} options
|
79
|
+
* @param {Project} options.project
|
80
|
+
* @param {[Target]} options.targets
|
81
|
+
*/
|
82
|
+
async function loadPluginData ({ logger, targets }) {
|
83
|
+
await eachOfLimitProgress(
|
84
|
+
targets,
|
85
|
+
target => target.fetchProjectInfo(),
|
86
|
+
percentage => logger?.logProgress?.(`${chalk.bold.cyan('<info>')} Getting plugin info ${percentage}% complete`)
|
87
|
+
)
|
88
|
+
logger?.log(`${chalk.bold.cyan('<info>')} Getting plugin info 100% complete`)
|
89
|
+
await eachOfLimitProgress(
|
90
|
+
targets,
|
91
|
+
target => target.markUninstallable(),
|
92
|
+
percentage => logger?.logProgress?.(`${chalk.bold.cyan('<info>')} Marking uninstallable ${percentage}% complete`)
|
93
|
+
)
|
94
|
+
logger?.log(`${chalk.bold.cyan('<info>')} Marking uninstallable 100% complete`)
|
95
|
+
}
|
96
|
+
|
97
|
+
/**
|
98
|
+
* @param {Object} options
|
99
|
+
* @param {Project} options.project
|
100
|
+
* @param {[Target]} options.targets
|
101
|
+
* @returns
|
102
|
+
*/
|
103
|
+
async function updateManifest ({ project, targets, installedDependencies, isInteractive }) {
|
104
|
+
if (targets.filter(target => target.isToBeUninstalled).length === 0) return
|
105
|
+
if (intersection(Object.keys(installedDependencies), targets.map(target => target.packageName)).length) return
|
106
|
+
if (isInteractive) {
|
107
|
+
const shouldUpdate = await createPromptTask({
|
108
|
+
message: chalk.white('Update the manifest (adapt.json)?'),
|
109
|
+
type: 'confirm',
|
110
|
+
default: true
|
111
|
+
})
|
112
|
+
if (!shouldUpdate) return
|
113
|
+
}
|
114
|
+
targets.forEach(target => target.isToBeUninstalled && project.remove(target))
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* @param {Object} options
|
119
|
+
* @param {[Target]} options.targets
|
120
|
+
*/
|
121
|
+
function summariseUninstallation ({ logger, targets }) {
|
122
|
+
const uninstallSucceeded = targets.filter(target => target.isUninstallSuccessful)
|
123
|
+
const uninstallSkipped = targets.filter(target => !target.isToBeUninstalled || target.isSkipped)
|
124
|
+
const uninstallErrored = targets.filter(target => target.isUninstallFailure)
|
125
|
+
const missing = targets.filter(target => target.isMissing)
|
126
|
+
const noneUninstalled = (uninstallSucceeded.length === 0)
|
127
|
+
const allUninstalledSuccessfully = (uninstallErrored.length === 0 && missing.length === 0)
|
128
|
+
const someUninstalledSuccessfully = (!noneUninstalled && !allUninstalledSuccessfully)
|
129
|
+
summarise(logger, uninstallSkipped, packageNamePrinter, 'The following plugins were skipped:')
|
130
|
+
summarise(logger, missing, packageNamePrinter, 'There was a problem locating the following plugins:')
|
131
|
+
summarise(logger, uninstallErrored, errorPrinter, 'The following plugins could not be uninstalled:')
|
132
|
+
if (noneUninstalled) logger?.log(chalk.cyanBright('None of the requested plugins could be uninstalled'))
|
133
|
+
else if (allUninstalledSuccessfully) summarise(logger, uninstallSucceeded, packageNamePrinter, 'All requested plugins were successfully uninstalled. Summary of uninstallation:')
|
134
|
+
else if (someUninstalledSuccessfully) summarise(logger, uninstallSucceeded, packageNamePrinter, 'The following plugins were successfully uninstalled:')
|
135
|
+
}
|
136
|
+
|
137
|
+
function summarise (logger, list, iterator, header) {
|
138
|
+
if (!list || !iterator || list.length === 0) return
|
139
|
+
logger?.log(chalk.cyanBright(header))
|
140
|
+
list.forEach(item => iterator(item, logger))
|
141
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import getBowerRegistryConfig from '../getBowerRegistryConfig.js'
|
2
|
+
import authenticate from './autenticate.js'
|
3
|
+
import fs from 'fs-extra'
|
4
|
+
import path from 'path'
|
5
|
+
import chalk from 'chalk'
|
6
|
+
import inquirer from 'inquirer'
|
7
|
+
import { readValidateJSON } from '../../util/JSONReadValidate.js'
|
8
|
+
import Plugin from '../Plugin.js'
|
9
|
+
import request from 'request'
|
10
|
+
|
11
|
+
export default async function unregister ({
|
12
|
+
logger,
|
13
|
+
cwd = process.cwd(),
|
14
|
+
pluginName
|
15
|
+
} = {}) {
|
16
|
+
cwd = path.resolve(process.cwd(), cwd)
|
17
|
+
const BOWER_REGISTRY_CONFIG = getBowerRegistryConfig({ cwd })
|
18
|
+
logger?.warn('Using registry at', BOWER_REGISTRY_CONFIG.register)
|
19
|
+
try {
|
20
|
+
const bowerJSONPath = path.join(cwd, 'bower.json')
|
21
|
+
const hasBowerJSON = fs.existsSync(bowerJSONPath)
|
22
|
+
const bowerJSON = hasBowerJSON ? await readValidateJSON(bowerJSONPath) : {}
|
23
|
+
if (pluginName) bowerJSON.name = pluginName
|
24
|
+
const props = await confirm(bowerJSON)
|
25
|
+
pluginName = props.pluginName
|
26
|
+
const repository = props.repository
|
27
|
+
const { username, token, type } = await authenticate({ repository, pluginName })
|
28
|
+
logger?.log(`${username} authenticated as ${type}`)
|
29
|
+
await finalConfirm()
|
30
|
+
await unregisterInBowerRepo({ pluginName, username, token, BOWER_REGISTRY_CONFIG })
|
31
|
+
logger?.log(chalk.green('The plugin was successfully unregistered.'))
|
32
|
+
} catch (err) {
|
33
|
+
logger?.error(err)
|
34
|
+
logger?.log('The plugin was not unregistered.')
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
async function confirm (properties) {
|
39
|
+
const plugin = new Plugin({ name: properties.name })
|
40
|
+
const schema = [
|
41
|
+
{
|
42
|
+
name: 'pluginName',
|
43
|
+
message: chalk.cyan('name'),
|
44
|
+
validate: v => {
|
45
|
+
return /^adapt-[\w|-]+?$/.test(v) ||
|
46
|
+
'Name must prefixed with \'adapt\' and each word separated with a hyphen(-)'
|
47
|
+
},
|
48
|
+
type: 'input',
|
49
|
+
default: plugin.toString() || 'not specified'
|
50
|
+
},
|
51
|
+
{
|
52
|
+
name: 'repository',
|
53
|
+
message: chalk.cyan('repository URL'),
|
54
|
+
validate: v => {
|
55
|
+
return /https:\/\/([\w.@:/\-~]+)(\.git)(\/)?/.test(v) ||
|
56
|
+
'Please provide a repository URL of the form https://<domain><path>.git'
|
57
|
+
},
|
58
|
+
type: 'input',
|
59
|
+
default: properties.repository ? properties.repository.url : undefined
|
60
|
+
}
|
61
|
+
]
|
62
|
+
return await inquirer.prompt(schema)
|
63
|
+
}
|
64
|
+
|
65
|
+
async function finalConfirm () {
|
66
|
+
const schema = [
|
67
|
+
{
|
68
|
+
name: 'ready',
|
69
|
+
message: chalk.cyan('Confirm Unregister now?'),
|
70
|
+
type: 'confirm',
|
71
|
+
default: true
|
72
|
+
}
|
73
|
+
]
|
74
|
+
const confirmation = await inquirer.prompt(schema)
|
75
|
+
if (!confirmation.ready) throw new Error('Aborted. Nothing has been unregistered.')
|
76
|
+
}
|
77
|
+
|
78
|
+
async function unregisterInBowerRepo ({
|
79
|
+
pluginName,
|
80
|
+
username,
|
81
|
+
token,
|
82
|
+
BOWER_REGISTRY_CONFIG
|
83
|
+
}) {
|
84
|
+
const uri = `${BOWER_REGISTRY_CONFIG.register}packages/${username}/${pluginName}?access_token=${token}`
|
85
|
+
return new Promise((resolve, reject) => {
|
86
|
+
request({
|
87
|
+
uri,
|
88
|
+
method: 'DELETE',
|
89
|
+
headers: { 'User-Agent': 'adapt-cli' },
|
90
|
+
followRedirect: false
|
91
|
+
}, (err, res, body) => {
|
92
|
+
if (err) return reject(err)
|
93
|
+
if (res.statusCode !== 204) reject(new Error(`The server responded with ${res.statusCode}`))
|
94
|
+
try {
|
95
|
+
resolve()
|
96
|
+
} catch (err) {
|
97
|
+
reject(err)
|
98
|
+
}
|
99
|
+
})
|
100
|
+
})
|
101
|
+
}
|