@pressgang-wp/shakedown 0.1.1 → 0.1.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.
- package/bin/seed-states.php +81 -19
- package/lib/sandbox.mjs +6 -0
- package/package.json +1 -1
package/bin/seed-states.php
CHANGED
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* the sandbox isolation witness has already proven this WordPress runs on
|
|
5
5
|
* throwaway SQLite before this script is invoked).
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Each ACF field group is seeded according to where it is located:
|
|
8
|
+
* - post types, page and post templates → a dedicated PAIR of fixtures,
|
|
9
|
+
* `populated` (every generatable field filled) and `minimal` (required
|
|
10
|
+
* fields only — the sparsest publishable state, where empty-link and
|
|
11
|
+
* missing-image bugs live), each with a placeholder featured image;
|
|
12
|
+
* - options pages → the populated values written once (global chrome);
|
|
13
|
+
* - the front page (`page_type=front_page`) → the populated values onto the
|
|
14
|
+
* home page, reusing a theme-assigned front page or creating one;
|
|
15
|
+
* - nav-menu-item groups → left to the theme's own seeder, whose menus carry
|
|
16
|
+
* the real structure the fields attach to.
|
|
12
17
|
*
|
|
13
18
|
* Run via: wp eval-file bin/seed-states.php <muster-autoload> <acf-json-dir> <seed> <epoch>
|
|
14
19
|
* Emits JSON: {"routes": [{url, kind, expect}...]} for the matrix.
|
|
@@ -51,13 +56,23 @@ $generator = new AcfValueGenerator($context->victuals(), [
|
|
|
51
56
|
->id(),
|
|
52
57
|
'post' => function (array $types) use ($context): int {
|
|
53
58
|
$type = $types[0] ?? 'post';
|
|
59
|
+
$slug = 'state-related-' . sanitize_title($type);
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
$ref = (new PostBuilder($context, $type))
|
|
56
62
|
->title('State related ' . $type)
|
|
57
|
-
->slug(
|
|
63
|
+
->slug($slug)
|
|
58
64
|
->status('publish')
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
// Pin before the fixture epoch so relationship stubs sink below the
|
|
66
|
+
// real fixtures in date-ordered feeds rather than heading them.
|
|
67
|
+
->date($context->clock()->epoch()->modify('-1 year')->format('Y-m-d H:i:s'))
|
|
68
|
+
->save();
|
|
69
|
+
|
|
70
|
+
(new AttachmentBuilder($context, "{$slug}-thumb"))
|
|
71
|
+
->placeholder(1200, 800)
|
|
72
|
+
->featuredOn($ref)
|
|
73
|
+
->save();
|
|
74
|
+
|
|
75
|
+
return $ref->id();
|
|
61
76
|
},
|
|
62
77
|
'term' => fn (string $taxonomy): int => (new TermBuilder($context, $taxonomy))
|
|
63
78
|
->name('State term')
|
|
@@ -68,43 +83,90 @@ $generator = new AcfValueGenerator($context->victuals(), [
|
|
|
68
83
|
]);
|
|
69
84
|
|
|
70
85
|
$routes = [];
|
|
86
|
+
$liveAcf = new LiveAcfAdapter();
|
|
71
87
|
|
|
72
88
|
foreach (AcfJson::groups($acfJsonDir) as $group) {
|
|
73
89
|
$targets = AcfJson::targets($group);
|
|
74
90
|
|
|
75
91
|
if ($targets === []) {
|
|
76
|
-
continue; //
|
|
92
|
+
continue; // no seedable location (taxonomy terms, user roles, …)
|
|
77
93
|
}
|
|
78
94
|
|
|
79
95
|
$target = $targets[0];
|
|
96
|
+
$fields = (array) $group['fields'];
|
|
97
|
+
$slugBase = sanitize_title($group['title'] ?? $group['key']);
|
|
80
98
|
|
|
81
99
|
// Options-page groups are global state, not a URL: seed the populated
|
|
82
|
-
// values once so chrome (header/footer) renders fully. The unseeded
|
|
83
|
-
//
|
|
100
|
+
// values once so chrome (header/footer) renders fully. The unseeded fresh
|
|
101
|
+
// install already exercises the empty-options state.
|
|
84
102
|
if ($target['param'] === 'options_page') {
|
|
85
|
-
|
|
103
|
+
$liveAcf->updateFields($generator->populated($fields), 'option', 0);
|
|
86
104
|
continue;
|
|
87
105
|
}
|
|
88
|
-
$slugBase = sanitize_title($group['title'] ?? $group['key']);
|
|
89
106
|
|
|
107
|
+
// The front page is a singular surface — seed the populated state onto the
|
|
108
|
+
// one home page. Reuse the front page a theme seeder may already have set;
|
|
109
|
+
// assign one only when none exists, so we never fight a theme-authored home.
|
|
110
|
+
// The matrix's own `/` route covers it, so no dedicated route is emitted.
|
|
111
|
+
// Other page_type values (top_level, …) have no generic surface to seed.
|
|
112
|
+
if ($target['param'] === 'page_type') {
|
|
113
|
+
if ($target['value'] !== 'front_page') {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
$frontId = (int) get_option('page_on_front');
|
|
118
|
+
if ($frontId === 0) {
|
|
119
|
+
$frontId = (new PostBuilder($context, 'page'))
|
|
120
|
+
->title('Home')
|
|
121
|
+
->slug('state-front-page')
|
|
122
|
+
->status('publish')
|
|
123
|
+
->date($fixtureDate)
|
|
124
|
+
->save()
|
|
125
|
+
->id();
|
|
126
|
+
update_option('show_on_front', 'page');
|
|
127
|
+
update_option('page_on_front', $frontId);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$liveAcf->updateFields($generator->populated($fields), 'post', $frontId);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// nav_menu_item groups attach to a menu's items, which exist only once a
|
|
135
|
+
// menu is built with the theme's real structure — that is the theme
|
|
136
|
+
// seeder's job. The derived path leaves them to the baseline rather than
|
|
137
|
+
// inventing a menu here.
|
|
138
|
+
if ($target['param'] === 'nav_menu_item') {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Per-instance surfaces (post types, page and post templates): a dedicated
|
|
143
|
+
// populated AND minimal fixture, so the sparsest publishable state is
|
|
144
|
+
// exercised alongside the full one.
|
|
90
145
|
foreach (['populated', 'minimal'] as $variant) {
|
|
91
|
-
$fields = (array) $group['fields'];
|
|
92
146
|
$values = $variant === 'populated' ? $generator->populated($fields) : $generator->minimal($fields);
|
|
93
147
|
|
|
94
|
-
$builder = $target['param']
|
|
95
|
-
|
|
96
|
-
|
|
148
|
+
$builder = match ($target['param']) {
|
|
149
|
+
'page_template' => (new PostBuilder($context, 'page'))->template($target['value']),
|
|
150
|
+
'post_template' => (new PostBuilder($context, 'post'))->template($target['value']),
|
|
151
|
+
default => new PostBuilder($context, $target['value']),
|
|
152
|
+
};
|
|
97
153
|
|
|
98
154
|
$ref = $builder
|
|
99
155
|
->title(($group['title'] ?? $group['key']) . ' — ' . $variant)
|
|
100
156
|
->slug("state-{$slugBase}-{$variant}")
|
|
101
157
|
->status('publish')
|
|
102
158
|
->date($fixtureDate)
|
|
103
|
-
|
|
104
159
|
->content('State fixture: ' . $slugBase . ' (' . $variant . ')')
|
|
105
160
|
->acf($values)
|
|
106
161
|
->save();
|
|
107
162
|
|
|
163
|
+
// A placeholder featured image so archive and card thumbnails render
|
|
164
|
+
// instead of the theme's empty-thumbnail fallback.
|
|
165
|
+
(new AttachmentBuilder($context, "state-thumb-{$slugBase}-{$variant}"))
|
|
166
|
+
->placeholder(1200, 800)
|
|
167
|
+
->featuredOn($ref)
|
|
168
|
+
->save();
|
|
169
|
+
|
|
108
170
|
$url = get_permalink($ref->id());
|
|
109
171
|
|
|
110
172
|
if ($url) {
|
package/lib/sandbox.mjs
CHANGED
|
@@ -285,6 +285,12 @@ export async function bootSandbox(target, options = {}) {
|
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
// Clear WordPress's install defaults (Hello world!, Sample Page, the seeded
|
|
289
|
+
// comment) so the sandbox starts from a deterministic empty state — only
|
|
290
|
+
// seeded fixtures exist, and no version-dependent defaults leak into feeds,
|
|
291
|
+
// archives, or menus.
|
|
292
|
+
wp(root, ['site', 'empty', '--yes']);
|
|
293
|
+
|
|
288
294
|
wp(root, ['rewrite', 'structure', '/%postname%/', '--hard']);
|
|
289
295
|
|
|
290
296
|
const server = spawn('wp', ['server', `--host=127.0.0.1`, `--port=${port}`, `--docroot=${docroot}`, `--path=${root}`], {
|