@wp-typia/create-workspace-template 0.12.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 ADDED
@@ -0,0 +1,17 @@
1
+ # `@wp-typia/create-workspace-template`
2
+
3
+ The official empty workspace template for `wp-typia`.
4
+
5
+ Use it through the canonical CLI:
6
+
7
+ ```bash
8
+ npx wp-typia create my-plugin --template @wp-typia/create-workspace-template
9
+ ```
10
+
11
+ The generated project starts with an empty `src/blocks/*` workspace shell and is designed to grow through:
12
+
13
+ ```bash
14
+ wp-typia add block my-block --template basic
15
+ wp-typia add binding-source hero-data
16
+ wp-typia add hooked-block my-block --anchor core/post-content --position after
17
+ ```
@@ -0,0 +1,39 @@
1
+ # {{title}}
2
+
3
+ {{description}}
4
+
5
+ ## Workspace
6
+
7
+ This scaffold is the official empty `wp-typia` workspace shell. It starts with
8
+ an empty `src/blocks/*` inventory and grows through `wp-typia add`.
9
+
10
+ ## First Block
11
+
12
+ ```bash
13
+ wp-typia add block counter-card --template basic
14
+ ```
15
+
16
+ Persistence-ready blocks and compound parent/child blocks can be added later:
17
+
18
+ ```bash
19
+ wp-typia add block counter-card --template persistence --data-storage custom-table --persistence-policy authenticated
20
+ wp-typia add block faq-stack --template compound --data-storage custom-table --persistence-policy public
21
+ ```
22
+
23
+ Variations and patterns can also be added after the first block exists:
24
+
25
+ ```bash
26
+ wp-typia add variation hero-card --block counter-card
27
+ wp-typia add pattern hero-layout
28
+ ```
29
+
30
+ ## Development
31
+
32
+ ```bash
33
+ bun run build
34
+ bun run typecheck
35
+ ```
36
+
37
+ If you scaffolded the workspace with `--with-migration-ui`, the migration
38
+ workspace is seeded at `v1` and `wp-typia add block` updates the migration
39
+ config plus current-version snapshots for newly added blocks.
@@ -0,0 +1 @@
1
+
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@wp-typia/create-workspace-template",
3
+ "version": "0.12.0",
4
+ "description": "Official empty wp-typia workspace template package",
5
+ "packageManager": "bun@1.3.11",
6
+ "type": "module",
7
+ "files": [
8
+ "package.json.mustache",
9
+ "tsconfig.json.mustache",
10
+ "webpack.config.js.mustache",
11
+ "scripts/",
12
+ "src/",
13
+ "languages/",
14
+ "*.php.mustache",
15
+ "README.md.mustache",
16
+ "README.md",
17
+ "package.json"
18
+ ],
19
+ "keywords": [
20
+ "wordpress",
21
+ "gutenberg",
22
+ "typia",
23
+ "workspace",
24
+ "template",
25
+ "scaffold"
26
+ ],
27
+ "author": "imjlk",
28
+ "license": "GPL-2.0-or-later",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/imjlk/wp-typia.git",
32
+ "directory": "packages/create-workspace-template"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/imjlk/wp-typia/issues"
36
+ },
37
+ "homepage": "https://github.com/imjlk/wp-typia/tree/main/packages/create-workspace-template#readme",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "engines": {
42
+ "node": ">=20.0.0",
43
+ "npm": ">=10.0.0",
44
+ "bun": ">=1.3.11"
45
+ }
46
+ }
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "{{slug}}",
3
+ "version": "0.1.0",
4
+ "packageManager": "bun@1.3.11",
5
+ "description": "{{description}}",
6
+ "author": "{{author}}",
7
+ "license": "GPL-2.0-or-later",
8
+ "scripts": {
9
+ "sync-types": "tsx scripts/sync-types-to-block-json.ts",
10
+ "sync-rest": "tsx scripts/sync-rest-contracts.ts",
11
+ "build": "bun run sync-types --check && bun run sync-rest --check && node scripts/build-workspace.mjs build",
12
+ "start": "bun run sync-types && bun run sync-rest && node scripts/build-workspace.mjs start",
13
+ "dev": "bun run start",
14
+ "typecheck": "bun run sync-types --check && bun run sync-rest --check && tsc --noEmit",
15
+ "lint:js": "wp-scripts lint-js",
16
+ "lint:css": "wp-scripts lint-style",
17
+ "lint": "bun run lint:js && bun run lint:css",
18
+ "format": "wp-scripts format"
19
+ },
20
+ "wpTypia": {
21
+ "projectType": "workspace",
22
+ "templatePackage": "@wp-typia/create-workspace-template",
23
+ "namespace": "{{namespace}}",
24
+ "textDomain": "{{textDomain}}",
25
+ "phpPrefix": "{{phpPrefix}}"
26
+ },
27
+ "devDependencies": {
28
+ "@wp-typia/api-client": "{{apiClientPackageVersion}}",
29
+ "@wp-typia/block-runtime": "{{blockRuntimePackageVersion}}",
30
+ "@wp-typia/block-types": "{{blockTypesPackageVersion}}",
31
+ "@wp-typia/rest": "{{restPackageVersion}}",
32
+ "@typia/unplugin": "^12.0.1",
33
+ "@types/node": "^24.5.2",
34
+ "@types/wordpress__block-editor": "^11.5.17",
35
+ "@types/wordpress__blocks": "^12.5.18",
36
+ "@wordpress/browserslist-config": "^6.42.0",
37
+ "@wordpress/scripts": "^30.22.0",
38
+ "ajv": "^8.18.0",
39
+ "eslint-plugin-jsx-a11y": "^6.10.2",
40
+ "tsx": "^4.20.5",
41
+ "typescript": "^5.9.2",
42
+ "typia": "^12.0.1"
43
+ },
44
+ "dependencies": {
45
+ "@wordpress/api-fetch": "^7.42.0",
46
+ "@wordpress/block-editor": "^15.2.0",
47
+ "@wordpress/blocks": "^15.2.0",
48
+ "@wordpress/components": "^30.2.0",
49
+ "@wordpress/element": "^6.29.0",
50
+ "@wordpress/i18n": "^6.2.0",
51
+ "@wordpress/interactivity": "^6.29.0"
52
+ }
53
+ }
@@ -0,0 +1,43 @@
1
+ export interface WorkspaceBlockConfig {
2
+ slug: string;
3
+ attributeTypeName: string;
4
+ typesFile: string;
5
+ apiTypesFile?: string;
6
+ openApiFile?: string;
7
+ restManifest?: ReturnType<
8
+ typeof import( '@wp-typia/block-runtime/metadata-core' ).defineEndpointManifest
9
+ >;
10
+ }
11
+
12
+ export interface WorkspaceVariationConfig {
13
+ block: string;
14
+ file: string;
15
+ slug: string;
16
+ }
17
+
18
+ export interface WorkspacePatternConfig {
19
+ file: string;
20
+ slug: string;
21
+ }
22
+
23
+ export interface WorkspaceBindingSourceConfig {
24
+ editorFile: string;
25
+ serverFile: string;
26
+ slug: string;
27
+ }
28
+
29
+ export const BLOCKS: WorkspaceBlockConfig[] = [
30
+ // wp-typia add block entries
31
+ ];
32
+
33
+ export const VARIATIONS: WorkspaceVariationConfig[] = [
34
+ // wp-typia add variation entries
35
+ ];
36
+
37
+ export const PATTERNS: WorkspacePatternConfig[] = [
38
+ // wp-typia add pattern entries
39
+ ];
40
+
41
+ export const BINDING_SOURCES: WorkspaceBindingSourceConfig[] = [
42
+ // wp-typia add binding-source entries
43
+ ];
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { execFileSync } from 'node:child_process';
6
+
7
+ function getBlockRoot() {
8
+ return path.join( process.cwd(), 'src', 'blocks' );
9
+ }
10
+
11
+ function getBlockSlugs() {
12
+ const blockRoot = getBlockRoot();
13
+ if ( ! fs.existsSync( blockRoot ) ) {
14
+ return [];
15
+ }
16
+
17
+ return fs
18
+ .readdirSync( blockRoot, { withFileTypes: true } )
19
+ .filter( ( entry ) => entry.isDirectory() )
20
+ .map( ( entry ) => entry.name )
21
+ .sort();
22
+ }
23
+
24
+ function hasSharedEditorEntries() {
25
+ for ( const relativePath of [ 'src/bindings/index.ts', 'src/bindings/index.js' ] ) {
26
+ if ( fs.existsSync( path.join( process.cwd(), relativePath ) ) ) {
27
+ return true;
28
+ }
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ function ensureEmptyWorkspaceArtifacts() {
35
+ const buildBlocksDir = path.join( process.cwd(), 'build', 'blocks' );
36
+ const manifestPath = path.join( process.cwd(), 'build', 'blocks-manifest.php' );
37
+ fs.rmSync( buildBlocksDir, { force: true, recursive: true } );
38
+ fs.mkdirSync( buildBlocksDir, { recursive: true } );
39
+ fs.writeFileSync(
40
+ manifestPath,
41
+ "<?php\nreturn array();\n",
42
+ 'utf8'
43
+ );
44
+ }
45
+
46
+ function getWpScriptsBin() {
47
+ return path.join(
48
+ process.cwd(),
49
+ 'node_modules',
50
+ '.bin',
51
+ process.platform === 'win32' ? 'wp-scripts.cmd' : 'wp-scripts'
52
+ );
53
+ }
54
+
55
+ function main() {
56
+ const mode = process.argv[ 2 ] ?? 'build';
57
+ const passthroughArgs = process.argv.slice( 3 );
58
+ if ( mode !== 'build' && mode !== 'start' ) {
59
+ throw new Error( `Unknown workspace build mode: ${ mode }` );
60
+ }
61
+
62
+ const blockSlugs = getBlockSlugs();
63
+ const hasSharedEntries = hasSharedEditorEntries();
64
+ if ( blockSlugs.length === 0 && ! hasSharedEntries ) {
65
+ ensureEmptyWorkspaceArtifacts();
66
+ console.log(
67
+ mode === 'start'
68
+ ? 'ℹ️ No workspace blocks are registered yet. Add one with `wp-typia add block <name> --template basic` before starting the watcher.'
69
+ : 'ℹ️ No workspace blocks are registered yet. Empty workspace build artifacts are up to date.'
70
+ );
71
+ return;
72
+ }
73
+
74
+ execFileSync(
75
+ getWpScriptsBin(),
76
+ [ mode, '--experimental-modules', '--blocks-manifest', ...passthroughArgs ],
77
+ {
78
+ cwd: process.cwd(),
79
+ stdio: 'inherit',
80
+ }
81
+ );
82
+
83
+ if ( blockSlugs.length === 0 ) {
84
+ ensureEmptyWorkspaceArtifacts();
85
+ }
86
+ }
87
+
88
+ main();
@@ -0,0 +1,124 @@
1
+ /* eslint-disable no-console */
2
+ import path from 'node:path';
3
+
4
+ import {
5
+ syncEndpointClient,
6
+ syncRestOpenApi,
7
+ syncTypeSchemas,
8
+ } from '@wp-typia/block-runtime/metadata-core';
9
+
10
+ import { BLOCKS, type WorkspaceBlockConfig } from './block-config';
11
+
12
+ function parseCliOptions( argv: string[] ) {
13
+ const options = {
14
+ check: false,
15
+ };
16
+
17
+ for ( const argument of argv ) {
18
+ if ( argument === '--check' ) {
19
+ options.check = true;
20
+ continue;
21
+ }
22
+
23
+ throw new Error( `Unknown sync-rest flag: ${ argument }` );
24
+ }
25
+
26
+ return options;
27
+ }
28
+
29
+ function isRestEnabledBlock(
30
+ block: WorkspaceBlockConfig
31
+ ): block is WorkspaceBlockConfig & {
32
+ apiTypesFile: string;
33
+ openApiFile: string;
34
+ restManifest: NonNullable< WorkspaceBlockConfig[ 'restManifest' ] >;
35
+ } {
36
+ return (
37
+ typeof block.apiTypesFile === 'string' &&
38
+ typeof block.openApiFile === 'string' &&
39
+ typeof block.restManifest === 'object' &&
40
+ block.restManifest !== null
41
+ );
42
+ }
43
+
44
+ async function main() {
45
+ const options = parseCliOptions( process.argv.slice( 2 ) );
46
+ const restBlocks = BLOCKS.filter( isRestEnabledBlock );
47
+
48
+ if ( restBlocks.length === 0 ) {
49
+ console.log(
50
+ 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.'
53
+ );
54
+ return;
55
+ }
56
+
57
+ for ( const block of restBlocks ) {
58
+ const contracts = block.restManifest.contracts;
59
+
60
+ for ( const [ baseName, contract ] of Object.entries( contracts ) ) {
61
+ await syncTypeSchemas(
62
+ {
63
+ jsonSchemaFile: path.join(
64
+ 'src',
65
+ 'blocks',
66
+ block.slug,
67
+ 'api-schemas',
68
+ `${ baseName }.schema.json`
69
+ ),
70
+ openApiFile: path.join(
71
+ 'src',
72
+ 'blocks',
73
+ block.slug,
74
+ 'api-schemas',
75
+ `${ baseName }.openapi.json`
76
+ ),
77
+ sourceTypeName: contract.sourceTypeName,
78
+ typesFile: block.apiTypesFile,
79
+ },
80
+ {
81
+ check: options.check,
82
+ }
83
+ );
84
+ }
85
+
86
+ await syncRestOpenApi(
87
+ {
88
+ manifest: block.restManifest,
89
+ openApiFile: block.openApiFile,
90
+ typesFile: block.apiTypesFile,
91
+ },
92
+ {
93
+ check: options.check,
94
+ }
95
+ );
96
+
97
+ await syncEndpointClient(
98
+ {
99
+ clientFile: path.join(
100
+ 'src',
101
+ 'blocks',
102
+ block.slug,
103
+ 'api-client.ts'
104
+ ),
105
+ manifest: block.restManifest,
106
+ typesFile: block.apiTypesFile,
107
+ },
108
+ {
109
+ check: options.check,
110
+ }
111
+ );
112
+ }
113
+
114
+ console.log(
115
+ options.check
116
+ ? '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents are already up to date with the TypeScript types!'
117
+ : '✅ REST contract schemas, portable API clients, and endpoint-aware OpenAPI documents generated from TypeScript types!'
118
+ );
119
+ }
120
+
121
+ main().catch( ( error ) => {
122
+ console.error( '❌ REST contract sync failed:', error );
123
+ process.exit( 1 );
124
+ } );
@@ -0,0 +1,65 @@
1
+ /* eslint-disable no-console */
2
+ import path from 'node:path';
3
+
4
+ import { syncBlockMetadata } from '@wp-typia/block-runtime/metadata-core';
5
+
6
+ import { BLOCKS } from './block-config';
7
+
8
+ function parseCliOptions( argv: string[] ) {
9
+ const options = {
10
+ check: false,
11
+ };
12
+
13
+ for ( const argument of argv ) {
14
+ if ( argument === '--check' ) {
15
+ options.check = true;
16
+ continue;
17
+ }
18
+
19
+ throw new Error( `Unknown sync-types flag: ${ argument }` );
20
+ }
21
+
22
+ return options;
23
+ }
24
+
25
+ async function main() {
26
+ const options = parseCliOptions( process.argv.slice( 2 ) );
27
+
28
+ if ( BLOCKS.length === 0 ) {
29
+ console.log(
30
+ options.check
31
+ ? 'ℹ️ No workspace blocks are registered yet. `sync-types --check` is already clean.'
32
+ : 'ℹ️ No workspace blocks are registered yet. Add one with `wp-typia add block <name> --template basic`.'
33
+ );
34
+ return;
35
+ }
36
+
37
+ for ( const block of BLOCKS ) {
38
+ const baseDir = path.join( 'src', 'blocks', block.slug );
39
+ const result = await syncBlockMetadata(
40
+ {
41
+ blockJsonFile: path.join( baseDir, 'block.json' ),
42
+ jsonSchemaFile: path.join( baseDir, 'typia.schema.json' ),
43
+ manifestFile: path.join( baseDir, 'typia.manifest.json' ),
44
+ openApiFile: path.join( baseDir, 'typia.openapi.json' ),
45
+ sourceTypeName: block.attributeTypeName,
46
+ typesFile: block.typesFile,
47
+ },
48
+ {
49
+ check: options.check,
50
+ }
51
+ );
52
+
53
+ console.log(
54
+ options.check
55
+ ? `✅ ${ block.slug }: block.json, typia.manifest.json, typia-validator.php, typia.schema.json, and typia.openapi.json are already up to date with the TypeScript types!`
56
+ : `✅ ${ block.slug }: block.json, typia.manifest.json, typia-validator.php, typia.schema.json, and typia.openapi.json were generated from TypeScript types!`
57
+ );
58
+ console.log( '📝 Generated attributes:', result.attributeNames );
59
+ }
60
+ }
61
+
62
+ main().catch( ( error ) => {
63
+ console.error( '❌ Type sync failed:', error );
64
+ process.exit( 1 );
65
+ } );
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,14 @@
1
+ import { registerBlockCollection } from '@wordpress/blocks';
2
+
3
+ const globalScope = globalThis as typeof globalThis & {
4
+ __wpTypiaCollections?: Record< string, true >;
5
+ };
6
+
7
+ globalScope.__wpTypiaCollections ??= {};
8
+
9
+ if ( ! globalScope.__wpTypiaCollections[ '{{namespace}}' ] ) {
10
+ registerBlockCollection( '{{namespace}}', {
11
+ title: '{{title}}',
12
+ } );
13
+ globalScope.__wpTypiaCollections[ '{{namespace}}' ] = true;
14
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "moduleResolution": "bundler",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "noEmit": true,
16
+ "baseUrl": ".",
17
+ "types": ["node", "wordpress__block-editor", "wordpress__blocks"]
18
+ },
19
+ "include": ["src/**/*", "scripts/**/*"],
20
+ "exclude": ["node_modules", "build"]
21
+ }
@@ -0,0 +1,267 @@
1
+ const fs = require( 'fs' );
2
+ const path = require( 'path' );
3
+ const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
4
+
5
+ const STATIC_METADATA_FILENAMES = new Set( [
6
+ 'block.json',
7
+ 'render.php',
8
+ 'typia.openapi.json',
9
+ 'typia.manifest.json',
10
+ 'typia.schema.json',
11
+ 'typia-validator.php',
12
+ ] );
13
+
14
+ function isMetadataAsset( filename ) {
15
+ return (
16
+ STATIC_METADATA_FILENAMES.has( filename ) ||
17
+ filename.endsWith( '.schema.json' ) ||
18
+ filename.endsWith( '.openapi.json' )
19
+ );
20
+ }
21
+
22
+ function normalizeScriptModuleAssetSource( source ) {
23
+ return String( source ).replace(
24
+ /'dependencies'\s*=>\s*array\([^)]*\)/,
25
+ "'dependencies' => array()"
26
+ );
27
+ }
28
+
29
+ function getBlockDirs() {
30
+ const blocksRoot = path.resolve( process.cwd(), 'src/blocks' );
31
+
32
+ if ( ! fs.existsSync( blocksRoot ) ) {
33
+ return [];
34
+ }
35
+
36
+ return fs
37
+ .readdirSync( blocksRoot, { withFileTypes: true } )
38
+ .filter( ( entry ) => entry.isDirectory() )
39
+ .map( ( entry ) => ( {
40
+ dir: path.join( blocksRoot, entry.name ),
41
+ slug: entry.name,
42
+ } ) );
43
+ }
44
+
45
+ function getMetadataEntries() {
46
+ const entries = [];
47
+
48
+ for ( const blockDir of getBlockDirs() ) {
49
+ for ( const inputPath of findMetadataFiles( blockDir.dir ) ) {
50
+ entries.push( {
51
+ inputPath,
52
+ outputPath: path.join(
53
+ 'blocks',
54
+ blockDir.slug,
55
+ path.relative( blockDir.dir, inputPath )
56
+ ),
57
+ } );
58
+ }
59
+ }
60
+
61
+ return entries;
62
+ }
63
+
64
+ function findMetadataFiles( directory ) {
65
+ const metadataFiles = [];
66
+
67
+ for ( const entry of fs.readdirSync( directory, {
68
+ withFileTypes: true,
69
+ } ) ) {
70
+ const entryPath = path.join( directory, entry.name );
71
+
72
+ if ( entry.isDirectory() ) {
73
+ metadataFiles.push( ...findMetadataFiles( entryPath ) );
74
+ continue;
75
+ }
76
+
77
+ if ( entry.isFile() && isMetadataAsset( entry.name ) ) {
78
+ metadataFiles.push( entryPath );
79
+ }
80
+ }
81
+
82
+ return metadataFiles;
83
+ }
84
+
85
+ function isScriptModuleAsset( assetName ) {
86
+ return /(^|\/)(interactivity|view)\.asset\.php$/.test( assetName );
87
+ }
88
+
89
+ function normalizeScriptModuleAssetsInDirectory( directory ) {
90
+ if ( ! fs.existsSync( directory ) ) {
91
+ return;
92
+ }
93
+
94
+ for ( const entry of fs.readdirSync( directory, {
95
+ withFileTypes: true,
96
+ } ) ) {
97
+ const entryPath = path.join( directory, entry.name );
98
+ if ( entry.isDirectory() ) {
99
+ normalizeScriptModuleAssetsInDirectory( entryPath );
100
+ continue;
101
+ }
102
+ if ( entry.isFile() && isScriptModuleAsset( entryPath ) ) {
103
+ fs.writeFileSync(
104
+ entryPath,
105
+ normalizeScriptModuleAssetSource(
106
+ fs.readFileSync( entryPath, 'utf8' )
107
+ )
108
+ );
109
+ }
110
+ }
111
+ }
112
+
113
+ class MetadataAssetPlugin {
114
+ apply( compiler ) {
115
+ compiler.hooks.thisCompilation.tap(
116
+ 'WorkspaceMetadataAssetPlugin',
117
+ ( compilation ) => {
118
+ compilation.hooks.processAssets.tap(
119
+ {
120
+ name: 'WorkspaceMetadataAssetPlugin',
121
+ stage: compiler.webpack.Compilation
122
+ .PROCESS_ASSETS_STAGE_ADDITIONS,
123
+ },
124
+ () => {
125
+ for ( const entry of getMetadataEntries() ) {
126
+ if ( compilation.getAsset( entry.outputPath ) ) {
127
+ continue;
128
+ }
129
+
130
+ compilation.emitAsset(
131
+ entry.outputPath,
132
+ new compiler.webpack.sources.RawSource(
133
+ fs.readFileSync( entry.inputPath )
134
+ )
135
+ );
136
+ }
137
+
138
+ for ( const asset of compilation.getAssets() ) {
139
+ if ( ! isScriptModuleAsset( asset.name ) ) {
140
+ continue;
141
+ }
142
+
143
+ compilation.updateAsset(
144
+ asset.name,
145
+ new compiler.webpack.sources.RawSource(
146
+ normalizeScriptModuleAssetSource(
147
+ asset.source.source()
148
+ )
149
+ )
150
+ );
151
+ }
152
+ }
153
+ );
154
+ }
155
+ );
156
+
157
+ compiler.hooks.afterEmit.tap(
158
+ 'WorkspaceMetadataAssetPlugin',
159
+ ( compilation ) => {
160
+ if ( ! compilation.outputOptions.path ) {
161
+ return;
162
+ }
163
+
164
+ normalizeScriptModuleAssetsInDirectory(
165
+ compilation.outputOptions.path
166
+ );
167
+ }
168
+ );
169
+ }
170
+ }
171
+
172
+ function toWebpackConfigs( config ) {
173
+ return Array.isArray( config ) ? config : [ config ];
174
+ }
175
+
176
+ function isModuleConfig( config ) {
177
+ return config?.output?.module === true;
178
+ }
179
+
180
+ function getEditorEntries() {
181
+ return Object.fromEntries(
182
+ getBlockDirs().map( ( blockDir ) => [
183
+ `blocks/${ blockDir.slug }/index`,
184
+ path.join( blockDir.dir, 'index.tsx' ),
185
+ ] )
186
+ );
187
+ }
188
+
189
+ function getSharedEditorEntries() {
190
+ const entries = [];
191
+
192
+ for ( const relativePath of [ 'src/bindings/index.ts', 'src/bindings/index.js' ] ) {
193
+ const entryPath = path.resolve( process.cwd(), relativePath );
194
+ if ( ! fs.existsSync( entryPath ) ) {
195
+ continue;
196
+ }
197
+
198
+ entries.push( [ 'bindings/index', entryPath ] );
199
+ break;
200
+ }
201
+
202
+ return Object.fromEntries( entries );
203
+ }
204
+
205
+ function getOptionalModuleEntries() {
206
+ const entries = [];
207
+
208
+ for ( const blockDir of getBlockDirs() ) {
209
+ for ( const filename of [
210
+ 'interactivity.ts',
211
+ 'interactivity.js',
212
+ 'view.ts',
213
+ 'view.js',
214
+ ] ) {
215
+ const entryPath = path.join( blockDir.dir, filename );
216
+ if ( ! fs.existsSync( entryPath ) ) {
217
+ continue;
218
+ }
219
+
220
+ entries.push( [
221
+ `blocks/${ blockDir.slug }/${ filename.replace(
222
+ /\.[^.]+$/,
223
+ ''
224
+ ) }`,
225
+ entryPath,
226
+ ] );
227
+ }
228
+ }
229
+
230
+ return Object.fromEntries( entries );
231
+ }
232
+
233
+ module.exports = async () => {
234
+ const { default: UnpluginTypia } = await import(
235
+ '@typia/unplugin/webpack'
236
+ );
237
+ const resolvedDefaultConfig =
238
+ typeof defaultConfig === 'function'
239
+ ? await defaultConfig()
240
+ : defaultConfig;
241
+ const editorEntries = {
242
+ ...getSharedEditorEntries(),
243
+ ...getEditorEntries(),
244
+ };
245
+ const moduleEntries = getOptionalModuleEntries();
246
+ const configs = toWebpackConfigs( resolvedDefaultConfig ).map(
247
+ ( config ) => ( {
248
+ ...config,
249
+ entry: async () =>
250
+ isModuleConfig( config ) ? moduleEntries : editorEntries,
251
+ resolve: {
252
+ ...( config.resolve || {} ),
253
+ extensionAlias: {
254
+ ...( config.resolve?.extensionAlias || {} ),
255
+ '.js': [ '.js', '.ts', '.tsx' ],
256
+ },
257
+ },
258
+ plugins: [
259
+ UnpluginTypia(),
260
+ ...( config.plugins || [] ),
261
+ new MetadataAssetPlugin(),
262
+ ],
263
+ } )
264
+ );
265
+
266
+ return configs.length === 1 ? configs[ 0 ] : configs;
267
+ };
@@ -0,0 +1,145 @@
1
+ <?php
2
+ /**
3
+ * Plugin Name: {{title}}
4
+ * Description: {{description}}
5
+ * Version: 0.1.0
6
+ * Requires at least: 6.7
7
+ * Tested up to: 6.9
8
+ * Requires PHP: 8.0
9
+ * Author: {{author}}
10
+ * License: GPL-2.0-or-later
11
+ * License URI: https://www.gnu.org/licenses/gpl-2.0.html
12
+ * Text Domain: {{textDomain}}
13
+ * Domain Path: /languages
14
+ */
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ foreach ( glob( __DIR__ . '/src/blocks/*/server.php' ) ?: array() as $server_module ) {
21
+ require_once $server_module;
22
+ }
23
+
24
+ function {{phpPrefix}}_load_textdomain() {
25
+ load_plugin_textdomain(
26
+ '{{textDomain}}',
27
+ false,
28
+ dirname( plugin_basename( __FILE__ ) ) . '/languages'
29
+ );
30
+ }
31
+
32
+ function {{phpPrefix}}_get_build_root() {
33
+ return __DIR__ . '/build/blocks';
34
+ }
35
+
36
+ function {{phpPrefix}}_get_blocks_manifest_path() {
37
+ return __DIR__ . '/build/blocks-manifest.php';
38
+ }
39
+
40
+ function {{phpPrefix}}_register_blocks_from_manifest_fallback() {
41
+ $build_root = {{phpPrefix}}_get_build_root();
42
+ $manifest_path = {{phpPrefix}}_get_blocks_manifest_path();
43
+ $manifest_data = file_exists( $manifest_path ) ? require $manifest_path : array();
44
+
45
+ if ( ! is_array( $manifest_data ) || empty( $manifest_data ) ) {
46
+ $block_dirs = glob( $build_root . '/*', GLOB_ONLYDIR );
47
+ if ( ! is_array( $block_dirs ) ) {
48
+ return;
49
+ }
50
+
51
+ foreach ( $block_dirs as $block_dir ) {
52
+ if ( file_exists( $block_dir . '/block.json' ) ) {
53
+ register_block_type( $block_dir );
54
+ }
55
+ }
56
+ return;
57
+ }
58
+
59
+ foreach ( array_keys( $manifest_data ) as $block_name ) {
60
+ $block_slug = is_string( $block_name ) && str_contains( $block_name, '/' )
61
+ ? substr( $block_name, strpos( $block_name, '/' ) + 1 )
62
+ : (string) $block_name;
63
+ $block_dir = trailingslashit( $build_root ) . $block_slug;
64
+
65
+ if ( file_exists( $block_dir . '/block.json' ) ) {
66
+ register_block_type( $block_dir );
67
+ }
68
+ }
69
+ }
70
+
71
+ function {{phpPrefix}}_register_blocks() {
72
+ $build_root = {{phpPrefix}}_get_build_root();
73
+ $manifest_path = {{phpPrefix}}_get_blocks_manifest_path();
74
+
75
+ if ( ! is_dir( $build_root ) ) {
76
+ return;
77
+ }
78
+
79
+ if (
80
+ file_exists( $manifest_path ) &&
81
+ function_exists( 'wp_register_block_metadata_collection' ) &&
82
+ function_exists( 'wp_register_block_types_from_metadata_collection' )
83
+ ) {
84
+ wp_register_block_metadata_collection( $build_root, $manifest_path );
85
+ wp_register_block_types_from_metadata_collection( $build_root, $manifest_path );
86
+ return;
87
+ }
88
+
89
+ {{phpPrefix}}_register_blocks_from_manifest_fallback();
90
+ }
91
+
92
+ function {{phpPrefix}}_register_binding_sources() {
93
+ foreach ( glob( __DIR__ . '/src/bindings/*/server.php' ) ?: array() as $binding_source_module ) {
94
+ require_once $binding_source_module;
95
+ }
96
+ }
97
+
98
+ function {{phpPrefix}}_enqueue_binding_sources_editor() {
99
+ $script_path = __DIR__ . '/build/bindings/index.js';
100
+ $asset_path = __DIR__ . '/build/bindings/index.asset.php';
101
+
102
+ if ( ! file_exists( $script_path ) || ! file_exists( $asset_path ) ) {
103
+ return;
104
+ }
105
+
106
+ $asset = require $asset_path;
107
+ if ( ! is_array( $asset ) ) {
108
+ $asset = array();
109
+ }
110
+
111
+ wp_enqueue_script(
112
+ '{{slugKebabCase}}-binding-sources',
113
+ plugins_url( 'build/bindings/index.js', __FILE__ ),
114
+ isset( $asset['dependencies'] ) && is_array( $asset['dependencies'] ) ? $asset['dependencies'] : array(),
115
+ isset( $asset['version'] ) ? $asset['version'] : filemtime( $script_path ),
116
+ true
117
+ );
118
+ }
119
+
120
+ function {{phpPrefix}}_register_pattern_category() {
121
+ if ( function_exists( 'register_block_pattern_category' ) ) {
122
+ register_block_pattern_category(
123
+ '{{namespace}}',
124
+ array(
125
+ 'label' => sprintf(
126
+ __( '%s Patterns', '{{textDomain}}' ),
127
+ {{titleJson}}
128
+ ),
129
+ )
130
+ );
131
+ }
132
+ }
133
+
134
+ function {{phpPrefix}}_register_patterns() {
135
+ foreach ( glob( __DIR__ . '/src/patterns/*.php' ) ?: array() as $pattern_module ) {
136
+ require $pattern_module;
137
+ }
138
+ }
139
+
140
+ add_action( 'init', '{{phpPrefix}}_load_textdomain' );
141
+ add_action( 'init', '{{phpPrefix}}_register_blocks' );
142
+ add_action( 'init', '{{phpPrefix}}_register_binding_sources', 20 );
143
+ add_action( 'init', '{{phpPrefix}}_register_pattern_category' );
144
+ add_action( 'init', '{{phpPrefix}}_register_patterns', 20 );
145
+ add_action( 'enqueue_block_editor_assets', '{{phpPrefix}}_enqueue_binding_sources_editor' );