Skip to content

Commit

Permalink
Add warning package (#19317)
Browse files Browse the repository at this point in the history
* Add new warning package

* Add babel-plugin-dev-expression

* Remove babel-plugin-dev-expression

* Add babel-plugin to the warning pacakge

* Include @wordpress/warning/babel-plugin into @wordpress/babel-preset-default

* Add warning to webpack.config.js

* Check the presence of process.env

* Check presence of process.env in the code transformed by the babel plugin

* Move babel-plugin.js and its test file to the directory top-level

* Improve README with information about dead code removal

* Turn sideEffects into true in package.json

* Revert sideEffects change

* npm run docs:build

* Update packages/warning/babel-plugin.js

Co-Authored-By: Grzegorz (Greg) Ziółkowski <[email protected]>

* warning: Add types checking for warning package

* Avoid indentation on the warning function with an early return

* Fix TS error on babel-plugin.js

* Accept a single string as message instead of multiple messages

Co-authored-by: Grzegorz (Greg) Ziółkowski <[email protected]>
Co-authored-by: Andrew Duthie <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2020
1 parent b5c9419 commit 8175deb
Show file tree
Hide file tree
Showing 16 changed files with 366 additions and 1 deletion.
1 change: 1 addition & 0 deletions bin/api-docs/packages.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const packages = [
'shortcode',
'url',
'viewport',
'warning',
'wordcount',
];

Expand Down
6 changes: 6 additions & 0 deletions docs/manifest-devhub.json
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,12 @@
"markdown_source": "../packages/viewport/README.md",
"parent": "packages"
},
{
"title": "@wordpress/warning",
"slug": "packages-warning",
"markdown_source": "../packages/warning/README.md",
"parent": "packages"
},
{
"title": "@wordpress/wordcount",
"slug": "packages-wordcount",
Expand Down
4 changes: 4 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"@wordpress/token-list": "file:packages/token-list",
"@wordpress/url": "file:packages/url",
"@wordpress/viewport": "file:packages/viewport",
"@wordpress/warning": "file:packages/warning",
"@wordpress/wordcount": "file:packages/wordcount"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/babel-preset-default/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module.exports = function( api ) {
presets: [ getPresetEnv() ],
plugins: [
require.resolve( '@babel/plugin-proposal-object-rest-spread' ),
require.resolve( '@wordpress/warning/babel-plugin' ),
[
require.resolve( '@wordpress/babel-plugin-import-jsx-pragma' ),
{
Expand Down
1 change: 1 addition & 0 deletions packages/babel-preset-default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@wordpress/babel-plugin-import-jsx-pragma": "file:../babel-plugin-import-jsx-pragma",
"@wordpress/browserslist-config": "file:../browserslist-config",
"@wordpress/element": "file:../element",
"@wordpress/warning": "file:../warning",
"core-js": "^3.1.4"
},
"publishConfig": {
Expand Down
1 change: 1 addition & 0 deletions packages/warning/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
Empty file added packages/warning/CHANGELOG.md
Empty file.
56 changes: 56 additions & 0 deletions packages/warning/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Warning

Utility for warning messages to the console based on a passed condition.

## Installation

Install the module

```bash
npm install @wordpress/warning --save
```

_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._

## Reducing bundle size

Literal strings aren't minified. Keeping them in your production bundle may increase the bundle size significantly.

To prevent that, you should:

1. Put `@wordpress/warning/babel-plugin` into your [babel config](https://babeljs.io/docs/en/plugins#plugin-options) or use [`@wordpress/babel-preset-default`](https://www.npmjs.com/package/@wordpress/babel-preset-default), which already includes the babel plugin.

This will make sure your `warning` calls are wrapped within a condition that checks if `process.env.NODE_ENV !== 'production'`.

2. Use [UglifyJS](https://github.com/mishoo/UglifyJS2), [Terser](https://github.com/terser/terser) or any other JavaScript parser that performs [dead code elimination](https://en.wikipedia.org/wiki/Dead_code_elimination). This is usually used in conjunction with JavaScript bundlers, such as [webpack](https://github.com/webpack/webpack).

When parsing the code in `production` mode, the `warning` call will be removed altogether.

## API

<!-- START TOKEN(Autogenerated API docs) -->

<a name="default" href="#default">#</a> **default**

Shows a warning with `message` if `condition` passes and environment is not `production`.

_Usage_

```js
import warning from '@wordpress/warning';

function MyComponent( props ) {
warning( ! props.title, '`props.title` was not passed' );
...
}
```

_Parameters_

- _condition_ `boolean`: Whether the warning will be triggered or not.
- _message_ `string`: Message to show in the warning.


<!-- END TOKEN(Autogenerated API docs) -->

<br/><br/><p align="center"><img src="https://s.w.org/style/images/codeispoetry.png?1" alt="Code is Poetry." /></p>
88 changes: 88 additions & 0 deletions packages/warning/babel-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Internal dependencies
*/
const pkg = require( './package.json' );

/**
* Babel plugin which transforms `warning` function calls to wrap within a
* condition that checks if `process.env.NODE_ENV !== 'production'`.
*
* @param {import('@babel/core')} babel Current Babel object.
*
* @return {import('@babel/core').PluginObj} Babel plugin object.
*/
function babelPlugin( { types: t } ) {
const seen = Symbol();

const typeofProcessExpression = t.binaryExpression(
'!==',
t.unaryExpression( 'typeof', t.identifier( 'process' ), false ),
t.stringLiteral( 'undefined' )
);

const processEnvExpression = t.memberExpression(
t.identifier( 'process' ),
t.identifier( 'env' ),
false
);

const nodeEnvCheckExpression = t.binaryExpression(
'!==',
t.memberExpression( processEnvExpression, t.identifier( 'NODE_ENV' ), false ),
t.stringLiteral( 'production' )
);

const logicalExpression = t.logicalExpression(
'&&',
t.logicalExpression( '&&', typeofProcessExpression, processEnvExpression ),
nodeEnvCheckExpression
);

return {
visitor: {
ImportDeclaration( path, state ) {
const { node } = path;
const isThisPackageImport = node.source.value.indexOf( pkg.name ) !== -1;

if ( ! isThisPackageImport ) {
return;
}

const defaultSpecifier = node.specifiers.find(
( specifier ) => specifier.type === 'ImportDefaultSpecifier'
);

if ( defaultSpecifier && defaultSpecifier.local ) {
const { name } = defaultSpecifier.local;
state.callee = name;
}
},
CallExpression( path, state ) {
const { node } = path;

// Ignore if it's already been processed
if ( node[ seen ] ) {
return;
}

const name = state.callee || state.opts.callee;

if ( path.get( 'callee' ).isIdentifier( { name } ) ) {
// Turns this code:
// warning(condition, argument, argument);
// into this:
// typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" ? warning(condition, argument, argument) : void 0;
node[ seen ] = true;
path.replaceWith(
t.ifStatement(
logicalExpression,
t.blockStatement( [ t.expressionStatement( node ) ] )
)
);
}
},
},
};
}

module.exports = babelPlugin;
27 changes: 27 additions & 0 deletions packages/warning/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@wordpress/warning",
"version": "0.0.1",
"description": "Warning utility for WordPress.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"keywords": [
"wordpress",
"warning"
],
"homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/warning/README.md",
"repository": {
"type": "git",
"url": "https://github.com/WordPress/gutenberg.git",
"directory": "packages/warning"
},
"bugs": {
"url": "https://github.com/WordPress/gutenberg/issues"
},
"main": "build/index.js",
"module": "build-module/index.js",
"react-native": "src/index",
"sideEffects": false,
"publishConfig": {
"access": "public"
}
}
45 changes: 45 additions & 0 deletions packages/warning/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
function isDev() {
return (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV !== 'production'
);
}

/**
* Shows a warning with `message` if `condition` passes and environment is not `production`.
*
* @param {boolean} condition Whether the warning will be triggered or not.
* @param {string} message Message to show in the warning.
*
* @example
* ```js
* import warning from '@wordpress/warning';
*
* function MyComponent( props ) {
* warning( ! props.title, '`props.title` was not passed' );
* ...
* }
* ```
*/
export default function warning( condition, message ) {
if ( ! isDev() ) {
return;
}

if ( ! condition ) {
return;
}

// eslint-disable-next-line no-console
console.warn( message );

// Throwing an error and catching it immediately to improve debugging
// A consumer can use 'pause on caught exceptions'
// https://github.com/facebook/react/issues/4216
try {
throw Error( message );
} catch ( x ) {
// do nothing
}
}
30 changes: 30 additions & 0 deletions packages/warning/src/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import warning from '..';

const initialNodeEnv = process.env.NODE_ENV;

describe( 'warning', () => {
afterEach( () => {
process.env.NODE_ENV = initialNodeEnv;
} );

it( 'logs to console.warn when NODE_ENV is not "production"', () => {
process.env.NODE_ENV = 'development';
warning( true, 'warning' );
expect( console ).toHaveWarnedWith( 'warning' );
} );

it( 'does not log to console.warn if NODE_ENV is "production"', () => {
process.env.NODE_ENV = 'production';
warning( true, 'warning' );
expect( console ).not.toHaveWarned();
} );

it( 'does not log to console.warn if condition is falsy', () => {
process.env.NODE_ENV = 'development';
warning( false, 'warning' );
expect( console ).not.toHaveWarned();
} );
} );
Loading

0 comments on commit 8175deb

Please sign in to comment.