diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md index 3f624be966f9c..046107a475769 100644 --- a/docs/reference-guides/block-api/block-metadata.md +++ b/docs/reference-guides/block-api/block-metadata.md @@ -56,7 +56,7 @@ From a performance perspective, when themes support lazy loading assets, blocks Furthermore, because the [Block Type REST API Endpoint](https://developer.wordpress.org/rest-api/reference/block-types/) can only list blocks registered on the server, registering blocks server-side is recommended; using the `block.json` file simplifies this registration. -The [WordPress Plugins Directory](https://wordpress.org/plugins/) can detect `block.json` files, highlight blocks included in plugins, and extract their metadata. If you wish to [submit your block(s) to the Block Directory](/docs/getting-started/tutorials/create-block/submitting-to-block-directory.md), all blocks contained in your plugin must have a `block.json` file for the Block Directory to recognize them. +The [WordPress Plugins Directory](https://wordpress.org/plugins/) can detect `block.json` files, highlight blocks included in plugins, and extract their metadata. If you wish to [submit your block(s) to the Block Directory](/docs/getting-started/create-block/submitting-to-block-directory.md), all blocks contained in your plugin must have a `block.json` file for the Block Directory to recognize them. Development is improved by using a defined schema definition file. Supported editors can provide help like tooltips, autocomplete, and schema validation. To use the schema, add the following to the top of the `block.json`. diff --git a/package-lock.json b/package-lock.json index a1a07aff43ee0..c8fbc22c09966 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17326,6 +17326,7 @@ "chalk": "^4.0.0", "check-node-version": "^4.1.0", "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^10.2.0", "cross-spawn": "^5.1.0", "css-loader": "^6.2.0", "cssnano": "^5.0.7", @@ -18112,6 +18113,15 @@ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", "dev": true }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, "ajv-keywords": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", @@ -29104,140 +29114,88 @@ } }, "copy-webpack-plugin": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.0.1.tgz", - "integrity": "sha512-14gHKKdYIxF84jCEgPgYXCPpldbwpxxLbCmA7LReY7gvbaT555DgeBWBgBZM116tv/fO6RRJrsivBqRyRlukhw==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.0.tgz", + "integrity": "sha512-my6iXII95c78w14HzYCNya5TlJYa44lOppAge5GSTMM1SyDxNsVGCJvhP4/ld6snm8lzjn3XOonMZD6s1L86Og==", "dev": true, "requires": { - "fast-glob": "^3.2.5", - "glob-parent": "^6.0.0", - "globby": "^11.0.3", + "fast-glob": "^3.2.7", + "glob-parent": "^6.0.1", + "globby": "^12.0.2", "normalize-path": "^3.0.0", - "p-limit": "^3.1.0", - "schema-utils": "^3.0.0", + "schema-utils": "^4.0.0", "serialize-javascript": "^6.0.0" }, "dependencies": { - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, "@types/json-schema": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", - "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", + "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fast-deep-equal": "^3.1.3" } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, "glob-parent": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.1.tgz", - "integrity": "sha512-kEVjS71mQazDBHKcsq4E9u/vUzaLcw1A8EtUeydawvIWQCJM0qQ08G1H7/XTjFUulla6XQiDOG6MXSaG0HDKog==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.0.2.tgz", + "integrity": "sha512-lAsmb/5Lww4r7MM9nCCliDZVIKbZTavrsunAsHLr9oHthrZP1qi7/gAnHOsUs9bLvEt2vKVJhHmxuL7QbDuPdQ==", "dev": true, "requires": { - "array-union": "^2.1.0", + "array-union": "^3.0.1", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "fast-glob": "^3.2.7", + "ignore": "^5.1.8", + "merge2": "^1.4.1", + "slash": "^4.0.0" } }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, "merge2": { @@ -29246,46 +29204,22 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true - }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" } }, "serialize-javascript": { @@ -29297,14 +29231,11 @@ "randombytes": "^2.1.0" } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true } } }, diff --git a/package.json b/package.json index ce6120159c176..406447e3ad9f3 100755 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "chalk": "4.1.1", "commander": "4.1.0", "concurrently": "3.5.0", - "copy-webpack-plugin": "9.0.1", + "copy-webpack-plugin": "10.2.0", "core-js-builder": "3.19.1", "cross-env": "3.2.4", "css-loader": "6.2.0", diff --git a/packages/create-block/CHANGELOG.md b/packages/create-block/CHANGELOG.md index fc39fdd09b5e3..9cfaa4347b867 100644 --- a/packages/create-block/CHANGELOG.md +++ b/packages/create-block/CHANGELOG.md @@ -5,6 +5,8 @@ ### New Features - Integrated a new `plugin-zip` command to create a zip file for a WordPress plugin ([#37687](https://github.com/WordPress/gutenberg/pull/37687)). +- Add support for handling block templates with the `blockTemplatesPath` field in the external template configuration ([#37612](https://github.com/WordPress/gutenberg/pull/37612)). +- Add a new field `folderName` for setting the location for the `block.json` file and other optional block files generated from block templates included in the folder set with the `blockTemplatesPath` setting ([#37612](https://github.com/WordPress/gutenberg/pull/37612)). ### Enhancement diff --git a/packages/create-block/README.md b/packages/create-block/README.md index d93908647acb2..1e56c1f65fb84 100644 --- a/packages/create-block/README.md +++ b/packages/create-block/README.md @@ -12,7 +12,7 @@ Visit the [Gutenberg handbook](https://developer.wordpress.org/block-editor/deve ## Quick start -You just need to provide the `slug` which is the target location for scaffolded files and the internal block name. +You just need to provide the `slug` which is the target location for scaffolded plugin files and the internal block name. ```bash $ npx @wordpress/create-block todo-list @@ -40,10 +40,10 @@ Options: ```bash -V, --version output the version number --t, --template block template type name, allowed values: "es5", "esnext", the name of an external npm package (default: "esnext"), or the path to a local directory. +-t, --template plugin template type name, allowed values: "es5", "esnext", the name of an external npm package (default: "esnext"), or the path to a local directory. --namespace internal namespace for the block name ---title display title for the block ---short-description short description for the block +--title display title for the plugin/block +--short-description short description for the plugin/block --category category name for the block --wp-scripts enable integration with `@wordpress/scripts` package --no-wp-scripts disable integration with `@wordpress/scripts` package @@ -131,7 +131,7 @@ It is mandatory to provide the main file (`index.js` by default) for the package #### `templatesPath` -A mandatory field with the path pointing to the location where template files live (nested folders are also supported). All files without the `.mustache` extension will be ignored. +A mandatory field with the path pointing to the location where plugin template files live (nested folders are also supported). All files without the `.mustache` extension will be ignored. _Example:_ @@ -139,13 +139,27 @@ _Example:_ const { join } = require( 'path' ); module.exports = { - templatesPath: join( __dirname, 'templates' ), + templatesPath: join( __dirname, 'plugin-templates' ), +}; +``` + +#### `blockTemplatesPath` + +An optional field with the path pointing to the location where template files for the individual block live (nested folders are also supported). All files without the `.mustache` extension will be ignored. + +_Example:_ + +```js +const { join } = require( 'path' ); + +module.exports = { + blockTemplatesPath: join( __dirname, 'block-templates' ), }; ``` #### `assetsPath` -This setting is useful when your template scaffolds a block that uses static assets like images or fonts, which should not be processed. It provides the path pointing to the location where assets are located. They will be copied to the `assets` subfolder in the generated plugin. +This setting is useful when your template scaffolds a plugin that uses static assets like images or fonts, which should not be processed. It provides the path pointing to the location where assets are located. They will be copied to the `assets` subfolder in the generated plugin. _Example:_ @@ -153,7 +167,7 @@ _Example:_ const { join } = require( 'path' ); module.exports = { - assetsPath: join( __dirname, 'assets' ), + assetsPath: join( __dirname, 'plugin-assets' ), }; ``` @@ -193,7 +207,8 @@ The following configurable variables are used with the template files. Template - `version` (default: `'0.1.0'`) - `wpScripts` (default: `true`) - `wpEnv` (default: `false`) -- `npmDependencies` (default: `[]`) – the list of remote npm packages to be installed in the project with [`npm install`](https://docs.npmjs.com/cli/v6/commands/npm-install). +- `npmDependencies` (default: `[]`) – the list of remote npm packages to be installed in the project with [`npm install`](https://docs.npmjs.com/cli/v8/commands/npm-install) when `wpScripts` is enabled. +- `folderName` (default: `.`) – the location for the `block.json` file and other optional block files generated from block templates included in the folder set with the `blockTemplatesPath` setting. - `editorScript` (default: `'file:./build/index.js'`) - `editorStyle` (default: `'file:./build/index.css'`) - `style` (default: `'file:./build/style-index.css'`) diff --git a/packages/create-block/lib/index.js b/packages/create-block/lib/index.js index 2ce1bcb12347b..da4a903ca49d7 100644 --- a/packages/create-block/lib/index.js +++ b/packages/create-block/lib/index.js @@ -14,7 +14,7 @@ const log = require( './log' ); const { engines, version } = require( '../package.json' ); const scaffold = require( './scaffold' ); const { - getBlockTemplate, + getPluginTemplate, getDefaultValues, getPrompts, } = require( './templates' ); @@ -34,13 +34,16 @@ program .arguments( '[slug]' ) .option( '-t, --template ', - 'block template type name, allowed values: "es5", "esnext", or the name of an external npm package', + 'plugin template type name, allowed values: "es5", "esnext", or the name of an external npm package', 'esnext' ) .option( '--namespace ', 'internal namespace for the block name' ) - .option( '--title ', 'display title for the block' ) + .option( '--title ', 'display title for the plugin/block' ) // The name "description" is used internally so it couldn't be used. - .option( '--short-description ', 'short description for the block' ) + .option( + '--short-description ', + 'short description for the plugin/block' + ) .option( '--category ', 'category name for the block' ) .option( '--wp-scripts', @@ -66,8 +69,8 @@ program ) => { await checkSystemRequirements( engines ); try { - const blockTemplate = await getBlockTemplate( templateName ); - const defaultValues = getDefaultValues( blockTemplate ); + const pluginTemplate = await getPluginTemplate( templateName ); + const defaultValues = getDefaultValues( pluginTemplate ); const optionsValues = pickBy( { category, @@ -88,16 +91,16 @@ program title: startCase( slug ), ...optionsValues, }; - await scaffold( blockTemplate, answers ); + await scaffold( pluginTemplate, answers ); } else { - const prompts = getPrompts( blockTemplate ).filter( + const prompts = getPrompts( pluginTemplate ).filter( ( { name } ) => ! Object.keys( optionsValues ).includes( name ) ); log.info( '' ); - log.info( "Let's customize your block:" ); + log.info( "Let's customize your WordPress plugin:" ); const answers = await inquirer.prompt( prompts ); - await scaffold( blockTemplate, { + await scaffold( pluginTemplate, { ...defaultValues, ...optionsValues, ...answers, diff --git a/packages/create-block/lib/init-block-json.js b/packages/create-block/lib/init-block.js similarity index 56% rename from packages/create-block/lib/init-block-json.js rename to packages/create-block/lib/init-block.js index 1520c6d0c5910..b02aaf142a816 100644 --- a/packages/create-block/lib/init-block-json.js +++ b/packages/create-block/lib/init-block.js @@ -2,15 +2,17 @@ * External dependencies */ const { omitBy } = require( 'lodash' ); -const { join } = require( 'path' ); +const { dirname, join } = require( 'path' ); +const makeDir = require( 'make-dir' ); const { writeFile } = require( 'fs' ).promises; /** * Internal dependencies */ const { info } = require( './log' ); +const { writeOutputTemplate } = require( './output' ); -module.exports = async ( { +async function initBlockJSON( { $schema, apiVersion, slug, @@ -23,13 +25,16 @@ module.exports = async ( { supports, dashicon, textdomain, + folderName, editorScript, editorStyle, style, -} ) => { - const outputFile = join( process.cwd(), slug, 'block.json' ); +} ) { info( '' ); info( 'Creating a "block.json" file.' ); + + const outputFile = join( process.cwd(), slug, folderName, 'block.json' ); + await makeDir( dirname( outputFile ) ); await writeFile( outputFile, JSON.stringify( @@ -56,4 +61,19 @@ module.exports = async ( { '\t' ) ); +} + +module.exports = async function ( outputTemplates, view ) { + await Promise.all( + Object.keys( outputTemplates ).map( + async ( outputFile ) => + await writeOutputTemplate( + outputTemplates[ outputFile ], + join( view.folderName, outputFile ), + view + ) + ) + ); + + await initBlockJSON( view ); }; diff --git a/packages/create-block/lib/output.js b/packages/create-block/lib/output.js new file mode 100644 index 0000000000000..9671a52bb20c3 --- /dev/null +++ b/packages/create-block/lib/output.js @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +const { dirname, join } = require( 'path' ); +const makeDir = require( 'make-dir' ); +const { render } = require( 'mustache' ); +const { writeFile } = require( 'fs' ).promises; + +const writeOutputAsset = async ( inputFile, outputFile, view ) => { + const outputFilePath = join( view.slug, 'assets', outputFile ); + await makeDir( dirname( outputFilePath ) ); + writeFile( outputFilePath, inputFile ); +}; + +const writeOutputTemplate = async ( inputFile, outputFile, view ) => { + // Output files can have names that depend on the slug provided. + const outputFilePath = join( + view.slug, + outputFile.replace( /\$slug/g, view.slug ) + ); + await makeDir( dirname( outputFilePath ) ); + writeFile( outputFilePath, render( inputFile, view ) ); +}; + +module.exports = { + writeOutputAsset, + writeOutputTemplate, +}; diff --git a/packages/create-block/lib/scaffold.js b/packages/create-block/lib/scaffold.js index f5c89bad0768f..7efb364af534d 100644 --- a/packages/create-block/lib/scaffold.js +++ b/packages/create-block/lib/scaffold.js @@ -1,23 +1,20 @@ /** * External dependencies */ -const { writeFile } = require( 'fs' ).promises; const { snakeCase, camelCase, upperFirst } = require( 'lodash' ); -const makeDir = require( 'make-dir' ); -const { render } = require( 'mustache' ); -const { dirname, join } = require( 'path' ); /** * Internal dependencies */ -const initBlockJSON = require( './init-block-json' ); +const initBlock = require( './init-block' ); const initPackageJSON = require( './init-package-json' ); const initWPScripts = require( './init-wp-scripts' ); const initWPEnv = require( './init-wp-env' ); const { code, info, success } = require( './log' ); +const { writeOutputAsset, writeOutputTemplate } = require( './output' ); module.exports = async ( - blockTemplate, + { blockOutputTemplates, outputTemplates, outputAssets }, { $schema, apiVersion, @@ -36,6 +33,7 @@ module.exports = async ( wpScripts, wpEnv, npmDependencies, + folderName, editorScript, editorStyle, style, @@ -45,9 +43,8 @@ module.exports = async ( namespace = namespace.toLowerCase(); info( '' ); - info( `Creating a new WordPress block in "${ slug }" folder.` ); + info( `Creating a new WordPress plugin in "${ slug }" folder.` ); - const { outputTemplates, outputAssets } = blockTemplate; const view = { $schema, apiVersion, @@ -69,34 +66,36 @@ module.exports = async ( textdomain: slug, wpScripts, npmDependencies, + folderName, editorScript, editorStyle, style, }; + await Promise.all( - Object.keys( outputTemplates ).map( async ( outputFile ) => { - // Output files can have names that depend on the slug provided. - const outputFilePath = join( - slug, - outputFile.replace( /\$slug/g, slug ) - ); - await makeDir( dirname( outputFilePath ) ); - writeFile( - outputFilePath, - render( outputTemplates[ outputFile ], view ) - ); - } ) + Object.keys( outputTemplates ).map( + async ( outputFile ) => + await writeOutputTemplate( + outputTemplates[ outputFile ], + outputFile, + view + ) + ) ); await Promise.all( - Object.keys( outputAssets ).map( async ( outputFile ) => { - const outputFilePath = join( slug, 'assets', outputFile ); - await makeDir( dirname( outputFilePath ) ); - writeFile( outputFilePath, outputAssets[ outputFile ] ); - } ) + Object.keys( outputAssets ).map( + async ( outputFile ) => + await writeOutputAsset( + outputAssets[ outputFile ], + outputFile, + view + ) + ) ); - await initBlockJSON( view ); + await initBlock( blockOutputTemplates, view ); + await initPackageJSON( view ); if ( wpScripts ) { diff --git a/packages/create-block/lib/templates.js b/packages/create-block/lib/templates.js index 054573402d3b3..155a306c033ba 100644 --- a/packages/create-block/lib/templates.js +++ b/packages/create-block/lib/templates.js @@ -19,7 +19,7 @@ const CLIError = require( './cli-error' ); const { info } = require( './log' ); const prompts = require( './prompts' ); -const predefinedBlockTemplates = { +const predefinedPluginTemplates = { es5: { defaultValues: { slug: 'es5-example', @@ -44,8 +44,13 @@ const predefinedBlockTemplates = { supports: { html: false, }, + folderName: 'src', + editorScript: 'file:./index.js', + editorStyle: 'file:./index.css', + style: 'file:./style-index.css', }, - templatesPath: join( __dirname, 'templates', 'esnext' ), + templatesPath: join( __dirname, 'templates', 'esnext', 'plugin' ), + blockTemplatesPath: join( __dirname, 'templates', 'esnext', 'block' ), }, }; @@ -99,6 +104,7 @@ const externalTemplateExists = async ( templateName ) => { const configToTemplate = async ( { assetsPath, + blockTemplatesPath, defaultValues = {}, templatesPath, } ) => { @@ -107,16 +113,19 @@ const configToTemplate = async ( { } return { + blockOutputTemplates: blockTemplatesPath + ? await getOutputTemplates( blockTemplatesPath ) + : {}, defaultValues, outputAssets: assetsPath ? await getOutputAssets( assetsPath ) : {}, outputTemplates: await getOutputTemplates( templatesPath ), }; }; -const getBlockTemplate = async ( templateName ) => { - if ( predefinedBlockTemplates[ templateName ] ) { +const getPluginTemplate = async ( templateName ) => { + if ( predefinedPluginTemplates[ templateName ] ) { return await configToTemplate( - predefinedBlockTemplates[ templateName ] + predefinedPluginTemplates[ templateName ] ); } @@ -137,8 +146,8 @@ const getBlockTemplate = async ( templateName ) => { if ( ! ( await externalTemplateExists( templateName ) ) ) { throw new CLIError( - `Invalid block template type name: "${ templateName }". Allowed values: ` + - Object.keys( predefinedBlockTemplates ) + `Invalid plugin template type name: "${ templateName }". Allowed values: ` + + Object.keys( predefinedPluginTemplates ) .map( ( name ) => `"${ name }"` ) .join( ', ' ) + ', or an existing npm package name.' @@ -168,7 +177,7 @@ const getBlockTemplate = async ( templateName ) => { throw error; } else { throw new CLIError( - `Invalid block template downloaded. Error: ${ error.message }` + `Invalid plugin template downloaded. Error: ${ error.message }` ); } } finally { @@ -178,7 +187,7 @@ const getBlockTemplate = async ( templateName ) => { } }; -const getDefaultValues = ( blockTemplate ) => { +const getDefaultValues = ( pluginTemplate ) => { return { $schema: 'https://schemas.wp.org/trunk/block.json', apiVersion: 2, @@ -191,15 +200,16 @@ const getDefaultValues = ( blockTemplate ) => { wpScripts: true, wpEnv: false, npmDependencies: [], + folderName: '.', editorScript: 'file:./build/index.js', editorStyle: 'file:./build/index.css', style: 'file:./build/style-index.css', - ...blockTemplate.defaultValues, + ...pluginTemplate.defaultValues, }; }; -const getPrompts = ( blockTemplate ) => { - const defaultValues = getDefaultValues( blockTemplate ); +const getPrompts = ( pluginTemplate ) => { + const defaultValues = getDefaultValues( pluginTemplate ); return Object.keys( prompts ).map( ( promptName ) => { return { ...prompts[ promptName ], @@ -209,7 +219,7 @@ const getPrompts = ( blockTemplate ) => { }; module.exports = { - getBlockTemplate, + getPluginTemplate, getDefaultValues, getPrompts, }; diff --git a/packages/create-block/lib/templates/esnext/src/edit.js.mustache b/packages/create-block/lib/templates/esnext/block/edit.js.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/src/edit.js.mustache rename to packages/create-block/lib/templates/esnext/block/edit.js.mustache diff --git a/packages/create-block/lib/templates/esnext/src/editor.scss.mustache b/packages/create-block/lib/templates/esnext/block/editor.scss.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/src/editor.scss.mustache rename to packages/create-block/lib/templates/esnext/block/editor.scss.mustache diff --git a/packages/create-block/lib/templates/esnext/src/index.js.mustache b/packages/create-block/lib/templates/esnext/block/index.js.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/src/index.js.mustache rename to packages/create-block/lib/templates/esnext/block/index.js.mustache diff --git a/packages/create-block/lib/templates/esnext/src/save.js.mustache b/packages/create-block/lib/templates/esnext/block/save.js.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/src/save.js.mustache rename to packages/create-block/lib/templates/esnext/block/save.js.mustache diff --git a/packages/create-block/lib/templates/esnext/src/style.scss.mustache b/packages/create-block/lib/templates/esnext/block/style.scss.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/src/style.scss.mustache rename to packages/create-block/lib/templates/esnext/block/style.scss.mustache diff --git a/packages/create-block/lib/templates/esnext/$slug.php.mustache b/packages/create-block/lib/templates/esnext/plugin/$slug.php.mustache similarity index 95% rename from packages/create-block/lib/templates/esnext/$slug.php.mustache rename to packages/create-block/lib/templates/esnext/plugin/$slug.php.mustache index 3194175ebef61..97e52cb5d1322 100644 --- a/packages/create-block/lib/templates/esnext/$slug.php.mustache +++ b/packages/create-block/lib/templates/esnext/plugin/$slug.php.mustache @@ -29,6 +29,6 @@ * @see https://developer.wordpress.org/reference/functions/register_block_type/ */ function {{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init() { - register_block_type( __DIR__ ); + register_block_type( __DIR__ . '/build' ); } add_action( 'init', '{{namespaceSnakeCase}}_{{slugSnakeCase}}_block_init' ); diff --git a/packages/create-block/lib/templates/esnext/.editorconfig.mustache b/packages/create-block/lib/templates/esnext/plugin/.editorconfig.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/.editorconfig.mustache rename to packages/create-block/lib/templates/esnext/plugin/.editorconfig.mustache diff --git a/packages/create-block/lib/templates/esnext/.gitignore.mustache b/packages/create-block/lib/templates/esnext/plugin/.gitignore.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/.gitignore.mustache rename to packages/create-block/lib/templates/esnext/plugin/.gitignore.mustache diff --git a/packages/create-block/lib/templates/esnext/readme.txt.mustache b/packages/create-block/lib/templates/esnext/plugin/readme.txt.mustache similarity index 100% rename from packages/create-block/lib/templates/esnext/readme.txt.mustache rename to packages/create-block/lib/templates/esnext/plugin/readme.txt.mustache diff --git a/packages/scripts/CHANGELOG.md b/packages/scripts/CHANGELOG.md index 0b2bd20d740cd..6db8b19b16348 100644 --- a/packages/scripts/CHANGELOG.md +++ b/packages/scripts/CHANGELOG.md @@ -13,6 +13,7 @@ - Added a new `plugin-zip` command to create a zip file for a WordPress plugin ([#37687](https://github.com/WordPress/gutenberg/pull/37687)). - Added optional support for React Fast Refresh in the `start` command. It can be activated with `--hot` CLI argument ([#28273](https://github.com/WordPress/gutenberg/pull/28273)). +- Automatically copy `block.json` files located in the `src` folder and its subfolders to the output folder (`build` by default) ([#37612](https://github.com/WordPress/gutenberg/pull/37612)). ### Bug Fixes diff --git a/packages/scripts/config/webpack.config.js b/packages/scripts/config/webpack.config.js index a2152a7eabac6..7ddb22219c9c7 100644 --- a/packages/scripts/config/webpack.config.js +++ b/packages/scripts/config/webpack.config.js @@ -3,6 +3,7 @@ */ const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); const { CleanWebpackPlugin } = require( 'clean-webpack-plugin' ); +const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); const browserslist = require( 'browserslist' ); const { sync: glob } = require( 'fast-glob' ); const MiniCSSExtractPlugin = require( 'mini-css-extract-plugin' ); @@ -233,6 +234,15 @@ const config = { // multiple configurations returned in the webpack config. cleanStaleWebpackAssets: false, } ), + new CopyWebpackPlugin( { + patterns: [ + { + from: '**/block.json', + context: 'src', + noErrorOnMissing: true, + }, + ], + } ), // The WP_BUNDLE_ANALYZER global variable enables a utility that represents // bundle content as a convenient interactive zoomable treemap. process.env.WP_BUNDLE_ANALYZER && new BundleAnalyzerPlugin(), diff --git a/packages/scripts/package.json b/packages/scripts/package.json index 66620264aee79..744f41389ed43 100644 --- a/packages/scripts/package.json +++ b/packages/scripts/package.json @@ -51,6 +51,7 @@ "chalk": "^4.0.0", "check-node-version": "^4.1.0", "clean-webpack-plugin": "^3.0.0", + "copy-webpack-plugin": "^10.2.0", "cross-spawn": "^5.1.0", "css-loader": "^6.2.0", "cssnano": "^5.0.7",