Skip to content

Commit 9fc58f7

Browse files
authored
[ xdebug ] Add --experimental-unsafe-ide-integration option in PHP.wasm CLI (#2947)
## Motivation for the change, related issues Based on issue #2763 and following pull request #2777 This pull request generates the IDE configuration files inside the developer's IDE. ## Implementation details - It first clears all the configs named `PHP.wasm CLI - Listen for Xdebug` in VSCode and PHPStorm config files. - If `--xdebug` and `--experimental-unsafe-ide-integration` options are present, we add IDE configs in the related configs. - PHPStorm : it adds a new `server` with name `PHP.wasm CLI - Listen for Xdebug` in `.idea/workspace.xml`. - VSCode : it adds a new `configuration` with name `PHP.wasm CLI - Listen for Xdebug` in `.vscode/launch.json`. - Correction of an error in Playground CLI console.logs. - Creation of a new distinct package named `@php-wasm/cli-util` which is integrated into PHP.wasm CLI and Playground CLI with the `xdebug-path-mappings`. Next steps are reported in the [Xdebug Follow-up issue](#2315). ## Testing Instructions (or ideally a Blueprint) CI 🧪 test-php-wasm-cli-util cc @fellyph
1 parent 0c91d35 commit 9fc58f7

File tree

20 files changed

+648
-203
lines changed

20 files changed

+648
-203
lines changed

package-lock.json

Lines changed: 118 additions & 102 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"extends": ["../../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
}
17+
]
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# php-wasm-cli-util
2+
3+
This library was generated with [Nx](https://nx.dev).
4+
5+
## Building
6+
7+
Run `nx build php-wasm-cli-util` to build the library.
8+
9+
## Running unit tests
10+
11+
Run `nx test php-wasm-cli-util` to execute the unit tests via [Vitest](https://vitest.dev).
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@php-wasm/cli-util",
3+
"version": "3.0.22",
4+
"description": "Utilities for PHP.wasm related CLIs",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/WordPress/wordpress-playground"
8+
},
9+
"homepage": "https://developer.wordpress.org/playground",
10+
"author": "The WordPress contributors",
11+
"exports": {
12+
".": {
13+
"import": "./index.js",
14+
"require": "./index.cjs"
15+
},
16+
"./package.json": "./package.json",
17+
"./README.md": "./README.md"
18+
},
19+
"main": "./index.cjs",
20+
"module": "./index.js",
21+
"type": "module",
22+
"types": "index.d.ts",
23+
"typedoc": {
24+
"entryPoint": "./src/index.ts",
25+
"readmeFile": "./README.md",
26+
"displayName": "@php-wasm/cli-util",
27+
"tsconfig": "./tsconfig.lib.json"
28+
},
29+
"publishConfig": {
30+
"access": "public",
31+
"directory": "../../../dist/packages/php-wasm/cli-util"
32+
},
33+
"license": "GPL-2.0-or-later",
34+
"gitHead": "2f8d8f3cea548fbd75111e8659a92f601cddc593",
35+
"engines": {
36+
"node": ">=20.18.3",
37+
"npm": ">=10.1.0"
38+
}
39+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"name": "php-wasm-cli-util",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "packages/php-wasm/cli-util/src",
5+
"projectType": "library",
6+
"targets": {
7+
"build": {
8+
"executor": "nx:noop",
9+
"dependsOn": ["build:README"]
10+
},
11+
"build:README": {
12+
"executor": "nx:run-commands",
13+
"options": {
14+
"commands": [
15+
"cp packages/php-wasm/cli-util/README.md dist/packages/php-wasm/cli-util"
16+
]
17+
},
18+
"dependsOn": ["build:package-json"]
19+
},
20+
"build:package-json": {
21+
"executor": "@wp-playground/nx-extensions:package-json",
22+
"options": {
23+
"tsConfig": "packages/php-wasm/cli-util/tsconfig.lib.json",
24+
"outputPath": "dist/packages/php-wasm/cli-util",
25+
"buildTarget": "php-wasm-cli-util:build:bundle:production"
26+
}
27+
},
28+
"build:bundle": {
29+
"executor": "@nx/vite:build",
30+
"outputs": ["{options.outputPath}"],
31+
"options": {
32+
"emptyOutDir": false,
33+
"outputPath": "dist/packages/php-wasm/cli-util"
34+
}
35+
},
36+
"publish": {
37+
"executor": "nx:run-commands",
38+
"options": {
39+
"command": "node tools/scripts/publish.mjs php-wasm-cli-util {args.ver} {args.tag}"
40+
},
41+
"dependsOn": ["build"]
42+
},
43+
"package-for-self-hosting": {
44+
"executor": "@wp-playground/nx-extensions:package-for-self-hosting",
45+
"dependsOn": ["build"]
46+
},
47+
"lint": {
48+
"executor": "@nx/linter:eslint",
49+
"outputs": ["{options.outputFile}"],
50+
"options": {
51+
"useFlatConfig": false,
52+
"lintFilePatterns": ["packages/php-wasm/cli-util/**/*.ts"],
53+
"maxWarnings": 0
54+
}
55+
},
56+
"test": {
57+
"executor": "@nx/vite:test",
58+
"outputs": ["{workspaceRoot}/coverage/packages/php-wasm/cli-util"],
59+
"options": {
60+
"reportsDirectory": "../../../coverage/packages/php-wasm/cli-util"
61+
}
62+
},
63+
"test:esmcjs": {
64+
"executor": "@wp-playground/nx-extensions:assert-built-esm-and-cjs",
65+
"options": {
66+
"outputPath": "dist/packages/php-wasm/cli-util"
67+
},
68+
"dependsOn": ["build"]
69+
},
70+
"typecheck": {
71+
"executor": "nx:run-commands",
72+
"options": {
73+
"commands": [
74+
"tsc -p packages/php-wasm/cli-util/tsconfig.lib.json --noEmit",
75+
"tsc -p packages/php-wasm/cli-util/tsconfig.spec.json --noEmit"
76+
]
77+
}
78+
}
79+
},
80+
"tags": ["scope:independent-from-php-binaries"]
81+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib';
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './mounts';
2+
export * from './xdebug-path-mappings';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface Mount {
2+
hostPath: string;
3+
vfsPath: string;
4+
}

packages/playground/cli/src/xdebug-path-mappings.ts renamed to packages/php-wasm/cli-util/src/lib/xdebug-path-mappings.ts

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,44 @@
11
import fs from 'fs';
22
import path from 'path';
3-
import { type Mount } from './mounts';
3+
import type { Mount } from './mounts';
44
import {
55
type X2jOptions,
66
type XmlBuilderOptions,
77
XMLParser,
88
XMLBuilder,
99
} from 'fast-xml-parser';
10-
import JSONC from 'jsonc-parser';
10+
import * as JSONC from 'jsonc-parser';
1111

1212
/**
13-
* Create a symlink to temp dir for the Playground CLI.
13+
* Create a symlink to a tempory directory.
1414
*
1515
* The symlink is created to access the system temp dir
1616
* inside the current debugging directory.
1717
*
1818
* @param nativeDirPath The system temp dir path.
1919
* @param symlinkPath The symlink name.
2020
*/
21-
export async function createPlaygroundCliTempDirSymlink(
21+
export async function createTempDirSymlink(
2222
nativeDirPath: string,
2323
symlinkPath: string,
2424
platform: string
2525
) {
2626
const type =
2727
platform === 'win32'
2828
? // On Windows, creating a 'dir' symlink can require elevated permissions.
29-
// In this case, let's make junction points because they function like
30-
// symlinks and do not require elevated permissions.
31-
'junction'
29+
// In this case, let's make junction points because they function like
30+
// symlinks and do not require elevated permissions.
31+
'junction'
3232
: 'dir';
3333
fs.symlinkSync(nativeDirPath, symlinkPath, type);
3434
}
3535

3636
/**
37-
* Remove the temp dir symlink if it exists.
37+
* Remove the given temporary directory symlink if it exists.
3838
*
3939
* @param symlinkPath The symlink path.
4040
*/
41-
export async function removePlaygroundCliTempDirSymlink(symlinkPath: string) {
41+
export async function removeTempDirSymlink(symlinkPath: string) {
4242
try {
4343
const stats = fs.lstatSync(symlinkPath);
4444
if (stats.isSymbolicLink()) {
@@ -52,7 +52,7 @@ export async function removePlaygroundCliTempDirSymlink(symlinkPath: string) {
5252
/**
5353
* Filters out mounts that are not in the current working directory
5454
*
55-
* @param mounts The Playground CLI mount options.
55+
* @param mounts The mounts list.
5656
*/
5757
function filterLocalMounts(cwd: string, mounts: Mount[]) {
5858
return mounts.filter((mount) => {
@@ -91,7 +91,7 @@ export type IDEConfig = {
9191
/**
9292
* The mounts to consider for debugger path mapping.
9393
*/
94-
mounts: Mount[];
94+
mounts?: Mount[];
9595
/**
9696
* The IDE key to use for the debug configuration. Defaults to 'PLAYGROUNDCLI'.
9797
*/
@@ -137,7 +137,7 @@ type VSCodeConfigNode = {
137137
type: string;
138138
request: string;
139139
port: number;
140-
pathMappings: VSCodeConfigMetaData;
140+
pathMappings?: VSCodeConfigMetaData;
141141
};
142142

143143
const xmlParserOptions: X2jOptions = {
@@ -170,7 +170,7 @@ export type PhpStormConfigOptions = {
170170
host: string;
171171
port: number;
172172
projectDir: string;
173-
mappings: Mount[];
173+
mappings?: Mount[];
174174
ideKey: string;
175175
};
176176

@@ -201,19 +201,7 @@ export function updatePhpStormConfig(
201201

202202
// Create the server element with path mappings
203203
const serverElement: PhpStormConfigNode = {
204-
server: [
205-
{
206-
path_mappings: mappings.map((mapping) => ({
207-
mapping: [],
208-
':@': {
209-
'local-root': `$PROJECT_DIR$/${toPosixPath(
210-
path.relative(options.projectDir, mapping.hostPath)
211-
)}`,
212-
'remote-root': mapping.vfsPath,
213-
},
214-
})),
215-
},
216-
],
204+
server: [{}],
217205
':@': {
218206
name,
219207
// NOTE: PhpStorm quirk: Xdebug only works when the full URL (including port)
@@ -224,6 +212,18 @@ export function updatePhpStormConfig(
224212
},
225213
};
226214

215+
if (mappings && mappings.length) {
216+
serverElement.server![0].path_mappings = mappings.map((mapping) => ({
217+
mapping: [],
218+
':@': {
219+
'local-root': `$PROJECT_DIR$/${toPosixPath(
220+
path.relative(options.projectDir, mapping.hostPath)
221+
)}`,
222+
'remote-root': mapping.vfsPath,
223+
},
224+
}));
225+
}
226+
227227
// Find or create project element
228228
let projectElement = config?.find((c: PhpStormConfigNode) => !!c?.project);
229229
if (projectElement) {
@@ -364,7 +364,7 @@ export function updatePhpStormConfig(
364364
export type VSCodeConfigOptions = {
365365
name: string;
366366
workspaceDir: string;
367-
mappings: Mount[];
367+
mappings?: Mount[];
368368
};
369369

370370
/**
@@ -408,7 +408,8 @@ export function updateVSCodeConfig(
408408

409409
// Check if configuration already exists
410410
const configurationIndex = configurationsNode?.children?.findIndex(
411-
(child) => JSONC.findNodeAtLocation(child, ['name'])?.value === name
411+
(child: any) =>
412+
JSONC.findNodeAtLocation(child, ['name'])?.value === name
412413
);
413414

414415
// Only add configuration if it doesn't exist
@@ -418,13 +419,16 @@ export function updateVSCodeConfig(
418419
type: 'php',
419420
request: 'launch',
420421
port: 9003,
421-
pathMappings: mappings.reduce((acc, mount) => {
422+
};
423+
424+
if (mappings && mappings.length) {
425+
configuration.pathMappings = mappings.reduce((acc, mount) => {
422426
acc[mount.vfsPath] = `\${workspaceFolder}/${toPosixPath(
423427
path.relative(options.workspaceDir, mount.hostPath)
424428
)}`;
425429
return acc;
426-
}, {} as VSCodeConfigMetaData),
427-
};
430+
}, {} as VSCodeConfigMetaData);
431+
}
428432

429433
// Get the current length to append at the end
430434
const currentLength = configurationsNode?.children?.length || 0;
@@ -452,7 +456,7 @@ export function updateVSCodeConfig(
452456
* Implement necessary parameters and path mappings in IDE configuration files.
453457
*
454458
* @param name The configuration name.
455-
* @param mounts The Playground CLI mount options.
459+
* @param mounts The mounts options.
456460
*/
457461
export async function addXdebugIDEConfig({
458462
name,
@@ -461,10 +465,10 @@ export async function addXdebugIDEConfig({
461465
port,
462466
cwd,
463467
mounts,
464-
ideKey = 'PLAYGROUNDCLI',
468+
ideKey = 'PHPWASMCLI',
465469
}: IDEConfig) {
466-
const mappings = filterLocalMounts(cwd, mounts);
467-
const modifiedConfig: string[] = [];
470+
const mappings = mounts ? filterLocalMounts(cwd, mounts) : [];
471+
const modifiedConfig: Record<string, string> = {};
468472

469473
// PHPstorm
470474
if (ides.includes('phpstorm')) {
@@ -500,9 +504,8 @@ export async function addXdebugIDEConfig({
500504
ideKey,
501505
});
502506
fs.writeFileSync(phpStormConfigFilePath, updatedXml);
507+
modifiedConfig['phpstorm'] = phpStormRelativeConfigFilePath;
503508
}
504-
505-
modifiedConfig.push(phpStormRelativeConfigFilePath);
506509
}
507510

508511
// VSCode
@@ -539,7 +542,7 @@ export async function addXdebugIDEConfig({
539542
// Only write and track the file if changes were made
540543
if (updatedJson !== content) {
541544
fs.writeFileSync(vsCodeConfigFilePath, updatedJson);
542-
modifiedConfig.push(vsCodeRelativeConfigFilePath);
545+
modifiedConfig['vscode'] = vsCodeRelativeConfigFilePath;
543546
}
544547
}
545548
}
@@ -626,7 +629,8 @@ export async function clearXdebugIDEConfig(name: string, cwd: string) {
626629
]);
627630

628631
const configurationIndex = configurationsNode?.children?.findIndex(
629-
(child) => JSONC.findNodeAtLocation(child, ['name'])?.value === name
632+
(child: any) =>
633+
JSONC.findNodeAtLocation(child, ['name'])?.value === name
630634
);
631635

632636
if (configurationIndex !== undefined && configurationIndex >= 0) {
@@ -682,8 +686,8 @@ function jsoncApplyEdits(content: string, edits: JSONC.Edit[]) {
682686
(edit) => `At ${edit.offset}:${edit.length} - (${edit.content})`
683687
);
684688
throw new Error(
685-
`VS Code configuration file (.vscode/launch.json) is not valid a JSONC after Playground CLI modifications. This is likely ` +
686-
`a Playground CLI bug. Please report it at https://github.com/WordPress/wordpress-playground/issues and include the contents ` +
689+
`VS Code configuration file (.vscode/launch.json) is not valid a JSONC after CLI modifications. This is likely ` +
690+
`a CLI bug. Please report it at https://github.com/WordPress/wordpress-playground/issues and include the contents ` +
687691
`of your ".vscode/launch.json" file. \n\n Applied edits: ${formattedEdits.join(
688692
'\n'
689693
)}\n\n The errors are: ${formattedErrors.join('\n')}`

0 commit comments

Comments
 (0)