@scandipwa/magento-scripts 1.11.3 → 1.12.2

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 (97) hide show
  1. package/.eslintrc +0 -0
  2. package/LICENSE +0 -0
  3. package/lib/config/dependencies-for-platforms.js +0 -1
  4. package/lib/config/index.js +4 -2
  5. package/lib/config/{php.js → php-config.js} +0 -0
  6. package/lib/config/phpbrew.js +12 -0
  7. package/lib/config/phpstorm/debug-config.js +46 -0
  8. package/lib/config/save-config.js +0 -0
  9. package/lib/config/templates/Project_Default.template.xml +13 -0
  10. package/lib/config/templates/cma-config.template.js +0 -0
  11. package/lib/config/templates/dataSources.local.template.xml +11 -0
  12. package/lib/config/templates/dataSources.template.xml +12 -0
  13. package/lib/config/templates/magentorc.template +0 -0
  14. package/lib/config/templates/php-fpm.template.conf +0 -0
  15. package/lib/config/templates/php.template.xml +14 -0
  16. package/lib/config/templates/vscode-launch.template.json +15 -0
  17. package/lib/config/templates/workspace.template.xml +20 -0
  18. package/lib/config/versions/magento-2.3.0.js +1 -1
  19. package/lib/config/versions/magento-2.3.1.js +1 -1
  20. package/lib/config/versions/magento-2.3.2-p2.js +1 -1
  21. package/lib/config/versions/magento-2.3.2.js +1 -1
  22. package/lib/config/versions/magento-2.3.3-p1.js +1 -1
  23. package/lib/config/versions/magento-2.3.3.js +1 -1
  24. package/lib/config/versions/magento-2.3.4-p2.js +1 -1
  25. package/lib/config/versions/magento-2.3.4.js +1 -1
  26. package/lib/config/versions/magento-2.3.5-p1.js +1 -1
  27. package/lib/config/versions/magento-2.3.5-p2.js +1 -1
  28. package/lib/config/versions/magento-2.3.5.js +1 -1
  29. package/lib/config/versions/magento-2.3.6-p1.js +1 -1
  30. package/lib/config/versions/magento-2.3.6.js +1 -1
  31. package/lib/config/versions/magento-2.3.7-p1.js +3 -3
  32. package/lib/config/versions/magento-2.3.7.js +3 -3
  33. package/lib/config/versions/magento-2.4.0-p1.js +2 -2
  34. package/lib/config/versions/magento-2.4.0.js +2 -2
  35. package/lib/config/versions/magento-2.4.1-p1.js +2 -2
  36. package/lib/config/versions/magento-2.4.1.js +2 -2
  37. package/lib/config/versions/magento-2.4.2-p1.js +3 -3
  38. package/lib/config/versions/magento-2.4.2-p2.js +3 -3
  39. package/lib/config/versions/magento-2.4.2.js +3 -3
  40. package/lib/config/versions/magento-2.4.3-p1.js +3 -3
  41. package/lib/config/versions/magento-2.4.3.js +3 -3
  42. package/lib/tasks/cache/index.js +0 -0
  43. package/lib/tasks/cli/index.js +0 -0
  44. package/lib/tasks/composer/index.js +13 -6
  45. package/lib/tasks/docker/index.js +1 -1
  46. package/lib/tasks/docker/network.js +2 -2
  47. package/lib/tasks/execute/index.js +0 -0
  48. package/lib/tasks/file-system/create-php-storm-config.js +82 -0
  49. package/lib/tasks/file-system/create-vscode-config.js +111 -0
  50. package/lib/tasks/file-system/index.js +5 -1
  51. package/lib/tasks/magento/index.js +0 -0
  52. package/lib/tasks/magento/setup-magento/adjust-magento-configuration.js +0 -0
  53. package/lib/tasks/magento/setup-magento/configure-elasticsearch.js +0 -0
  54. package/lib/tasks/magento/setup-magento/create-admin.js +0 -0
  55. package/lib/tasks/magento/setup-magento/delete-admin-users.js +0 -0
  56. package/lib/tasks/magento/setup-magento/flush-redis-config.js +0 -0
  57. package/lib/tasks/magento/setup-magento/increase-admin-session-lifetime.js +0 -0
  58. package/lib/tasks/magento/setup-magento/set-base-url.js +0 -0
  59. package/lib/tasks/magento/setup-magento/set-deployment-mode.js +1 -1
  60. package/lib/tasks/magento/setup-magento/set-url-rewrite.js +0 -0
  61. package/lib/tasks/magento/setup-magento/waiting-for-redis.js +1 -1
  62. package/lib/tasks/mysql/index.js +0 -0
  63. package/lib/tasks/php/compile.js +2 -2
  64. package/lib/tasks/php/configure.js +80 -59
  65. package/lib/tasks/php/extensions/disable.js +41 -0
  66. package/lib/tasks/php/extensions/enable.js +44 -0
  67. package/lib/tasks/php/extensions/install.js +47 -0
  68. package/lib/tasks/php/update-phpbrew.js +0 -0
  69. package/lib/tasks/php-fpm/index.js +0 -0
  70. package/lib/tasks/php-fpm/start-php-fpm.js +3 -3
  71. package/lib/tasks/php-fpm/stop-php-fpm.js +2 -2
  72. package/lib/tasks/requirements/composer.js +1 -1
  73. package/lib/tasks/requirements/dependency/arch.js +0 -0
  74. package/lib/tasks/requirements/dependency/centos.js +2 -2
  75. package/lib/tasks/requirements/dependency/fedora.js +0 -0
  76. package/lib/tasks/requirements/dependency/index.js +1 -4
  77. package/lib/tasks/requirements/dependency/mac.js +0 -0
  78. package/lib/tasks/requirements/dependency/ubuntu.js +0 -0
  79. package/lib/tasks/requirements/docker/index.js +2 -2
  80. package/lib/util/clean-object.js +0 -0
  81. package/lib/util/config-file-validator.js +7 -1
  82. package/lib/util/download-file.js +0 -0
  83. package/lib/util/ip.js +0 -0
  84. package/lib/util/match-filesystem.js +0 -0
  85. package/lib/util/move-file.js +0 -0
  86. package/lib/util/path-exists-sync.js +0 -0
  87. package/lib/util/portscanner.js +0 -0
  88. package/lib/util/run-composer.js +0 -0
  89. package/lib/util/run-magento.js +0 -0
  90. package/lib/util/run-php.js +0 -0
  91. package/lib/util/set-config.js +0 -0
  92. package/lib/util/sleep.js +0 -0
  93. package/lib/util/wait-for-it.js +0 -0
  94. package/lib/util/wait-for-logs.js +0 -0
  95. package/package.json +3 -2
  96. package/typings/context.d.ts +2 -2
  97. package/typings/index.d.ts +36 -2
@@ -32,8 +32,12 @@ const createComposerDir = async ({ composer }) => {
32
32
  };
33
33
 
34
34
  const downloadComposerBinary = async ({ composer }) => {
35
+ const composerVersion = /^\d$/.test(composer.version)
36
+ ? `latest-${composer.version}.x`
37
+ : composer.version;
38
+
35
39
  try {
36
- await downloadFile(`https://getcomposer.org/download/latest-${composer.version}.x/composer.phar`, {
40
+ await downloadFile(`https://getcomposer.org/download/${composerVersion}/composer.phar`, {
37
41
  destination: composer.binPath
38
42
  });
39
43
  } catch (e) {
@@ -48,7 +52,7 @@ const downloadComposerBinary = async ({ composer }) => {
48
52
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
49
53
  */
50
54
  const installComposer = () => ({
51
- title: 'Installing composer',
55
+ title: 'Installing Composer',
52
56
  task: async (ctx, task) => {
53
57
  const { composer, php } = ctx.config;
54
58
  const hasComposerInCache = await pathExists(composer.binPath);
@@ -59,11 +63,14 @@ const installComposer = () => ({
59
63
  await downloadComposerBinary({ composer });
60
64
  } else {
61
65
  const currentComposerVersion = await getComposerVersion({ composer, php });
66
+ const expectedComposerVersion = /^\d$/.test(composer.version)
67
+ ? `${composer.version}.x`
68
+ : composer.version;
62
69
 
63
- if (!semver.satisfies(currentComposerVersion, `${composer.version}.x`)) {
70
+ if (!semver.satisfies(currentComposerVersion, expectedComposerVersion)) {
64
71
  const continueComposerBinaryVersionSwitch = await task.prompt({
65
72
  type: 'Select',
66
- message: `You have Composer ${logger.style.misc(`${currentComposerVersion}`)} while your Magento version requires ${logger.style.misc(`${composer.version}.x`)}!`,
73
+ message: `You have Composer ${logger.style.misc(`${currentComposerVersion}`)} while your Magento version requires ${logger.style.misc(expectedComposerVersion)}!`,
67
74
  choices: [
68
75
  {
69
76
  message: `Continue with current installed version (${logger.style.misc(`${currentComposerVersion}`)})`,
@@ -86,7 +93,7 @@ const installComposer = () => ({
86
93
  break;
87
94
  }
88
95
  case 'exit': {
89
- throw new Error(`Current composer version ${logger.style.misc(`v${currentComposerVersion}`)} is not compatible with version ${logger.style.misc(`${composer.version}.x`)}!`);
96
+ throw new Error(`Current composer version ${logger.style.misc(`v${currentComposerVersion}`)} is not compatible with version ${logger.style.misc(expectedComposerVersion)}!`);
90
97
  }
91
98
  case 'continue':
92
99
  default: {
@@ -98,7 +105,7 @@ const installComposer = () => ({
98
105
  }
99
106
 
100
107
  const composerVersion = await getComposerVersion({ composer, php });
101
- task.title = `Using composer version ${composerVersion}`;
108
+ task.title = `Using Composer version ${composerVersion}`;
102
109
  ctx.composerVersion = composerVersion;
103
110
  },
104
111
  options: {
@@ -6,7 +6,7 @@ const volumes = require('./volumes');
6
6
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
7
7
  */
8
8
  const startServices = () => ({
9
- title: 'Starting docker services',
9
+ title: 'Starting Docker services',
10
10
  task: (ctx, task) => task.newListr([
11
11
  {
12
12
  title: 'Preparing services',
@@ -5,7 +5,7 @@ const { execAsyncSpawn } = require('../../util/exec-async-command');
5
5
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
6
6
  */
7
7
  const createNetwork = () => ({
8
- title: 'Deploying docker network',
8
+ title: 'Deploying Docker network',
9
9
  task: async ({ config: { docker } }, task) => {
10
10
  const networkList = (await execAsyncSpawn("docker network ls --format '{{.Name}}'")).split('\n');
11
11
 
@@ -41,7 +41,7 @@ Use command ${logger.style.command('docker network prune')}`);
41
41
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
42
42
  */
43
43
  const removeNetwork = () => ({
44
- title: 'Removing docker network',
44
+ title: 'Removing Docker network',
45
45
  task: async ({ config: { docker } }, task) => {
46
46
  const networkList = (await execAsyncSpawn("docker network ls --format '{{.Name}}'")).split('\n');
47
47
 
File without changes
@@ -0,0 +1,82 @@
1
+ const setConfigFile = require('../../util/set-config');
2
+ const pathExists = require('../../util/path-exists');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+
6
+ const createPhpStormConfig = () => ({
7
+ title: 'Setting PHPStorm config',
8
+ task: async ({ config: { phpStorm }, ports }) => {
9
+ const { phpLanguageLevel } = phpStorm.php;
10
+ const jdbcUrl = `jdbc:mysql://localhost:${ports.mysql}/magento`;
11
+
12
+ try {
13
+ await setConfigFile({
14
+ configPathname: phpStorm.xdebug.path,
15
+ template: phpStorm.xdebug.templatePath,
16
+ overwrite: true,
17
+ templateArgs: {
18
+ phpStorm
19
+ }
20
+ });
21
+ } catch (e) {
22
+ throw new Error(`Unexpected error accrued during workspace.xml config creation\n\n${e}`);
23
+ }
24
+
25
+ try {
26
+ await setConfigFile({
27
+ configPathname: phpStorm.php.path,
28
+ template: phpStorm.php.templatePath,
29
+ overwrite: true,
30
+ templateArgs: {
31
+ phpLanguageLevel
32
+ }
33
+ });
34
+ } catch (e) {
35
+ throw new Error(`Unexpected error accrued during php.xml config creation\n\n${e}`);
36
+ }
37
+
38
+ try {
39
+ await setConfigFile({
40
+ configPathname: phpStorm.database.dataSourcesLocal.path,
41
+ template: phpStorm.database.dataSourcesLocal.templatePath,
42
+ overwrite: true,
43
+ templateArgs: {
44
+ phpStorm
45
+ }
46
+ });
47
+ } catch (e) {
48
+ throw new Error(`Unexpected error accrued during dataSources.local.xml config creation\n\n${e}`);
49
+ }
50
+
51
+ try {
52
+ await setConfigFile({
53
+ configPathname: phpStorm.database.dataSources.path,
54
+ template: phpStorm.database.dataSources.templatePath,
55
+ overwrite: true,
56
+ templateArgs: {
57
+ phpStorm,
58
+ jdbcUrl
59
+ }
60
+ });
61
+ } catch (e) {
62
+ throw new Error(`Unexpected error accrued during dataSources.xml config creation\n\n${e}`);
63
+ }
64
+
65
+ if (!await pathExists(path.resolve('./.idea/dataSources'))) {
66
+ await fs.promises.mkdir(path.resolve('./.idea/dataSources'));
67
+ }
68
+
69
+ try {
70
+ await setConfigFile({
71
+ configPathname: phpStorm.inspectionTools.path,
72
+ template: phpStorm.inspectionTools.templatePath,
73
+ overwrite: true,
74
+ templateArgs: {}
75
+ });
76
+ } catch (e) {
77
+ throw new Error(`Unexpected error accrued during Project_Default.xml config creation\n\n${e}`);
78
+ }
79
+ }
80
+ });
81
+
82
+ module.exports = createPhpStormConfig;
@@ -0,0 +1,111 @@
1
+ const path = require('path');
2
+ const semver = require('semver');
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const hjson = require('hjson');
6
+ const pathExists = require('../../util/path-exists');
7
+ const setConfigFile = require('../../util/set-config');
8
+
9
+ const phpXDebug2port = 9111;
10
+ const phpXDebug3port = 9003;
11
+ const listenForXDebugConfigName = 'Listen for XDebug';
12
+
13
+ const vscodeLaunchConfigPath = path.join(process.cwd(), '.vscode', 'launch.json');
14
+
15
+ /**
16
+ * @param {import('../../../typings/context').ListrContext['config']['php']} php
17
+ */
18
+ const getCurrentXDebugPort = (php) => (semver.satisfies(php.extensions.xdebug.version, '>=3')
19
+ ? phpXDebug3port
20
+ : phpXDebug2port);
21
+
22
+ /**
23
+ * @param {import('../../../typings/context').ListrContext['config']['php']} php
24
+ */
25
+ const getPHPBinPathWithHomeVariable = (php) => php.binPath.replace(os.homedir(), '$HOME');
26
+ /**
27
+ * @param {import('../../../typings/context').ListrContext['config']['php']} php
28
+ */
29
+ const addPHPDebugConfig = (vscodeLaunchConfig, php) => {
30
+ const phpXDebugConfig = vscodeLaunchConfig.configurations.find(
31
+ ({ name }) => name === listenForXDebugConfigName
32
+ );
33
+
34
+ const phpXDebugPort = getCurrentXDebugPort(php);
35
+ const phpBingPath = getPHPBinPathWithHomeVariable(php);
36
+
37
+ if (!phpXDebugConfig) {
38
+ vscodeLaunchConfig.configurations.push({
39
+ name: listenForXDebugConfigName,
40
+ type: 'php',
41
+ request: 'launch',
42
+ port: phpXDebugPort,
43
+ runtimeExecutable: phpBingPath
44
+ });
45
+
46
+ return true;
47
+ }
48
+
49
+ if (
50
+ phpXDebugConfig.port !== phpXDebugPort
51
+ && phpXDebugConfig.runtimeExecutable !== phpBingPath
52
+ ) {
53
+ phpXDebugConfig.port = phpXDebugPort;
54
+ phpXDebugConfig.runtimeExecutable = phpBingPath;
55
+
56
+ return true;
57
+ }
58
+
59
+ return false;
60
+ };
61
+
62
+ /**
63
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
64
+ */
65
+ const createPhpFpmConfig = () => ({
66
+ title: 'Setting VSCode config',
67
+ task: async ({ config: { php, baseConfig } }, task) => {
68
+ if (await pathExists(vscodeLaunchConfigPath)) {
69
+ const vscodeLaunchConfig = hjson.parse(await fs.promises.readFile(vscodeLaunchConfigPath, 'utf-8'), {
70
+ keepWsc: true
71
+ });
72
+
73
+ const vscodeConfigEdited = addPHPDebugConfig(vscodeLaunchConfig, php);
74
+
75
+ // if vscode config is up-to-date, skip task
76
+ if (!vscodeConfigEdited) {
77
+ task.skip();
78
+ return;
79
+ }
80
+
81
+ await fs.promises.writeFile(vscodeLaunchConfigPath, hjson.stringify(vscodeLaunchConfig));
82
+
83
+ return;
84
+ }
85
+
86
+ if (!await pathExists(path.join(process.cwd(), '.vscode'))) {
87
+ try {
88
+ await fs.promises.mkdir(path.join(process.cwd(), '.vscode'));
89
+ } catch (e) {
90
+ throw new Error(`Unable to creade .vscode directory in your project!\n\n${e}`);
91
+ }
92
+ }
93
+
94
+ try {
95
+ const phpXDebugPort = getCurrentXDebugPort(php);
96
+ const vscodeLaunchConfigTemplatePath = path.join(baseConfig.templateDir, 'vscode-launch.template.json');
97
+ await setConfigFile({
98
+ template: vscodeLaunchConfigTemplatePath,
99
+ configPathname: vscodeLaunchConfigPath,
100
+ templateArgs: {
101
+ XDebugPort: phpXDebugPort,
102
+ PHPBinPath: getPHPBinPathWithHomeVariable(php)
103
+ }
104
+ });
105
+ } catch (e) {
106
+ throw new Error(`Unexpected error accrued during launch.json config creation!\n\n${e}`);
107
+ }
108
+ }
109
+ });
110
+
111
+ module.exports = createPhpFpmConfig;
@@ -1,6 +1,8 @@
1
1
  const createNginxConfig = require('./create-nginx-config');
2
2
  const createPhpConfig = require('./create-php-config');
3
3
  const createPhpFpmConfig = require('./create-php-fpm-config');
4
+ const createPhpStormConfig = require('./create-php-storm-config');
5
+ const createVSCodeConfig = require('./create-vscode-config');
4
6
 
5
7
  /**
6
8
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
@@ -10,7 +12,9 @@ const prepareFileSystem = () => ({
10
12
  task: (ctx, task) => task.newListr([
11
13
  createNginxConfig(),
12
14
  createPhpFpmConfig(),
13
- createPhpConfig()
15
+ createPhpConfig(),
16
+ createPhpStormConfig(),
17
+ createVSCodeConfig()
14
18
  ], {
15
19
  concurrent: true
16
20
  })
File without changes
File without changes
File without changes
@@ -5,7 +5,7 @@ const runMagentoCommand = require('../../../util/run-magento');
5
5
  * @type {() => import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
6
6
  */
7
7
  module.exports = () => ({
8
- title: 'Switching magento mode',
8
+ title: 'Switching Magento mode',
9
9
  task: async ({ magentoVersion, config: { magentoConfiguration: { mode } } }, task) => {
10
10
  const { result } = await runMagentoCommand('deploy:mode:show', {
11
11
  throwNonZeroCode: false,
@@ -4,7 +4,7 @@ const waitForIt = require('../../../util/wait-for-it');
4
4
  * @type {() => import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
5
5
  */
6
6
  module.exports = () => ({
7
- title: 'Waiting for redis',
7
+ title: 'Waiting for Redis',
8
8
  task: async ({ ports }, task) => {
9
9
  await waitForIt({
10
10
  name: 'redis',
File without changes
@@ -17,8 +17,8 @@ const compile = () => ({
17
17
  platformCompileOptions.extraOptions.push('--with-libdir=lib64');
18
18
  }
19
19
  }
20
- const exportEnv = Object.entries(platformCompileOptions.env || {}).map(([key, value]) => `export ${key}="${value}"`).join(' && ');
21
- const phpCompileCommand = `${exportEnv ? `${exportEnv} && ` : ''} \
20
+ const commandEnv = Object.entries(platformCompileOptions.env || {}).map(([key, value]) => `${key}="${value}"`).join(' && ');
21
+ const phpCompileCommand = `${commandEnv ? `${commandEnv} && ` : ''} \
22
22
  phpbrew install -j ${platformCompileOptions.cpuCount} ${php.version} ${platformCompileOptions.variants.join(' ')} \
23
23
  -- ${platformCompileOptions.extraOptions.join(' ')}`;
24
24
 
@@ -1,14 +1,19 @@
1
1
  /* eslint-disable max-len */
2
+ const path = require('path');
3
+ const fs = require('fs');
2
4
  const { execAsyncSpawn } = require('../../util/exec-async-command');
3
- const macosVersion = require('macos-version');
5
+ const pathExists = require('../../util/path-exists');
6
+ const enableExtension = require('./extensions/enable');
7
+ const installExtension = require('./extensions/install');
8
+ const disableExtension = require('./extensions/disable');
9
+ const phpbrewConfig = require('../../config/phpbrew');
4
10
 
5
11
  /**
6
- * Get installed modules list with versions
7
- * @param {Object} param0
8
- * @param {import('../../../typings/context').ListrContext['config']['php']} param0.php
12
+ * Get enabled extensions list with versions
13
+ * @param {import('../../../typings/context').ListrContext['config']} param0
9
14
  * @returns {Promise<{[key: string]: string}}>}
10
15
  */
11
- const getInstalledModules = async ({ php }) => {
16
+ const getEnabledExtensions = async ({ php }) => {
12
17
  const output = await execAsyncSpawn(
13
18
  `${ php.binPath } -c ${php.iniPath} -r 'foreach (get_loaded_extensions() as $extension) echo "$extension:" . phpversion($extension) . "\n";'`
14
19
  );
@@ -25,44 +30,59 @@ const getInstalledModules = async ({ php }) => {
25
30
  };
26
31
 
27
32
  /**
28
- * @type {(extensions: string[]) => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
33
+ * Get disabled extensions list
34
+ * @param {import('../../../typings/context').ListrContext['config']} param0
35
+ * @returns {Promise<string[]>}
29
36
  */
30
- const disablingExtensions = (extensions) => ({
31
- title: `Disabling extensions ${extensions.join(', ')}`,
32
- task: async ({ config: { overridenConfiguration: { configuration: { php } } } }, task) => {
33
- try {
34
- for (const extension of extensions) {
35
- await execAsyncSpawn(`
36
- source ~/.phpbrew/bashrc && \
37
- phpbrew use ${ php.version } && \
38
- phpbrew ext disable ${extension}`, {
39
- callback: (t) => {
40
- task.output = t;
41
- }
42
- });
43
- }
44
- } catch (e) {
45
- throw new Error(`Something went wrong during extension disabling.\n\n${e}`);
46
- }
47
- },
48
- options: {
49
- bottomBar: 10
37
+ const getDisabledExtensions = async ({ php }) => {
38
+ const extensionsIniDirectory = path.join(phpbrewConfig.phpPath, `php-${php.version}`, 'var', 'db');
39
+
40
+ if (!await pathExists(extensionsIniDirectory)) {
41
+ return [];
50
42
  }
51
- });
43
+
44
+ const extensionIniList = await fs.promises.readdir(
45
+ extensionsIniDirectory,
46
+ {
47
+ encoding: 'utf-8',
48
+ withFileTypes: true
49
+ }
50
+ );
51
+
52
+ return extensionIniList.filter((f) => f.isFile() && f.name.endsWith('.disabled')).map((f) => f.name.replace('.disabled', ''));
53
+ };
52
54
 
53
55
  /**
54
- * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
56
+ * Get installed extensions
57
+ * @param {import('../../../typings/context').ListrContext['config']} param0
58
+ * @returns {Promise<string[]>}
59
+ */
60
+ const getInstalledExtensions = async ({ php }) => {
61
+ const extensionDirectory = path.join(phpbrewConfig.buildPath, `php-${php.version}`, 'ext');
62
+
63
+ const availableExtensions = await fs.promises.readdir(extensionDirectory, {
64
+ encoding: 'utf-8'
65
+ });
66
+
67
+ return availableExtensions;
68
+ };
69
+
70
+ /**
71
+ * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
55
72
  */
56
73
  const configure = () => ({
57
74
  title: 'Configuring PHP extensions',
58
75
  task: async ({ config, debug }, task) => {
59
- const { php, overridenConfiguration: { configuration: { php: { disabledExtensions = [] } } } } = config;
60
- const loadedModules = await getInstalledModules({ php });
76
+ const { php, php: { disabledExtensions = [] } } = config;
77
+ const enabledExtensions = await getEnabledExtensions(config);
78
+ const installedExtensions = await getInstalledExtensions(config);
79
+ const disabledExtensionsInPHP = await getDisabledExtensions(config);
61
80
 
62
- if (!debug && loadedModules.xdebug && !disabledExtensions.includes('xdebug')) {
81
+ if (!debug && enabledExtensions.xdebug && !disabledExtensions.includes('xdebug')) {
63
82
  disabledExtensions.push('xdebug');
64
83
  }
65
84
 
85
+ /** @type {[string, import('../../../typings/index').PHPExtension][]} */
66
86
  const missingExtensions = Object.entries(php.extensions)
67
87
  .filter(([name, options]) => {
68
88
  const extensionName = options.extensionName || name;
@@ -75,49 +95,50 @@ const configure = () => ({
75
95
  if (extensionName === 'xdebug' && !debug) {
76
96
  return false;
77
97
  }
78
- if (!loadedModules[extensionName]) {
98
+ if (!enabledExtensions[extensionName]) {
79
99
  return true;
80
100
  }
81
101
 
82
- if (options && options.version && loadedModules[extensionName] !== options.version) {
102
+ if (options && options.version && enabledExtensions[extensionName] !== options.version) {
83
103
  return true;
84
104
  }
85
105
 
86
106
  return false;
87
107
  });
88
108
 
109
+ /** @type {import('listr2').ListrTask[]} */
110
+ const extensionTasks = [];
111
+
89
112
  if (missingExtensions.length > 0) {
90
- try {
91
- for (const [extensionName, extensionOptions] of missingExtensions) {
92
- const options = macosVersion.isMacOS ? extensionOptions.macOptions : extensionOptions.options;
93
- const { hooks = {} } = extensionOptions;
94
-
95
- if (hooks.preInstall) {
96
- await Promise.resolve(hooks.preInstall(config));
97
- }
98
- await execAsyncSpawn(`source ~/.phpbrew/bashrc && \
99
- phpbrew use ${ php.version } && \
100
- phpbrew ext install ${ extensionName }${ extensionOptions.version ? ` ${extensionOptions.version}` : ''}${ options ? ` -- ${ options }` : ''}`,
101
- {
102
- callback: (t) => {
103
- task.output = t;
104
- }
105
- });
106
-
107
- if (hooks.postInstall) {
108
- await Promise.resolve(hooks.postInstall(config));
109
- }
113
+ missingExtensions.forEach(([extensionName, extensionOptions]) => {
114
+ if (installedExtensions.includes(extensionName) && disabledExtensionsInPHP.includes(extensionName)) {
115
+ extensionTasks.push(enableExtension(extensionName, extensionOptions));
116
+ } else {
117
+ extensionTasks.push(installExtension(extensionName, extensionOptions));
110
118
  }
111
- } catch (e) {
112
- throw new Error(`Something went wrong during the extension installation.\n\n${e}`);
113
- }
119
+ });
114
120
  }
115
121
 
116
122
  if (disabledExtensions.length > 0) {
117
- return task.newListr(
118
- disablingExtensions(disabledExtensions)
119
- );
123
+ disabledExtensions.forEach((extensionName) => {
124
+ extensionTasks.push(disableExtension(extensionName));
125
+ });
126
+ }
127
+
128
+ if (extensionTasks.length === 0) {
129
+ task.skip();
130
+ return;
120
131
  }
132
+
133
+ return task.newListr(
134
+ extensionTasks,
135
+ {
136
+ concurrent: false,
137
+ rendererOptions: {
138
+ collapse: false
139
+ }
140
+ }
141
+ );
121
142
  },
122
143
  options: {
123
144
  bottomBar: 10
@@ -0,0 +1,41 @@
1
+ /* eslint-disable max-len */
2
+ const { execAsyncSpawn } = require('../../../util/exec-async-command');
3
+
4
+ /**
5
+ * @param {String} extensionName
6
+ * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
7
+ */
8
+ const disableExtension = (extensionName) => ({
9
+ title: `Disabling ${extensionName} extension`,
10
+ task: async (ctx, task) => {
11
+ const {
12
+ config,
13
+ config: { php }
14
+ } = ctx;
15
+ const extensionOptions = php.extensions[extensionName] || {};
16
+ const { hooks } = extensionOptions;
17
+ if (extensionOptions.disable) {
18
+ await Promise.resolve(extensionOptions.disable(ctx, task));
19
+ } else {
20
+ if (hooks && hooks.preDisable) {
21
+ await Promise.resolve(hooks.preDisable(config));
22
+ }
23
+ await execAsyncSpawn(`source ~/.phpbrew/bashrc && \
24
+ phpbrew use ${ php.version } && \
25
+ phpbrew ext disable ${ extensionName }`,
26
+ {
27
+ callback: (t) => {
28
+ task.output = t;
29
+ }
30
+ });
31
+ if (hooks && hooks.postDisable) {
32
+ await Promise.resolve(hooks.postDisable(config));
33
+ }
34
+ }
35
+ },
36
+ options: {
37
+ bottomBar: 10
38
+ }
39
+ });
40
+
41
+ module.exports = disableExtension;
@@ -0,0 +1,44 @@
1
+ /* eslint-disable max-len */
2
+ const { execAsyncSpawn } = require('../../../util/exec-async-command');
3
+
4
+ /**
5
+ * @param {String} extensionName
6
+ * @param {import('../../../../typings/index').PHPExtension} extensionOptions
7
+ * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
8
+ */
9
+ const enableExtension = (extensionName, extensionOptions) => ({
10
+ title: `Enabling ${extensionName} extension`,
11
+ task: async (ctx, task) => {
12
+ const {
13
+ config,
14
+ config: { php }
15
+ } = ctx;
16
+ const { hooks } = extensionOptions;
17
+
18
+ if (extensionOptions.enable) {
19
+ await Promise.resolve(extensionOptions.enable(ctx, task));
20
+ } else {
21
+ if (hooks && hooks.postEnable) {
22
+ await Promise.resolve(hooks.postEnable(config));
23
+ }
24
+
25
+ await execAsyncSpawn(`source ~/.phpbrew/bashrc && \
26
+ phpbrew use ${ php.version } && \
27
+ phpbrew ext enable ${ extensionName }`,
28
+ {
29
+ callback: (t) => {
30
+ task.output = t;
31
+ }
32
+ });
33
+
34
+ if (hooks && hooks.postEnable) {
35
+ await Promise.resolve(hooks.postEnable(config));
36
+ }
37
+ }
38
+ },
39
+ options: {
40
+ bottomBar: 10
41
+ }
42
+ });
43
+
44
+ module.exports = enableExtension;