Skip to content

Commit aa024f4

Browse files
committed
Explore Blueprint v2 overrides
1 parent afc4ad5 commit aa024f4

File tree

11 files changed

+452
-101
lines changed

11 files changed

+452
-101
lines changed

packages/playground/blueprints/src/lib/resolve-runtime-configuration.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
import { RecommendedPHPVersion } from '@wp-playground/common';
2+
import type { SupportedPHPVersion } from '@php-wasm/universal';
23
import { BlueprintReflection } from './reflection';
34
import type { Blueprint, RuntimeConfiguration } from './types';
45
import { compileBlueprintV1 } from './v1/compile';
56
import type { BlueprintV1 } from './v1/types';
67

8+
/**
9+
* BlueprintOverrides type - matches the type from @wp-playground/client
10+
* but defined here to avoid circular dependencies.
11+
*/
12+
export interface BlueprintOverrides {
13+
blueprintOverrides?: {
14+
wordpressVersion?: string;
15+
phpVersion?: string;
16+
additionalSteps?: any[];
17+
};
18+
applicationOptions?: {
19+
landingPage?: string;
20+
login?: boolean;
21+
networkAccess?: boolean;
22+
};
23+
}
24+
725
export async function resolveRuntimeConfiguration(
8-
blueprint: Blueprint
26+
blueprint: Blueprint,
27+
overrides?: BlueprintOverrides
928
): Promise<RuntimeConfiguration> {
1029
const reflection = await BlueprintReflection.create(blueprint);
1130
if (reflection.getVersion() === 1) {
@@ -30,12 +49,42 @@ export async function resolveRuntimeConfiguration(
3049
constants: {},
3150
};
3251
} else {
33-
// @TODO: actually compute the runtime configuration based on the resolved Blueprint v2
52+
// For Blueprint v2, compute runtime configuration from the blueprint and overrides
53+
const declaration = reflection.getDeclaration() as any;
54+
55+
// Determine WordPress version (priority: override > blueprint > default)
56+
const wpVersion =
57+
overrides?.blueprintOverrides?.wordpressVersion ||
58+
declaration.wordpressVersion ||
59+
'latest';
60+
61+
// Determine PHP version (priority: override > blueprint > default)
62+
let phpVersion: SupportedPHPVersion = RecommendedPHPVersion;
63+
if (overrides?.blueprintOverrides?.phpVersion) {
64+
phpVersion = overrides.blueprintOverrides
65+
.phpVersion as SupportedPHPVersion;
66+
} else if (declaration.phpVersion) {
67+
// Handle both string and object forms of phpVersion
68+
if (typeof declaration.phpVersion === 'string') {
69+
phpVersion = declaration.phpVersion as SupportedPHPVersion;
70+
} else if (declaration.phpVersion.recommended) {
71+
phpVersion = declaration.phpVersion
72+
.recommended as SupportedPHPVersion;
73+
}
74+
}
75+
76+
// Determine networking (priority: override > blueprint > default)
77+
const networking =
78+
overrides?.applicationOptions?.networkAccess ??
79+
declaration.applicationOptions?.['wordpress-playground']
80+
?.networkAccess ??
81+
true;
82+
3483
return {
35-
phpVersion: RecommendedPHPVersion,
36-
wpVersion: 'latest',
84+
phpVersion,
85+
wpVersion,
3786
intl: false,
38-
networking: true,
87+
networking,
3988
constants: {},
4089
extraLibraries: [],
4190
};

packages/playground/blueprints/src/lib/v2/run-blueprint-v2.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export async function runBlueprintV2(
124124
/**
125125
* Prepare hooks, filters, and run the Blueprint:
126126
*/
127+
console.log('options.blueprintOverrides', options.blueprintOverrides);
127128
await php?.writeFile(
128129
'/tmp/run-blueprints.php',
129130
`<?php
@@ -141,9 +142,9 @@ function playground_on_blueprint_target_resolved() {
141142
'type' => 'blueprint.target_resolved',
142143
]));
143144
}
144-
playground_add_filter('blueprint.target_resolved', 'playground_on_blueprint_target_resolved');
145+
//playground_add_filter('blueprint.target_resolved', 'playground_on_blueprint_target_resolved');
145146
146-
playground_add_filter('blueprint.resolved', 'playground_on_blueprint_resolved');
147+
//playground_add_filter('blueprint.resolved', 'playground_on_blueprint_resolved');
147148
function playground_on_blueprint_resolved($blueprint) {
148149
$additional_blueprint_steps = json_decode(${phpVar(
149150
JSON.stringify(options.blueprintOverrides?.additionalSteps || [])
@@ -154,7 +155,6 @@ function playground_on_blueprint_resolved($blueprint) {
154155
$additional_blueprint_steps
155156
);
156157
}
157-
158158
$wp_version_override = json_decode(${phpVar(
159159
JSON.stringify(options.blueprintOverrides?.wordpressVersion || null)
160160
)}, true);
@@ -222,6 +222,13 @@ require( "/tmp/blueprints.phar" );
222222
])) as StreamedPHPResponse;
223223

224224
streamedResponse.finished.finally(unbindMessageListener);
225+
streamedResponse.stdout.pipeTo(
226+
new WritableStream({
227+
write(chunk) {
228+
console.log('stdout', new TextDecoder().decode(chunk));
229+
},
230+
})
231+
);
225232

226233
return streamedResponse;
227234
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import type {
2+
BlueprintV1Declaration,
3+
BlueprintV1,
4+
BlueprintBundle,
5+
} from '@wp-playground/blueprints';
6+
import { isBlueprintBundle } from '@wp-playground/blueprints';
7+
import { RecommendedPHPVersion } from '@wp-playground/common';
8+
import type { BlueprintOverrides } from './index';
9+
10+
/**
11+
* Apply BlueprintOverrides to a Blueprint v1.
12+
* This is used by the v1 handler to reconcile URL parameter overrides
13+
* with the blueprint definition.
14+
*
15+
* Note: For bundle blueprints, this only works on the in-memory representation.
16+
* The bundle itself is not modified.
17+
*/
18+
export function applyBlueprintOverrides(
19+
blueprint: BlueprintV1,
20+
overrides: BlueprintOverrides
21+
): BlueprintV1 {
22+
// If it's a bundle, we can't modify it - return as is
23+
// The overrides will be applied during compilation
24+
if (isBlueprintBundle(blueprint)) {
25+
return blueprint;
26+
}
27+
28+
return applyOverridesToDeclaration(blueprint, overrides);
29+
}
30+
31+
function applyOverridesToDeclaration(
32+
blueprint: BlueprintV1Declaration,
33+
overrides: BlueprintOverrides
34+
): BlueprintV1Declaration {
35+
// Create a mutable copy of the blueprint to avoid mutating the original
36+
// (which may be frozen/sealed from Redux store)
37+
const mutableBlueprint: BlueprintV1Declaration = {
38+
...blueprint,
39+
preferredVersions: blueprint.preferredVersions
40+
? { ...blueprint.preferredVersions }
41+
: ({} as any),
42+
features: blueprint.features ? { ...blueprint.features } : {},
43+
steps: blueprint.steps ? [...blueprint.steps] : [],
44+
};
45+
46+
// Apply PHP version override
47+
if (overrides.blueprintOverrides?.phpVersion) {
48+
mutableBlueprint.preferredVersions!.php = overrides.blueprintOverrides
49+
.phpVersion as any;
50+
} else if (!mutableBlueprint.preferredVersions!.php) {
51+
mutableBlueprint.preferredVersions!.php = RecommendedPHPVersion;
52+
}
53+
54+
// Apply WordPress version override
55+
if (overrides.blueprintOverrides?.wordpressVersion) {
56+
mutableBlueprint.preferredVersions!.wp =
57+
overrides.blueprintOverrides.wordpressVersion;
58+
} else if (!mutableBlueprint.preferredVersions!.wp) {
59+
mutableBlueprint.preferredVersions!.wp = 'latest';
60+
}
61+
62+
// Apply network access override
63+
if (overrides.applicationOptions?.networkAccess !== undefined) {
64+
mutableBlueprint.features!['networking'] =
65+
overrides.applicationOptions.networkAccess;
66+
}
67+
68+
// Apply login override
69+
if (overrides.applicationOptions?.login !== undefined) {
70+
mutableBlueprint.login = overrides.applicationOptions.login;
71+
}
72+
73+
// Apply landing page override
74+
if (overrides.applicationOptions?.landingPage) {
75+
mutableBlueprint.landingPage = overrides.applicationOptions.landingPage;
76+
}
77+
78+
// Apply additional steps (language, multisite, Gutenberg PR, etc.)
79+
if (overrides.blueprintOverrides?.additionalSteps) {
80+
for (const step of overrides.blueprintOverrides.additionalSteps) {
81+
// Check if this step type already exists to avoid duplicates
82+
const stepType = (step as any).step;
83+
const existingStep = mutableBlueprint.steps!.find(
84+
(s) => s && (s as any).step === stepType
85+
);
86+
87+
// For some steps like setSiteLanguage, we want to avoid duplicates
88+
// For others like mkdir/writeFile/unzip/installPlugin, we want to add them
89+
if (!existingStep || stepType !== 'setSiteLanguage') {
90+
if (stepType === 'mkdir' || stepType === 'writeFile') {
91+
// Add these at the beginning for PR installations
92+
mutableBlueprint.steps!.unshift(step);
93+
} else {
94+
mutableBlueprint.steps!.push(step);
95+
}
96+
}
97+
}
98+
}
99+
100+
/*
101+
* The 6.3 release includes a caching bug where
102+
* registered styles aren't enqueued when they
103+
* should be. This isn't present in all environments
104+
* but it does here in the Playground. For now,
105+
* the fix is to define `WP_DEVELOPMENT_MODE = all`
106+
* to bypass the style cache.
107+
*
108+
* @see https://core.trac.wordpress.org/ticket/59056
109+
*/
110+
if (mutableBlueprint.preferredVersions?.wp === '6.3') {
111+
mutableBlueprint.steps!.unshift({
112+
step: 'defineWpConfigConsts',
113+
consts: {
114+
WP_DEVELOPMENT_MODE: 'all',
115+
},
116+
});
117+
}
118+
119+
return mutableBlueprint;
120+
}

packages/playground/client/src/blueprints-v1-handler.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
runBlueprintV1Steps,
77
resolveRuntimeConfiguration,
88
BlueprintReflection,
9+
applyBlueprintOverrides,
910
} from '.';
1011
import { collectPhpLogs, logger } from '@php-wasm/logger';
1112
import { consumeAPI } from '@php-wasm/universal';
@@ -27,12 +28,18 @@ export class BlueprintsV1Handler {
2728
shouldInstallWordPress,
2829
sqliteDriverVersion,
2930
onClientConnected,
31+
blueprintOverrides,
3032
} = this.options;
3133
const executionProgress = progressTracker!.stage(0.5);
3234
const downloadProgress = progressTracker!.stage();
3335

3436
// Set a default blueprint if none is provided.
35-
const blueprint = this.options.blueprint || {};
37+
let blueprint = this.options.blueprint || {};
38+
39+
// Apply overrides if provided (reconcile URL parameters with blueprint)
40+
if (blueprintOverrides) {
41+
blueprint = applyBlueprintOverrides(blueprint, blueprintOverrides);
42+
}
3643

3744
// Connect the Comlink API client to the remote worker,
3845
// boot the playground, and run the blueprint steps.
@@ -44,7 +51,8 @@ export class BlueprintsV1Handler {
4451
progressTracker.pipe(playground);
4552

4653
const runtimeConfiguration = await resolveRuntimeConfiguration(
47-
blueprint
54+
blueprint,
55+
blueprintOverrides
4856
);
4957
await playground.onDownloadProgress(downloadProgress.loadingListener);
5058
await playground.boot({

packages/playground/client/src/blueprints-v2-handler.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ProgressTracker } from '@php-wasm/progress';
22
import type { PlaygroundClient, StartPlaygroundOptions } from '.';
3+
import { BlueprintReflection } from '@wp-playground/blueprints';
34
import { collectPhpLogs, logger } from '@php-wasm/logger';
45
import { consumeAPI } from '@php-wasm/universal';
56

@@ -11,16 +12,49 @@ export class BlueprintsV2Handler {
1112
progressTracker: ProgressTracker
1213
) {
1314
const {
14-
blueprint,
15+
blueprint: rawBlueprint,
1516
onClientConnected,
1617
corsProxy,
1718
mounts,
1819
sapiName,
1920
scope,
21+
blueprintOverrides,
2022
} = this.options;
2123
const downloadProgress = progressTracker!.stage(0.25);
2224
const executionProgress = progressTracker!.stage(0.75);
2325

26+
// Convert v1 blueprint to v2 if needed
27+
let blueprint: any = rawBlueprint;
28+
if (rawBlueprint) {
29+
const reflection = await BlueprintReflection.create(rawBlueprint);
30+
if (reflection.getVersion() === 1) {
31+
// Convert v1 to minimal v2 blueprint
32+
blueprint = {
33+
version: 2,
34+
wordpressVersion:
35+
blueprintOverrides?.blueprintOverrides
36+
?.wordpressVersion || 'latest',
37+
};
38+
}
39+
} else {
40+
// Create minimal v2 blueprint if none provided
41+
blueprint = {
42+
version: 2,
43+
wordpressVersion:
44+
blueprintOverrides?.blueprintOverrides?.wordpressVersion ||
45+
'latest',
46+
};
47+
}
48+
49+
// Resolve runtime configuration to get PHP/WP versions
50+
const { resolveRuntimeConfiguration } = await import(
51+
'@wp-playground/blueprints'
52+
);
53+
const runtimeConfiguration = await resolveRuntimeConfiguration(
54+
blueprint,
55+
blueprintOverrides
56+
);
57+
2458
// Connect the Comlink API client to the remote worker,
2559
// boot the playground, and run the blueprint steps.
2660
const playground = consumeAPI<PlaygroundClient>(
@@ -87,10 +121,15 @@ export class BlueprintsV2Handler {
87121
mounts,
88122
sapiName,
89123
scope: scope ?? Math.random().toFixed(16),
124+
phpVersion: runtimeConfiguration.phpVersion,
125+
wpVersion: runtimeConfiguration.wpVersion,
126+
withICU: runtimeConfiguration.intl,
127+
withNetworking: runtimeConfiguration.networking,
90128
corsProxyUrl: corsProxy,
91129
experimentalBlueprintsV2Runner: true,
92130
// Pass the declaration directly – the worker runs the V2 runner.
93131
blueprint: blueprint as any,
132+
blueprintOverrides: blueprintOverrides?.blueprintOverrides,
94133
} as any);
95134

96135
await playground.isReady();
@@ -99,8 +138,10 @@ export class BlueprintsV2Handler {
99138
collectPhpLogs(logger, playground);
100139
onClientConnected?.(playground);
101140

102-
// @TODO: Get the landing page from the Blueprint.
103-
playground.goTo('/');
141+
// Navigate to landing page from overrides or default to root
142+
const landingPage =
143+
blueprintOverrides?.applicationOptions?.landingPage || '/';
144+
playground.goTo(landingPage);
104145

105146
/**
106147
* Pre-fetch WordPress update checks to speed up the initial wp-admin load.

0 commit comments

Comments
 (0)