@42wp/dev-env 1.0.5 → 1.0.6

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/README.md CHANGED
@@ -76,6 +76,27 @@ pass `--force`.
76
76
  | `wpvip` | clone + mount the VIP mu-plugins |
77
77
  | `initial_content` | `true` (200 posts) or a number |
78
78
 
79
+ ### Project dependencies
80
+
81
+ Before installing WordPress, `42wp start` installs the project's dependencies in
82
+ your repo so WordPress and its mu-plugins load cleanly:
83
+
84
+ - `composer install` — when `composer.json` is present
85
+ - `<pm> install` — when `package.json` is present, where `<pm>` is inferred from the
86
+ lockfile: `pnpm-lock.yaml` → **pnpm**, `yarn.lock` → **yarn**, `package-lock.json`
87
+ → **npm** (defaults to npm)
88
+
89
+ These run **on your machine**, in the mounted repo, before the containers start.
90
+ **Composer is required**: if a project has a `composer.json` and its PHP
91
+ dependencies can't be installed (composer missing, or the install fails), `start`
92
+ stops — a project with unmet PHP deps would break during the WordPress install
93
+ anyway. The JS install is best-effort (a missing tool or failure just warns). Skip
94
+ the whole step with `--skip-deps`:
95
+
96
+ ```bash
97
+ 42wp start my-site --skip-deps
98
+ ```
99
+
79
100
  ### WordPress version
80
101
 
81
102
  By default projects are built on **`wordpress:php8.4-apache`** (newest WordPress,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@42wp/dev-env",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "42WP — a CLI to spin up disposable WordPress dev environments with Docker (Traefik + MySQL + per-project containers).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,6 +15,7 @@ import {
15
15
  } from '../lib/naming.js';
16
16
  import { projectDir } from '../lib/paths.js';
17
17
  import { readProjectConfig } from '../lib/projectConfig.js';
18
+ import { installProjectDeps } from '../lib/deps.js';
18
19
  import {
19
20
  missingWpDirs,
20
21
  pathExists,
@@ -213,6 +214,20 @@ export async function start(rawName, opts = {}) {
213
214
  await fs.writeFile(path.join(envDir, 'dev-helper.php'), await readTemplate('dev-helper.php'), 'utf8');
214
215
  }
215
216
 
217
+ // 4d. Install the project's dependencies (composer / JS) on the host BEFORE the
218
+ // containers start, so WordPress and its mu-plugins load cleanly during
219
+ // install. Composer is required — if its deps can't be installed the project
220
+ // would likely break during `wp core install`, so stop now. --skip-deps skips.
221
+ if (!opts['skip-deps']) {
222
+ try {
223
+ await installProjectDeps(repoDir);
224
+ } catch (err) {
225
+ error(err.message);
226
+ process.exitCode = 1;
227
+ return;
228
+ }
229
+ }
230
+
216
231
  // 5. Build & start project containers.
217
232
  step(t('start.upping'));
218
233
  await compose(['up', '-d', '--build'], { cwd: envDir });
@@ -0,0 +1,69 @@
1
+ // Installs a project's dependencies on the HOST (in the mounted repo) before the
2
+ // WordPress install, so WordPress and its mu-plugins load cleanly. Best-effort: a
3
+ // missing tool or a failed install warns and continues — it never aborts `start`.
4
+ //
5
+ // - PHP: `composer install` when composer.json is present. REQUIRED — a project
6
+ // whose PHP deps can't be installed would likely break during the WordPress
7
+ // install, so a missing composer or a failed install throws (aborts `start`).
8
+ // - JS: `<pm> install` when package.json is present, where <pm> is inferred from
9
+ // the lockfile (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, package-lock.json
10
+ // / npm-shrinkwrap.json -> npm), defaulting to npm. Best-effort (build
11
+ // assets don't block WordPress from booting), so it only warns on failure.
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+
16
+ import { run, capture } from './docker.js';
17
+ import { step } from './log.js';
18
+ import { t } from './i18n.js';
19
+
20
+ // Infer the JS package manager from the repo's lockfile; default to npm.
21
+ export function detectPackageManager(dir) {
22
+ if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) return 'pnpm';
23
+ if (fs.existsSync(path.join(dir, 'yarn.lock'))) return 'yarn';
24
+ if (fs.existsSync(path.join(dir, 'package-lock.json'))) return 'npm';
25
+ if (fs.existsSync(path.join(dir, 'npm-shrinkwrap.json'))) return 'npm';
26
+ return 'npm';
27
+ }
28
+
29
+ const has = (dir, name) => fs.existsSync(path.join(dir, name));
30
+
31
+ // Is a host command available? (spawns `<cmd> --version`).
32
+ async function toolAvailable(cmd) {
33
+ try {
34
+ const { code } = await capture(cmd, ['--version']);
35
+ return code === 0;
36
+ } catch {
37
+ return false; // ENOENT — not installed
38
+ }
39
+ }
40
+
41
+ // Run one dependency install (tool + args) in repoDir. When `required`, a missing
42
+ // tool or a failed install throws (so the caller can abort); otherwise it warns.
43
+ async function installWith(tool, args, repoDir, { required = false } = {}) {
44
+ if (!(await toolAvailable(tool))) {
45
+ if (required) throw new Error(t('deps.missingRequired', { tool }));
46
+ step(t('deps.missingTool', { tool }));
47
+ return;
48
+ }
49
+ step(t('deps.installing', { tool }));
50
+ try {
51
+ await run(tool, args, { cwd: repoDir });
52
+ } catch {
53
+ if (required) throw new Error(t('deps.failedRequired', { tool }));
54
+ step(t('deps.failed', { tool }));
55
+ }
56
+ }
57
+
58
+ // Install the project's PHP and JS dependencies on the host, in repoDir. Throws if
59
+ // composer is required but unavailable/failing (caller aborts `start`).
60
+ export async function installProjectDeps(repoDir) {
61
+ // Composer is required — a missing vendor/ autoload can fatal WordPress on load.
62
+ if (has(repoDir, 'composer.json')) {
63
+ await installWith('composer', ['install', '--no-interaction'], repoDir, { required: true });
64
+ }
65
+ // JS deps are best-effort (build assets, not needed for WordPress to boot).
66
+ if (has(repoDir, 'package.json')) {
67
+ await installWith(detectPackageManager(repoDir), ['install'], repoDir);
68
+ }
69
+ }
package/src/locales/en.js CHANGED
@@ -31,6 +31,13 @@ export default {
31
31
  'start.upping': 'Starting project containers...',
32
32
  'start.waitingWp': 'Waiting for the WordPress container to respond...',
33
33
  'start.installing': 'Running silent WordPress install...',
34
+ 'deps.installing': 'Installing project dependencies (${tool})...',
35
+ 'deps.failed': 'Could not install ${tool} dependencies (continuing anyway).',
36
+ 'deps.missingTool': '${tool} not found on your machine — skipping. Install it and re-run to set up dependencies.',
37
+ 'deps.missingRequired':
38
+ "${tool} is required to install this project's dependencies but was not found. Install ${tool} and re-run, or pass --skip-deps to skip.",
39
+ 'deps.failedRequired':
40
+ 'Failed to install ${tool} dependencies. Fix the error above and re-run, or pass --skip-deps to skip.',
34
41
  'start.installingLang': 'Installing WordPress language pack (${locale})...',
35
42
  'start.langFailed': 'Could not install the ${locale} language pack (continuing in English).',
36
43
  'start.activatingTheme': 'Activating theme (${theme})...',
package/src/locales/pt.js CHANGED
@@ -31,6 +31,13 @@ export default {
31
31
  'start.upping': 'Subindo containers do projeto...',
32
32
  'start.waitingWp': 'Aguardando o container WP responder...',
33
33
  'start.installing': 'Executando instalação silenciosa do WordPress...',
34
+ 'deps.installing': 'Instalando as dependências do projeto (${tool})...',
35
+ 'deps.failed': 'Não foi possível instalar as dependências de ${tool} (continuando mesmo assim).',
36
+ 'deps.missingTool': '${tool} não encontrado na sua máquina — pulando. Instale-o e rode novamente para configurar as dependências.',
37
+ 'deps.missingRequired':
38
+ '${tool} é necessário para instalar as dependências deste projeto, mas não foi encontrado. Instale o ${tool} e rode novamente, ou use --skip-deps para pular.',
39
+ 'deps.failedRequired':
40
+ 'Falha ao instalar as dependências de ${tool}. Corrija o erro acima e rode novamente, ou use --skip-deps para pular.',
34
41
  'start.installingLang': 'Instalando o pacote de idioma do WordPress (${locale})...',
35
42
  'start.langFailed': 'Não foi possível instalar o pacote de idioma ${locale} (continuando em inglês).',
36
43
  'start.activatingTheme': 'Ativando o tema (${theme})...',