diff --git a/.eslintrc.js b/.eslintrc.js
index 77c4e0eaa7003..6aee436e02461 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -38,6 +38,10 @@ module.exports = {
selector: 'ImportDeclaration[source.value=/^@wordpress\\u002F.+\\u002F/]',
message: 'Path access on WordPress dependencies is not allowed.',
},
+ {
+ selector: 'ImportDeclaration[source.value=/^blob$/]',
+ message: 'Use @wordpress/blob as import path instead.',
+ },
{
selector: 'ImportDeclaration[source.value=/^blocks$/]',
message: 'Use @wordpress/blocks as import path instead.',
diff --git a/blocks/api/raw-handling/image-corrector.js b/blocks/api/raw-handling/image-corrector.js
index c6266c4d99db8..aae34d4628954 100644
--- a/blocks/api/raw-handling/image-corrector.js
+++ b/blocks/api/raw-handling/image-corrector.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { createBlobURL } from '@wordpress/utils';
+import { createBlobURL } from '@wordpress/blob';
/**
* Browser dependencies
diff --git a/core-blocks/image/edit.js b/core-blocks/image/edit.js
index 2ab94e4f28579..7e8c0ce7c0c25 100644
--- a/core-blocks/image/edit.js
+++ b/core-blocks/image/edit.js
@@ -16,7 +16,7 @@ import {
*/
import { __ } from '@wordpress/i18n';
import { Component, compose, Fragment } from '@wordpress/element';
-import { getBlobByURL, revokeBlobURL } from '@wordpress/utils';
+import { getBlobByURL, revokeBlobURL } from '@wordpress/blob';
import {
Button,
ButtonGroup,
diff --git a/editor/components/rich-text/index.js b/editor/components/rich-text/index.js
index 238dae578486b..bb338367585d7 100644
--- a/editor/components/rich-text/index.js
+++ b/editor/components/rich-text/index.js
@@ -25,10 +25,8 @@ import {
getScrollContainer,
} from '@wordpress/dom';
import deprecated from '@wordpress/deprecated';
-import {
- keycodes,
- createBlobURL,
-} from '@wordpress/utils';
+import { createBlobURL } from '@wordpress/blob';
+import { keycodes } from '@wordpress/utils';
import { withInstanceId, withSafeTimeout, Slot } from '@wordpress/components';
import { withSelect } from '@wordpress/data';
import { rawHandler } from '@wordpress/blocks';
diff --git a/lib/client-assets.php b/lib/client-assets.php
index b722feba7bd1f..71be6a780ef12 100644
--- a/lib/client-assets.php
+++ b/lib/client-assets.php
@@ -122,6 +122,13 @@ function gutenberg_register_scripts_and_styles() {
filemtime( gutenberg_dir_path() . 'build/deprecated/index.js' ),
true
);
+ wp_register_script(
+ 'wp-blob',
+ gutenberg_url( 'build/blob/index.js' ),
+ array(),
+ filemtime( gutenberg_dir_path() . 'build/blob/index.js' ),
+ true
+ );
wp_register_script(
'wp-data',
gutenberg_url( 'build/data/index.js' ),
@@ -146,7 +153,7 @@ function gutenberg_register_scripts_and_styles() {
wp_register_script(
'wp-utils',
gutenberg_url( 'build/utils/index.js' ),
- array( 'lodash', 'wp-deprecated', 'wp-dom' ),
+ array( 'lodash', 'wp-blob', 'wp-deprecated', 'wp-dom' ),
filemtime( gutenberg_dir_path() . 'build/utils/index.js' ),
true
);
@@ -213,7 +220,7 @@ function gutenberg_register_scripts_and_styles() {
wp_register_script(
'wp-blocks',
gutenberg_url( 'build/blocks/index.js' ),
- array( 'wp-deprecated', 'wp-dom', 'wp-element', 'wp-utils', 'wp-hooks', 'wp-i18n', 'shortcode', 'wp-data', 'lodash' ),
+ array( 'wp-blob', 'wp-deprecated', 'wp-dom', 'wp-element', 'wp-hooks', 'wp-i18n', 'shortcode', 'wp-data', 'lodash' ),
filemtime( gutenberg_dir_path() . 'build/blocks/index.js' ),
true
);
@@ -238,6 +245,7 @@ function gutenberg_register_scripts_and_styles() {
array(
'editor',
'lodash',
+ 'wp-blob',
'wp-blocks',
'wp-components',
'wp-core-data',
@@ -330,6 +338,7 @@ function gutenberg_register_scripts_and_styles() {
'postbox',
'wp-a11y',
'wp-api',
+ 'wp-blob',
'wp-blocks',
'wp-components',
'wp-core-data',
diff --git a/packages/blob/.npmrc b/packages/blob/.npmrc
new file mode 100644
index 0000000000000..43c97e719a5a8
--- /dev/null
+++ b/packages/blob/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/packages/blob/README.md b/packages/blob/README.md
new file mode 100644
index 0000000000000..71a88feefcc42
--- /dev/null
+++ b/packages/blob/README.md
@@ -0,0 +1,13 @@
+# @wordpress/blob
+
+Blob utils for WordPress.
+
+## Installation
+
+Install the module
+
+```bash
+npm install @wordpress/blob@next --save
+```
+
+
diff --git a/packages/blob/package.json b/packages/blob/package.json
new file mode 100644
index 0000000000000..434f648c5b4cc
--- /dev/null
+++ b/packages/blob/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@wordpress/blob",
+ "version": "0.0.1",
+ "description": "Blob utils for WordPress",
+ "author": "WordPress",
+ "license": "GPL-2.0-or-later",
+ "keywords": [
+ "wordpress",
+ "blob"
+ ],
+ "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/blob/README.md",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WordPress/gutenberg.git"
+ },
+ "bugs": {
+ "url": "https://github.com/WordPress/gutenberg/issues"
+ },
+ "main": "build/index.js",
+ "module": "build-module/index.js",
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/utils/blob-cache.js b/packages/blob/src/index.js
similarity index 100%
rename from utils/blob-cache.js
rename to packages/blob/src/index.js
diff --git a/test/unit/jest.config.json b/test/unit/jest.config.json
index c4dd16ffc48f0..1bd5665d0ba99 100644
--- a/test/unit/jest.config.json
+++ b/test/unit/jest.config.json
@@ -6,7 +6,7 @@
],
"moduleNameMapper": {
"@wordpress\\/(blocks|components|editor|data|utils|edit-post|viewport|plugins|core-data|core-blocks)$": "$1",
- "@wordpress\\/(date|dom|deprecated|element)$": "packages/$1/src"
+ "@wordpress\\/(blob|date|dom|deprecated|element)$": "packages/$1/src"
},
"preset": "@wordpress/jest-preset-default",
"setupFiles": [
diff --git a/utils/deprecated.js b/utils/deprecated.js
index cdc66c5b44eb9..44447e6a6a940 100644
--- a/utils/deprecated.js
+++ b/utils/deprecated.js
@@ -1,42 +1,53 @@
/**
* WordPress dependencies
*/
+import * as blob from '@wordpress/blob';
import * as dom from '@wordpress/dom';
import originalDeprecated from '@wordpress/deprecated';
-const wrapFunction = ( functionName, source = dom ) => ( ...args ) => {
- originalDeprecated( 'wp.utils.' + functionName, {
- version: '3.1',
- alternative: 'wp.dom.' + functionName,
- plugin: 'Gutenberg',
- } );
- return source[ functionName ]( ...args );
-};
+const wrapFunction = ( source, sourceName, version ) =>
+ ( functionName ) => ( ...args ) => {
+ originalDeprecated( `wp.utils.${ functionName }`, {
+ version,
+ alternative: `wp.${ sourceName }.${ functionName }`,
+ plugin: 'Gutenberg',
+ } );
+ return source[ functionName ]( ...args );
+ };
+
+// blob
+const wrapBlobFunction = wrapFunction( blob, 'blob', '3.2' );
+export const createBlobURL = wrapBlobFunction( 'createBlobURL' );
+export const getBlobByURL = wrapBlobFunction( 'getBlobByURL' );
+export const revokeBlobURL = wrapBlobFunction( 'revokeBlobURL' );
-export const computeCaretRect = wrapFunction( 'computeCaretRect' );
-export const documentHasSelection = wrapFunction( 'documentHasSelection' );
+// dom
+const wrapDomFunction = wrapFunction( dom, 'dom', '3.1' );
+export const computeCaretRect = wrapDomFunction( 'computeCaretRect' );
+export const documentHasSelection = wrapDomFunction( 'documentHasSelection' );
export const focus = {
focusable: {
- find: wrapFunction( 'find', dom.focus.focusable ),
+ find: wrapFunction( dom.focus.focusable, 'dom.focus.focusable', '3.1' )( 'find' ),
},
tabbable: {
- find: wrapFunction( 'find', dom.focus.tabbable ),
- isTabbableIndex: wrapFunction( 'isTabbableIndex', dom.focus.tabbable ),
+ find: wrapFunction( dom.focus.tabbable, 'dom.focus.tabbable', '3.1' )( 'find' ),
+ isTabbableIndex: wrapFunction( dom.focus.tabbable, 'dom.focus.tabbable', '3.1' )( 'isTabbableIndex' ),
},
};
-export const getRectangleFromRange = wrapFunction( 'getRectangleFromRange' );
-export const getScrollContainer = wrapFunction( 'getScrollContainer' );
-export const insertAfter = wrapFunction( 'insertAfter' );
-export const isHorizontalEdge = wrapFunction( 'isHorizontalEdge' );
-export const isTextField = wrapFunction( 'isTextField' );
-export const isVerticalEdge = wrapFunction( 'isVerticalEdge' );
-export const placeCaretAtHorizontalEdge = wrapFunction( 'placeCaretAtHorizontalEdge' );
-export const placeCaretAtVerticalEdge = wrapFunction( 'placeCaretAtVerticalEdge' );
-export const remove = wrapFunction( 'remove' );
-export const replace = wrapFunction( 'replace' );
-export const replaceTag = wrapFunction( 'replaceTag' );
-export const unwrap = wrapFunction( 'unwrap' );
+export const getRectangleFromRange = wrapDomFunction( 'getRectangleFromRange' );
+export const getScrollContainer = wrapDomFunction( 'getScrollContainer' );
+export const insertAfter = wrapDomFunction( 'insertAfter' );
+export const isHorizontalEdge = wrapDomFunction( 'isHorizontalEdge' );
+export const isTextField = wrapDomFunction( 'isTextField' );
+export const isVerticalEdge = wrapDomFunction( 'isVerticalEdge' );
+export const placeCaretAtHorizontalEdge = wrapDomFunction( 'placeCaretAtHorizontalEdge' );
+export const placeCaretAtVerticalEdge = wrapDomFunction( 'placeCaretAtVerticalEdge' );
+export const remove = wrapDomFunction( 'remove' );
+export const replace = wrapDomFunction( 'replace' );
+export const replaceTag = wrapDomFunction( 'replaceTag' );
+export const unwrap = wrapDomFunction( 'unwrap' );
+// deprecated
export function deprecated( ...params ) {
originalDeprecated( 'wp.utils.deprecated', {
version: '3.2',
@@ -47,6 +58,7 @@ export function deprecated( ...params ) {
return originalDeprecated( ...params );
}
+// viewport
export function isExtraSmall() {
originalDeprecated( 'wp.utils.isExtraSmall', {
version: '3.1',
diff --git a/utils/index.js b/utils/index.js
index 5a46909a6fbf6..bf89115c86dea 100644
--- a/utils/index.js
+++ b/utils/index.js
@@ -7,7 +7,6 @@ import { decodeEntities } from './entities';
export { decodeEntities };
export { keycodes };
-export * from './blob-cache';
export * from './mediaupload';
export * from './terms';
diff --git a/webpack.config.js b/webpack.config.js
index ec34191362963..3e4d766f0f1de 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -136,6 +136,7 @@ const entryPointNames = [
];
const gutenbergPackages = [
+ 'blob',
'date',
'deprecated',
'dom',