@wp-typia/create-workspace-template 0.13.1 → 0.15.0
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.mustache +2 -0
- package/package.json +1 -1
- package/package.json.mustache +7 -4
- package/scripts/block-config.ts.mustache +30 -0
- package/scripts/build-workspace.mjs.mustache +6 -1
- package/scripts/sync-project.ts.mustache +103 -0
- package/scripts/sync-rest-contracts.ts.mustache +111 -6
- package/src/editor-plugins/.gitkeep +1 -0
- package/webpack.config.js.mustache +18 -7
- package/{{slugKebabCase}}.php.mustache +37 -0
package/README.md.mustache
CHANGED
|
@@ -25,11 +25,13 @@ Variations and patterns can also be added after the first block exists:
|
|
|
25
25
|
```bash
|
|
26
26
|
wp-typia add variation hero-card --block counter-card
|
|
27
27
|
wp-typia add pattern hero-layout
|
|
28
|
+
wp-typia add rest-resource snapshots --namespace {{namespace}}/v1 --methods list,read,create
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
## Development
|
|
31
32
|
|
|
32
33
|
```bash
|
|
34
|
+
bun run sync
|
|
33
35
|
bun run build
|
|
34
36
|
bun run typecheck
|
|
35
37
|
```
|
package/package.json
CHANGED
package/package.json.mustache
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
"author": "{{author}}",
|
|
7
7
|
"license": "GPL-2.0-or-later",
|
|
8
8
|
"scripts": {
|
|
9
|
+
"sync": "tsx scripts/sync-project.ts",
|
|
9
10
|
"sync-types": "tsx scripts/sync-types-to-block-json.ts",
|
|
10
11
|
"sync-rest": "tsx scripts/sync-rest-contracts.ts",
|
|
11
|
-
"build": "bun run sync
|
|
12
|
-
"start": "bun run sync
|
|
12
|
+
"build": "bun run sync --check && node scripts/build-workspace.mjs build",
|
|
13
|
+
"start": "bun run sync && node scripts/build-workspace.mjs start",
|
|
13
14
|
"dev": "bun run start",
|
|
14
|
-
"typecheck": "bun run sync
|
|
15
|
+
"typecheck": "bun run sync --check && tsc --noEmit",
|
|
15
16
|
"lint:js": "wp-scripts lint-js",
|
|
16
17
|
"lint:css": "wp-scripts lint-style",
|
|
17
18
|
"lint": "bun run lint:js && bun run lint:css",
|
|
@@ -46,8 +47,10 @@
|
|
|
46
47
|
"@wordpress/block-editor": "^15.2.0",
|
|
47
48
|
"@wordpress/blocks": "^15.2.0",
|
|
48
49
|
"@wordpress/components": "^30.2.0",
|
|
50
|
+
"@wordpress/editor": "^14.44.0",
|
|
49
51
|
"@wordpress/element": "^6.29.0",
|
|
50
52
|
"@wordpress/i18n": "^6.2.0",
|
|
51
|
-
"@wordpress/interactivity": "^6.29.0"
|
|
53
|
+
"@wordpress/interactivity": "^6.29.0",
|
|
54
|
+
"@wordpress/plugins": "^7.44.0"
|
|
52
55
|
}
|
|
53
56
|
}
|
|
@@ -26,6 +26,28 @@ export interface WorkspaceBindingSourceConfig {
|
|
|
26
26
|
slug: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export interface WorkspaceRestResourceConfig {
|
|
30
|
+
apiFile: string;
|
|
31
|
+
clientFile: string;
|
|
32
|
+
dataFile: string;
|
|
33
|
+
methods: Array< 'list' | 'read' | 'create' | 'update' | 'delete' >;
|
|
34
|
+
namespace: string;
|
|
35
|
+
openApiFile: string;
|
|
36
|
+
phpFile: string;
|
|
37
|
+
restManifest?: ReturnType<
|
|
38
|
+
typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
|
|
39
|
+
>;
|
|
40
|
+
slug: string;
|
|
41
|
+
typesFile: string;
|
|
42
|
+
validatorsFile: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface WorkspaceEditorPluginConfig {
|
|
46
|
+
file: string;
|
|
47
|
+
slug: string;
|
|
48
|
+
slot: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
export const BLOCKS: WorkspaceBlockConfig[] = [
|
|
30
52
|
// wp-typia add block entries
|
|
31
53
|
];
|
|
@@ -41,3 +63,11 @@ export const PATTERNS: WorkspacePatternConfig[] = [
|
|
|
41
63
|
export const BINDING_SOURCES: WorkspaceBindingSourceConfig[] = [
|
|
42
64
|
// wp-typia add binding-source entries
|
|
43
65
|
];
|
|
66
|
+
|
|
67
|
+
export const REST_RESOURCES: WorkspaceRestResourceConfig[] = [
|
|
68
|
+
// wp-typia add rest-resource entries
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export const EDITOR_PLUGINS: WorkspaceEditorPluginConfig[] = [
|
|
72
|
+
// wp-typia add editor-plugin entries
|
|
73
|
+
];
|
|
@@ -22,7 +22,12 @@ function getBlockSlugs() {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function hasSharedEditorEntries() {
|
|
25
|
-
for ( const relativePath of [
|
|
25
|
+
for ( const relativePath of [
|
|
26
|
+
'src/bindings/index.ts',
|
|
27
|
+
'src/bindings/index.js',
|
|
28
|
+
'src/editor-plugins/index.ts',
|
|
29
|
+
'src/editor-plugins/index.js',
|
|
30
|
+
] ) {
|
|
26
31
|
if ( fs.existsSync( path.join( process.cwd(), relativePath ) ) ) {
|
|
27
32
|
return true;
|
|
28
33
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
interface SyncCliOptions {
|
|
7
|
+
check: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseCliOptions( argv: string[] ): SyncCliOptions {
|
|
11
|
+
const options: SyncCliOptions = {
|
|
12
|
+
check: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
for ( const argument of argv ) {
|
|
16
|
+
if ( argument === '--check' ) {
|
|
17
|
+
options.check = true;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error( `Unknown sync flag: ${ argument }` );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getSyncScriptEnv() {
|
|
28
|
+
const binaryDirectory = path.join( process.cwd(), 'node_modules', '.bin' );
|
|
29
|
+
const inheritedPath =
|
|
30
|
+
process.env.PATH ??
|
|
31
|
+
process.env.Path ??
|
|
32
|
+
Object.entries( process.env ).find(
|
|
33
|
+
( [ key ] ) => key.toLowerCase() === 'path'
|
|
34
|
+
)?.[ 1 ] ??
|
|
35
|
+
'';
|
|
36
|
+
const nextPath = fs.existsSync( binaryDirectory )
|
|
37
|
+
? `${ binaryDirectory }${ path.delimiter }${ inheritedPath }`
|
|
38
|
+
: inheritedPath;
|
|
39
|
+
const env: NodeJS.ProcessEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( const key of Object.keys( env ) ) {
|
|
44
|
+
if ( key.toLowerCase() === 'path' ) {
|
|
45
|
+
delete env[ key ];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
env.PATH = nextPath;
|
|
50
|
+
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function runSyncScript( scriptPath: string, options: SyncCliOptions ) {
|
|
55
|
+
const args = [ scriptPath ];
|
|
56
|
+
if ( options.check ) {
|
|
57
|
+
args.push( '--check' );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync( 'tsx', args, {
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: getSyncScriptEnv(),
|
|
63
|
+
shell: process.platform === 'win32',
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
if ( result.error ) {
|
|
68
|
+
if ( ( result.error as NodeJS.ErrnoException ).code === 'ENOENT' ) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Unable to resolve `tsx` for project sync. Install project dependencies or rerun the command through your package manager.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw result.error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( result.status !== 0 ) {
|
|
78
|
+
throw new Error( `Sync script failed: ${ scriptPath }` );
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
84
|
+
const syncTypesScriptPath = path.join( 'scripts', 'sync-types-to-block-json.ts' );
|
|
85
|
+
const syncRestScriptPath = path.join( 'scripts', 'sync-rest-contracts.ts' );
|
|
86
|
+
|
|
87
|
+
runSyncScript( syncTypesScriptPath, options );
|
|
88
|
+
|
|
89
|
+
if ( fs.existsSync( path.resolve( process.cwd(), syncRestScriptPath ) ) ) {
|
|
90
|
+
runSyncScript( syncRestScriptPath, options );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(
|
|
94
|
+
options.check
|
|
95
|
+
? '✅ Generated project metadata and REST artifacts are already synchronized.'
|
|
96
|
+
: '✅ Generated project metadata and REST artifacts were synchronized.'
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main().catch( ( error ) => {
|
|
101
|
+
console.error( '❌ Project sync failed:', error );
|
|
102
|
+
process.exit( 1 );
|
|
103
|
+
} );
|
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
+
runSyncBlockMetadata,
|
|
5
6
|
syncEndpointClient,
|
|
6
7
|
syncRestOpenApi,
|
|
7
8
|
syncTypeSchemas,
|
|
8
9
|
} from '@wp-typia/block-runtime/metadata-core';
|
|
9
10
|
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
BLOCKS,
|
|
13
|
+
REST_RESOURCES,
|
|
14
|
+
type WorkspaceBlockConfig,
|
|
15
|
+
type WorkspaceRestResourceConfig,
|
|
16
|
+
} from './block-config';
|
|
11
17
|
|
|
12
18
|
function parseCliOptions( argv: string[] ) {
|
|
13
19
|
const options = {
|
|
@@ -41,20 +47,70 @@ function isRestEnabledBlock(
|
|
|
41
47
|
);
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
function isWorkspaceRestResource(
|
|
51
|
+
resource: WorkspaceRestResourceConfig
|
|
52
|
+
): resource is WorkspaceRestResourceConfig & {
|
|
53
|
+
clientFile: string;
|
|
54
|
+
openApiFile: string;
|
|
55
|
+
restManifest: NonNullable< WorkspaceRestResourceConfig[ 'restManifest' ] >;
|
|
56
|
+
typesFile: string;
|
|
57
|
+
validatorsFile: string;
|
|
58
|
+
} {
|
|
59
|
+
return (
|
|
60
|
+
typeof resource.clientFile === 'string' &&
|
|
61
|
+
typeof resource.openApiFile === 'string' &&
|
|
62
|
+
typeof resource.typesFile === 'string' &&
|
|
63
|
+
typeof resource.validatorsFile === 'string' &&
|
|
64
|
+
typeof resource.restManifest === 'object' &&
|
|
65
|
+
resource.restManifest !== null
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function assertTypeArtifactsCurrent( block: WorkspaceBlockConfig ) {
|
|
70
|
+
const report = await runSyncBlockMetadata(
|
|
71
|
+
{
|
|
72
|
+
blockJsonFile: path.join( 'src', 'blocks', block.slug, 'block.json' ),
|
|
73
|
+
jsonSchemaFile: path.join( 'src', 'blocks', block.slug, 'typia.schema.json' ),
|
|
74
|
+
manifestFile: path.join( 'src', 'blocks', block.slug, 'typia.manifest.json' ),
|
|
75
|
+
openApiFile: path.join( 'src', 'blocks', block.slug, 'typia.openapi.json' ),
|
|
76
|
+
sourceTypeName: block.attributeTypeName,
|
|
77
|
+
typesFile: block.typesFile,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
check: true,
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if ( report.failure?.code === 'stale-generated-artifact' ) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`${ block.slug }: ${ report.failure.message }\nRun \`sync\` or \`sync-types\` first to refresh type-derived artifacts before rerunning \`sync-rest\`.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ( report.failure ) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${ block.slug }: type-derived artifact preflight failed before sync-rest.\n${ report.failure.message }`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
44
97
|
async function main() {
|
|
45
98
|
const options = parseCliOptions( process.argv.slice( 2 ) );
|
|
46
99
|
const restBlocks = BLOCKS.filter( isRestEnabledBlock );
|
|
100
|
+
const restResources = REST_RESOURCES.filter( isWorkspaceRestResource );
|
|
47
101
|
|
|
48
|
-
if ( restBlocks.length === 0 ) {
|
|
102
|
+
if ( restBlocks.length === 0 && restResources.length === 0 ) {
|
|
49
103
|
console.log(
|
|
50
104
|
options.check
|
|
51
|
-
? 'ℹ️ No REST-enabled workspace blocks are registered yet. `sync-rest --check` is already clean.'
|
|
52
|
-
: 'ℹ️ No REST-enabled workspace blocks are registered yet.'
|
|
105
|
+
? 'ℹ️ No REST-enabled workspace blocks or plugin-level REST resources are registered yet. `sync-rest --check` is already clean.'
|
|
106
|
+
: 'ℹ️ No REST-enabled workspace blocks or plugin-level REST resources are registered yet.'
|
|
53
107
|
);
|
|
54
108
|
return;
|
|
55
109
|
}
|
|
56
110
|
|
|
57
111
|
for ( const block of restBlocks ) {
|
|
112
|
+
await assertTypeArtifactsCurrent( block );
|
|
113
|
+
|
|
58
114
|
const contracts = block.restManifest.contracts;
|
|
59
115
|
|
|
60
116
|
for ( const [ baseName, contract ] of Object.entries( contracts ) ) {
|
|
@@ -111,10 +167,59 @@ async function main() {
|
|
|
111
167
|
);
|
|
112
168
|
}
|
|
113
169
|
|
|
170
|
+
for ( const resource of restResources ) {
|
|
171
|
+
const contracts = resource.restManifest.contracts;
|
|
172
|
+
|
|
173
|
+
for ( const [ baseName, contract ] of Object.entries( contracts ) ) {
|
|
174
|
+
await syncTypeSchemas(
|
|
175
|
+
{
|
|
176
|
+
jsonSchemaFile: path.join(
|
|
177
|
+
path.dirname( resource.typesFile ),
|
|
178
|
+
'api-schemas',
|
|
179
|
+
`${ baseName }.schema.json`
|
|
180
|
+
),
|
|
181
|
+
openApiFile: path.join(
|
|
182
|
+
path.dirname( resource.typesFile ),
|
|
183
|
+
'api-schemas',
|
|
184
|
+
`${ baseName }.openapi.json`
|
|
185
|
+
),
|
|
186
|
+
sourceTypeName: contract.sourceTypeName,
|
|
187
|
+
typesFile: resource.typesFile,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
check: options.check,
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await syncRestOpenApi(
|
|
196
|
+
{
|
|
197
|
+
manifest: resource.restManifest,
|
|
198
|
+
openApiFile: resource.openApiFile,
|
|
199
|
+
typesFile: resource.typesFile,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
check: options.check,
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
await syncEndpointClient(
|
|
207
|
+
{
|
|
208
|
+
clientFile: resource.clientFile,
|
|
209
|
+
manifest: resource.restManifest,
|
|
210
|
+
typesFile: resource.typesFile,
|
|
211
|
+
validatorsFile: resource.validatorsFile,
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
check: options.check,
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
114
219
|
console.log(
|
|
115
220
|
options.check
|
|
116
|
-
? '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents are already up to date
|
|
117
|
-
: '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents generated
|
|
221
|
+
? '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents are already up to date for workspace blocks and plugin-level resources!'
|
|
222
|
+
: '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents generated for workspace blocks and plugin-level resources!'
|
|
118
223
|
);
|
|
119
224
|
}
|
|
120
225
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -189,14 +189,25 @@ function getEditorEntries() {
|
|
|
189
189
|
function getSharedEditorEntries() {
|
|
190
190
|
const entries = [];
|
|
191
191
|
|
|
192
|
-
for ( const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
for ( const [ entryName, candidates ] of [
|
|
193
|
+
[
|
|
194
|
+
'bindings/index',
|
|
195
|
+
[ 'src/bindings/index.ts', 'src/bindings/index.js' ],
|
|
196
|
+
],
|
|
197
|
+
[
|
|
198
|
+
'editor-plugins/index',
|
|
199
|
+
[ 'src/editor-plugins/index.ts', 'src/editor-plugins/index.js' ],
|
|
200
|
+
],
|
|
201
|
+
] ) {
|
|
202
|
+
for ( const relativePath of candidates ) {
|
|
203
|
+
const entryPath = path.resolve( process.cwd(), relativePath );
|
|
204
|
+
if ( ! fs.existsSync( entryPath ) ) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
197
207
|
|
|
198
|
-
|
|
199
|
-
|
|
208
|
+
entries.push( [ entryName, entryPath ] );
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
200
211
|
}
|
|
201
212
|
|
|
202
213
|
return Object.fromEntries( entries );
|
|
@@ -117,6 +117,42 @@ function {{phpPrefix}}_enqueue_binding_sources_editor() {
|
|
|
117
117
|
);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
function {{phpPrefix}}_enqueue_editor_plugins_editor() {
|
|
121
|
+
$script_path = __DIR__ . '/build/editor-plugins/index.js';
|
|
122
|
+
$asset_path = __DIR__ . '/build/editor-plugins/index.asset.php';
|
|
123
|
+
$style_path = __DIR__ . '/build/editor-plugins/style-index.css';
|
|
124
|
+
$style_rtl_path = __DIR__ . '/build/editor-plugins/style-index-rtl.css';
|
|
125
|
+
|
|
126
|
+
if ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
$asset = require $asset_path;
|
|
131
|
+
if ( ! is_array( $asset ) ) {
|
|
132
|
+
$asset = array();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
wp_enqueue_script(
|
|
136
|
+
'{{slugKebabCase}}-editor-plugins',
|
|
137
|
+
plugins_url( 'build/editor-plugins/index.js', __FILE__ ),
|
|
138
|
+
isset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
|
|
139
|
+
isset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
|
|
140
|
+
true
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if ( file_exists( $style_path ) ) {
|
|
144
|
+
wp_enqueue_style(
|
|
145
|
+
'{{slugKebabCase}}-editor-plugins',
|
|
146
|
+
plugins_url( 'build/editor-plugins/style-index.css', __FILE__ ),
|
|
147
|
+
array(),
|
|
148
|
+
isset( $asset['version'] ) ? $asset['version'] : filemtime( $style_path )
|
|
149
|
+
);
|
|
150
|
+
if ( file_exists( $style_rtl_path ) ) {
|
|
151
|
+
wp_style_add_data( '{{slugKebabCase}}-editor-plugins', 'rtl', 'replace' );
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
120
156
|
function {{phpPrefix}}_register_pattern_category() {
|
|
121
157
|
if ( function_exists( 'register_block_pattern_category' ) ) {
|
|
122
158
|
register_block_pattern_category(
|
|
@@ -143,3 +179,4 @@ add_action( 'init', '{{phpPrefix}}_register_binding_sources', 20 );
|
|
|
143
179
|
add_action( 'init', '{{phpPrefix}}_register_pattern_category' );
|
|
144
180
|
add_action( 'init', '{{phpPrefix}}_register_patterns', 20 );
|
|
145
181
|
add_action( 'enqueue_block_editor_assets', '{{phpPrefix}}_enqueue_binding_sources_editor' );
|
|
182
|
+
add_action( 'enqueue_block_editor_assets', '{{phpPrefix}}_enqueue_editor_plugins_editor' );
|