@wp-typia/project-tools 0.24.7 → 0.24.9

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.
@@ -1,24 +1,53 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { parseScaffoldBlockMetadata } from "@wp-typia/block-runtime/blocks";
4
- import ts from "typescript";
4
+ import { WORDPRESS_BLOCK_API_COMPATIBILITY } from "@wp-typia/block-types/blocks/compatibility";
5
5
  import { createDoctorCheck, resolveWorkspaceBootstrapPath, } from "./cli-doctor-workspace-shared.js";
6
6
  import { readJsonFileSync } from "../shared/json-utils.js";
7
- import { getPropertyNameText } from "../shared/ts-property-names.js";
8
7
  import { compareVersionFloors, pickHigherVersionFloor, } from "../shared/version-floor.js";
9
8
  import { DEFAULT_SCAFFOLD_WORDPRESS_TARGET_VERSION } from "../templates/scaffold-compatibility.js";
10
9
  const WORDPRESS_VERSION_CHECK_CODES = {
11
10
  featureMinimum: "wp-typia.workspace.wordpress.feature-minimum",
12
11
  testedTarget: "wp-typia.workspace.wordpress.tested-target",
13
12
  };
14
- const BLOCK_METADATA_WORDPRESS_FLOORS = {
15
- blockHooks: "6.4",
16
- supportsInteractivity: "6.5",
17
- supportsSplitting: "6.5",
13
+ // Both entries read the same `variations` field and are distinguished by value type.
14
+ const BLOCK_VARIATION_BLOCK_JSON_KEYS = {
15
+ registrationBlockJsonMetadata: "variations",
16
+ registrationMetadataFile: "variations",
18
17
  };
19
18
  function isEnabledMetadataValue(value) {
20
19
  return value !== undefined && value !== false && value !== null;
21
20
  }
21
+ function isEnabledBlockVariationMetadataFeature(feature, value) {
22
+ if (feature === "registrationBlockJsonMetadata") {
23
+ return Array.isArray(value) && value.length > 0;
24
+ }
25
+ if (feature === "registrationMetadataFile") {
26
+ return typeof value === "string" && value.trim().length > 0;
27
+ }
28
+ return isEnabledMetadataValue(value);
29
+ }
30
+ function getNestedMetadataValue(object, key) {
31
+ if (!object) {
32
+ return undefined;
33
+ }
34
+ if (Object.prototype.hasOwnProperty.call(object, key)) {
35
+ return object[key];
36
+ }
37
+ return key
38
+ .split(".")
39
+ .reduce((current, segment) => {
40
+ if (current === null ||
41
+ typeof current !== "object" ||
42
+ Array.isArray(current)) {
43
+ return undefined;
44
+ }
45
+ const record = current;
46
+ return Object.prototype.hasOwnProperty.call(record, segment)
47
+ ? record[segment]
48
+ : undefined;
49
+ }, object);
50
+ }
22
51
  function getBootstrapHeaderValue(source, headerName) {
23
52
  const escapedHeaderName = headerName.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
24
53
  const pattern = new RegExp(`^\\s*\\*\\s*${escapedHeaderName}:\\s*([^\\r\\n]*)`, "mu");
@@ -42,6 +71,15 @@ function pushRequirement(requirements, label, version) {
42
71
  version,
43
72
  });
44
73
  }
74
+ function pushBlockApiRequirement(requirements, labelPrefix, entry) {
75
+ pushRequirement(requirements, `${labelPrefix} ${entry.label}`, entry.since);
76
+ }
77
+ function readExistingTextFile(filePath) {
78
+ if (!fs.existsSync(filePath)) {
79
+ return undefined;
80
+ }
81
+ return fs.readFileSync(filePath, "utf8");
82
+ }
45
83
  function collectBlockMetadataRequirements(workspace, inventory) {
46
84
  const issues = [];
47
85
  const requirements = [];
@@ -61,14 +99,31 @@ function collectBlockMetadataRequirements(workspace, inventory) {
61
99
  issues.push(`${blockJsonRelativePath}: ${error instanceof Error ? error.message : String(error)}`);
62
100
  continue;
63
101
  }
64
- if (isEnabledMetadataValue(blockJson.supports?.interactivity)) {
65
- pushRequirement(requirements, `Block ${block.slug} supports.interactivity`, BLOCK_METADATA_WORDPRESS_FLOORS.supportsInteractivity);
102
+ for (const [feature, entry] of Object.entries(WORDPRESS_BLOCK_API_COMPATIBILITY.blockSupports)) {
103
+ if (isEnabledMetadataValue(getNestedMetadataValue(blockJson.supports, feature))) {
104
+ pushBlockApiRequirement(requirements, `Block ${block.slug}`, entry);
105
+ }
106
+ }
107
+ for (const [feature, entry] of Object.entries(WORDPRESS_BLOCK_API_COMPATIBILITY.blockMetadata)) {
108
+ if (isEnabledMetadataValue(getNestedMetadataValue(blockJson, feature))) {
109
+ pushBlockApiRequirement(requirements, `Block ${block.slug}`, entry);
110
+ }
66
111
  }
67
- if (isEnabledMetadataValue(blockJson.supports?.splitting)) {
68
- pushRequirement(requirements, `Block ${block.slug} supports.splitting`, BLOCK_METADATA_WORDPRESS_FLOORS.supportsSplitting);
112
+ for (const [feature, entry] of Object.entries(WORDPRESS_BLOCK_API_COMPATIBILITY.blockBindings)) {
113
+ if (entry.runtime.includes("block-json") &&
114
+ isEnabledMetadataValue(getNestedMetadataValue(blockJson, feature))) {
115
+ pushBlockApiRequirement(requirements, `Block ${block.slug}`, entry);
116
+ }
69
117
  }
70
- if (blockJson.blockHooks !== undefined) {
71
- pushRequirement(requirements, `Block ${block.slug} blockHooks`, BLOCK_METADATA_WORDPRESS_FLOORS.blockHooks);
118
+ for (const [feature, entry] of Object.entries(WORDPRESS_BLOCK_API_COMPATIBILITY.blockVariations)) {
119
+ if (!entry.runtime.includes("block-json")) {
120
+ continue;
121
+ }
122
+ const blockJsonFeature = feature;
123
+ const blockJsonKey = BLOCK_VARIATION_BLOCK_JSON_KEYS[blockJsonFeature];
124
+ if (isEnabledBlockVariationMetadataFeature(blockJsonFeature, getNestedMetadataValue(blockJson, blockJsonKey))) {
125
+ pushBlockApiRequirement(requirements, `Block ${block.slug}`, entry);
126
+ }
72
127
  }
73
128
  }
74
129
  return {
@@ -76,89 +131,100 @@ function collectBlockMetadataRequirements(workspace, inventory) {
76
131
  requirements,
77
132
  };
78
133
  }
79
- function findExportedArrayLiteral(sourceFile, exportName) {
80
- for (const statement of sourceFile.statements) {
81
- if (!ts.isVariableStatement(statement) ||
82
- !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
83
- continue;
84
- }
85
- for (const declaration of statement.declarationList.declarations) {
86
- if (ts.isIdentifier(declaration.name) &&
87
- declaration.name.text === exportName &&
88
- declaration.initializer &&
89
- ts.isArrayLiteralExpression(declaration.initializer)) {
90
- return declaration.initializer;
91
- }
92
- }
134
+ function hasGeneratedCoreVariationModule(directoryPath, isRootDirectory = true) {
135
+ if (!fs.existsSync(directoryPath)) {
136
+ return false;
93
137
  }
94
- return null;
95
- }
96
- function getObjectProperty(objectLiteral, key) {
97
- for (const property of objectLiteral.properties) {
98
- if (!ts.isPropertyAssignment(property)) {
99
- continue;
138
+ for (const entry of fs.readdirSync(directoryPath, { withFileTypes: true })) {
139
+ const entryPath = path.join(directoryPath, entry.name);
140
+ if (entry.isDirectory() && hasGeneratedCoreVariationModule(entryPath, false)) {
141
+ return true;
100
142
  }
101
- if (getPropertyNameText(property.name) === key) {
102
- return property.initializer;
143
+ if (!isRootDirectory &&
144
+ entry.isFile() &&
145
+ entry.name.endsWith(".ts") &&
146
+ entry.name !== "index.ts") {
147
+ return true;
103
148
  }
104
149
  }
105
- return undefined;
106
- }
107
- function getObjectLiteralProperty(objectLiteral, key) {
108
- const property = objectLiteral ? getObjectProperty(objectLiteral, key) : undefined;
109
- return property && ts.isObjectLiteralExpression(property) ? property : undefined;
150
+ return false;
110
151
  }
111
- function getStringLiteralProperty(objectLiteral, key) {
112
- const property = getObjectProperty(objectLiteral, key);
113
- return property && ts.isStringLiteralLike(property) ? property.text : undefined;
152
+ function collectVariationRequirements(workspace, inventory) {
153
+ const requirements = [];
154
+ const variationEntries = WORDPRESS_BLOCK_API_COMPATIBILITY.blockVariations;
155
+ for (const variation of inventory.variations) {
156
+ pushBlockApiRequirement(requirements, `Variation ${variation.block}/${variation.slug}`, variationEntries.editorRegistration);
157
+ }
158
+ const coreVariationsDir = path.join(workspace.projectDir, "src", "editor-plugins", "core-variations");
159
+ if (hasGeneratedCoreVariationModule(coreVariationsDir)) {
160
+ pushBlockApiRequirement(requirements, "Core variations editor plugin", variationEntries.editorRegistration);
161
+ }
162
+ return {
163
+ issues: [],
164
+ requirements,
165
+ };
114
166
  }
115
167
  function collectInventoryCompatibilityRequirements(inventory) {
116
- const issues = [];
117
168
  const requirements = [];
118
- const sourceFile = ts.createSourceFile("scripts/block-config.ts", inventory.source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
119
- for (const section of [
120
- { exportName: "ABILITIES", label: "Ability" },
121
- { exportName: "AI_FEATURES", label: "AI feature" },
122
- ]) {
123
- const arrayLiteral = findExportedArrayLiteral(sourceFile, section.exportName);
124
- if (!arrayLiteral) {
125
- continue;
169
+ for (const ability of inventory.abilities) {
170
+ const wordpressMinimum = ability.compatibility?.hardMinimums.wordpress;
171
+ if (wordpressMinimum) {
172
+ requirements.push({
173
+ label: `Ability ${ability.slug} compatibility metadata`,
174
+ version: wordpressMinimum,
175
+ });
126
176
  }
127
- arrayLiteral.elements.forEach((element, elementIndex) => {
128
- if (!ts.isObjectLiteralExpression(element)) {
129
- return;
130
- }
131
- const slug = getStringLiteralProperty(element, "slug") ?? `entry ${elementIndex + 1}`;
132
- const compatibility = getObjectLiteralProperty(element, "compatibility");
133
- const hardMinimums = getObjectLiteralProperty(compatibility, "hardMinimums");
134
- const wordpressMinimum = hardMinimums
135
- ? getObjectProperty(hardMinimums, "wordpress")
136
- : undefined;
137
- if (wordpressMinimum === undefined) {
138
- return;
139
- }
140
- if (!ts.isStringLiteralLike(wordpressMinimum)) {
141
- issues.push(`${section.exportName}[${elementIndex}].compatibility.hardMinimums.wordpress must be a string literal.`);
142
- return;
143
- }
177
+ }
178
+ for (const aiFeature of inventory.aiFeatures) {
179
+ const wordpressMinimum = aiFeature.compatibility?.hardMinimums.wordpress;
180
+ if (wordpressMinimum) {
144
181
  requirements.push({
145
- label: `${section.label} ${slug} compatibility metadata`,
146
- version: wordpressMinimum.text,
182
+ label: `AI feature ${aiFeature.slug} compatibility metadata`,
183
+ version: wordpressMinimum,
147
184
  });
148
- });
185
+ }
149
186
  }
150
187
  return {
151
- issues,
188
+ issues: [],
189
+ requirements,
190
+ };
191
+ }
192
+ function collectBindingSourceRequirements(workspace, inventory) {
193
+ const requirements = [];
194
+ const bindingEntries = WORDPRESS_BLOCK_API_COMPATIBILITY.blockBindings;
195
+ for (const bindingSource of inventory.bindingSources) {
196
+ pushBlockApiRequirement(requirements, `Binding source ${bindingSource.slug}`, bindingEntries.serverRegistration);
197
+ pushBlockApiRequirement(requirements, `Binding source ${bindingSource.slug}`, bindingEntries.editorRegistration);
198
+ const editorFilePath = path.join(workspace.projectDir, bindingSource.editorFile);
199
+ if (readExistingTextFile(editorFilePath)?.includes("getFieldsList")) {
200
+ pushBlockApiRequirement(requirements, `Binding source ${bindingSource.slug}`, bindingEntries.editorFieldsList);
201
+ }
202
+ const serverFilePath = path.join(workspace.projectDir, bindingSource.serverFile);
203
+ if (readExistingTextFile(serverFilePath)?.includes("block_bindings_supported_attributes_")) {
204
+ pushBlockApiRequirement(requirements, `Binding source ${bindingSource.slug}`, bindingEntries.supportedAttributesFilter);
205
+ }
206
+ }
207
+ return {
208
+ issues: [],
152
209
  requirements,
153
210
  };
154
211
  }
155
212
  function collectWordPressVersionRequirements(workspace, inventory) {
156
213
  const blockRequirements = collectBlockMetadataRequirements(workspace, inventory);
214
+ const variationRequirements = collectVariationRequirements(workspace, inventory);
215
+ const bindingRequirements = collectBindingSourceRequirements(workspace, inventory);
157
216
  const inventoryRequirements = collectInventoryCompatibilityRequirements(inventory);
158
217
  return {
159
- issues: [...blockRequirements.issues, ...inventoryRequirements.issues],
218
+ issues: [
219
+ ...blockRequirements.issues,
220
+ ...variationRequirements.issues,
221
+ ...bindingRequirements.issues,
222
+ ...inventoryRequirements.issues,
223
+ ],
160
224
  requirements: [
161
225
  ...blockRequirements.requirements,
226
+ ...variationRequirements.requirements,
227
+ ...bindingRequirements.requirements,
162
228
  ...inventoryRequirements.requirements,
163
229
  ],
164
230
  };
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
2
  import { getPropertyNameText } from "../shared/ts-property-names.js";
3
+ import { parseVersionFloorParts } from "../shared/version-floor.js";
3
4
  import { assertParsedInventoryEntry, } from "./workspace-inventory-parser-validation.js";
4
5
  function findExportedArrayLiteral(sourceFile, exportName) {
5
6
  for (const statement of sourceFile.statements) {
@@ -129,6 +130,19 @@ function getOptionalNestedStringProperty(objectLiteral, key, context) {
129
130
  }
130
131
  return expression.text;
131
132
  }
133
+ function getOptionalVersionFloorProperty(objectLiteral, key, context) {
134
+ const value = getOptionalNestedStringProperty(objectLiteral, key, context);
135
+ if (value === undefined) {
136
+ return undefined;
137
+ }
138
+ try {
139
+ parseVersionFloorParts(value);
140
+ }
141
+ catch {
142
+ throw new Error(`${context}.${key} must be a dotted numeric version such as "6.7" or "8.1.2" in scripts/block-config.ts.`);
143
+ }
144
+ return value;
145
+ }
132
146
  function getRequiredStringArrayProperty(objectLiteral, key, context) {
133
147
  const expression = findObjectPropertyExpression(objectLiteral, key);
134
148
  if (!expression) {
@@ -150,8 +164,8 @@ function parseCompatibilityConfigLiteral(objectLiteral, context) {
150
164
  if (mode !== "baseline" && mode !== "optional" && mode !== "required") {
151
165
  throw new Error(`${context}.mode must be baseline, optional, or required in scripts/block-config.ts.`);
152
166
  }
153
- const php = getOptionalNestedStringProperty(hardMinimumsObject, "php", `${context}.hardMinimums`);
154
- const wordpress = getOptionalNestedStringProperty(hardMinimumsObject, "wordpress", `${context}.hardMinimums`);
167
+ const php = getOptionalVersionFloorProperty(hardMinimumsObject, "php", `${context}.hardMinimums`);
168
+ const wordpress = getOptionalVersionFloorProperty(hardMinimumsObject, "wordpress", `${context}.hardMinimums`);
155
169
  return {
156
170
  hardMinimums: {
157
171
  ...(php ? { php } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wp-typia/project-tools",
3
- "version": "0.24.7",
3
+ "version": "0.24.9",
4
4
  "description": "Project orchestration and programmatic tooling for wp-typia",
5
5
  "packageManager": "bun@1.3.11",
6
6
  "type": "module",
@@ -121,7 +121,7 @@
121
121
  "package.json"
122
122
  ],
123
123
  "scripts": {
124
- "build": "bun run --filter @wp-typia/api-client build && bun run --filter @wp-typia/block-runtime build && rm -rf dist && tsc -p tsconfig.runtime.json",
124
+ "build": "bun run --filter @wp-typia/block-types build && bun run --filter @wp-typia/api-client build && bun run --filter @wp-typia/block-runtime build && rm -rf dist && tsc -p tsconfig.runtime.json",
125
125
  "prepack": "bun run build && node ./scripts/publish-manifest.mjs prepare",
126
126
  "postpack": "node ./scripts/publish-manifest.mjs restore",
127
127
  "test": "bun run build && bun test tests/*.test.ts",
@@ -168,7 +168,7 @@
168
168
  "@wp-typia/api-client": "^0.4.6",
169
169
  "@wp-typia/block-runtime": "^0.7.1",
170
170
  "@wp-typia/rest": "^0.3.14",
171
- "@wp-typia/block-types": "^0.3.4",
171
+ "@wp-typia/block-types": "^0.3.5",
172
172
  "mustache": "^4.2.0",
173
173
  "npm-package-arg": "^13.0.0",
174
174
  "semver": "^7.7.3",