adapt-cli 2.1.13 → 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.
Files changed (114) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.json +14 -0
  3. package/README.md +150 -150
  4. package/bin/adapt.js +3 -0
  5. package/json/help-create/question.json +9 -0
  6. package/json/help-create.json +2 -1
  7. package/lib/api.js +260 -0
  8. package/lib/cli.js +61 -44
  9. package/lib/commands/authenticate.js +18 -0
  10. package/lib/commands/create/component.js +55 -72
  11. package/lib/commands/create/course.js +25 -80
  12. package/lib/commands/create/question.js +18 -0
  13. package/lib/commands/create.js +80 -85
  14. package/lib/commands/devinstall.js +35 -97
  15. package/lib/commands/help.js +31 -52
  16. package/lib/commands/install.js +16 -907
  17. package/lib/commands/ls.js +9 -24
  18. package/lib/commands/register.js +10 -195
  19. package/lib/commands/rename.js +13 -128
  20. package/lib/commands/search.js +10 -28
  21. package/lib/commands/uninstall.js +9 -136
  22. package/lib/commands/unregister.js +11 -95
  23. package/lib/commands/update.js +12 -867
  24. package/lib/commands/version.js +12 -14
  25. package/lib/integration/AdaptFramework/build.js +39 -0
  26. package/lib/integration/AdaptFramework/clone.js +27 -0
  27. package/lib/integration/AdaptFramework/deleteSrcCore.js +9 -0
  28. package/lib/integration/AdaptFramework/deleteSrcCourse.js +9 -0
  29. package/lib/integration/AdaptFramework/download.js +21 -0
  30. package/lib/integration/AdaptFramework/erase.js +34 -0
  31. package/lib/integration/AdaptFramework/getLatestVersion.js +79 -0
  32. package/lib/integration/AdaptFramework/npmInstall.js +21 -0
  33. package/lib/integration/AdaptFramework.js +19 -0
  34. package/lib/integration/Plugin.js +403 -0
  35. package/lib/integration/PluginManagement/autenticate.js +56 -0
  36. package/lib/integration/PluginManagement/install.js +222 -0
  37. package/lib/integration/PluginManagement/print.js +52 -0
  38. package/lib/integration/PluginManagement/register.js +130 -0
  39. package/lib/integration/PluginManagement/rename.js +101 -0
  40. package/lib/integration/PluginManagement/schemas.js +8 -0
  41. package/lib/integration/PluginManagement/search.js +46 -0
  42. package/lib/integration/PluginManagement/uninstall.js +141 -0
  43. package/lib/integration/PluginManagement/unregister.js +101 -0
  44. package/lib/integration/PluginManagement/update.js +224 -0
  45. package/lib/integration/PluginManagement.js +21 -0
  46. package/lib/integration/Project.js +146 -0
  47. package/lib/integration/Target.js +296 -0
  48. package/lib/integration/getBowerRegistryConfig.js +34 -0
  49. package/lib/logger.js +28 -0
  50. package/lib/util/JSONReadValidate.js +34 -0
  51. package/lib/util/constants.js +38 -0
  52. package/lib/util/createPromptTask.js +7 -0
  53. package/lib/util/download.js +45 -0
  54. package/lib/util/errors.js +58 -0
  55. package/lib/util/extract.js +24 -0
  56. package/lib/util/getDirNameFromImportMeta.js +6 -0
  57. package/lib/util/promises.js +36 -0
  58. package/package.json +20 -29
  59. package/TESTING.md +0 -25
  60. package/bin/adapt +0 -3
  61. package/gruntfile.js +0 -18
  62. package/lib/AdaptConsoleApplication.js +0 -19
  63. package/lib/CommandParser.js +0 -19
  64. package/lib/CommandTranslator.js +0 -16
  65. package/lib/ConsoleRenderer.js +0 -10
  66. package/lib/Constants.js +0 -68
  67. package/lib/JsonLoader.js +0 -40
  68. package/lib/JsonWriter.js +0 -21
  69. package/lib/PackageMeta.js +0 -41
  70. package/lib/Plugin.js +0 -53
  71. package/lib/PluginTypeResolver.js +0 -47
  72. package/lib/Project.js +0 -89
  73. package/lib/RendererHelpers.js +0 -41
  74. package/lib/RepositoryDownloader.js +0 -64
  75. package/lib/Slug.js +0 -5
  76. package/lib/VersionChecker.js +0 -7
  77. package/lib/commands/create/index.js +0 -6
  78. package/lib/commands/index.js +0 -16
  79. package/lib/commands/install/InstallLog.js +0 -32
  80. package/lib/commands/install/InstallTarget.js +0 -259
  81. package/lib/commands/install/extend.js +0 -31
  82. package/lib/download.js +0 -101
  83. package/lib/errors.js +0 -58
  84. package/lib/promise/authenticate.js +0 -58
  85. package/lib/promise/build.js +0 -20
  86. package/lib/promise/cloneInstall.js +0 -35
  87. package/lib/promise/confirmBuild.js +0 -6
  88. package/lib/promise/exec.js +0 -39
  89. package/lib/promise/getRepository.js +0 -26
  90. package/lib/promise/highest.js +0 -109
  91. package/lib/promise/install.js +0 -31
  92. package/lib/promise/installAdaptDependencies.js +0 -30
  93. package/lib/promise/installNodeDependencies.js +0 -28
  94. package/lib/promise/removeTemporaryDownload.js +0 -8
  95. package/lib/promise/replaceTextContent.js +0 -10
  96. package/lib/promise/uninstallPackage.js +0 -15
  97. package/lib/promise/update.js +0 -33
  98. package/lib/promise/util.js +0 -16
  99. package/test/fixtures/adapt-with-plugins.json +0 -6
  100. package/test/specs/command_translation_concerns.js +0 -13
  101. package/test/specs/create_command_concerns.js +0 -22
  102. package/test/specs/create_concerns.js +0 -30
  103. package/test/specs/install_concerns.js +0 -31
  104. package/test/specs/installing_compatible_plugins_concerns.js +0 -126
  105. package/test/specs/installing_incompatible_plugins_concerns.js +0 -103
  106. package/test/specs/ls_concerns.js +0 -28
  107. package/test/specs/plugin_name_concerns.js +0 -82
  108. package/test/specs/project_concerns.js +0 -128
  109. package/test/specs/registration_concerns.js +0 -31
  110. package/test/specs/repository_downloader_concerns.js +0 -55
  111. package/test/specs/search_concerns.js +0 -30
  112. package/test/specs/type_resolution_concerns.js +0 -71
  113. package/test/specs/uninstall_command_concerns.js +0 -64
  114. 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
+ }