Make WordPress Core

Changeset 44114


Ignore:
Timestamp:
12/13/2018 04:49:58 PM (6 years ago)
Author:
desrosj
Message:

Script loader: Register @wordpress scripts.

This allows the packages to be consumed by plugins and core itself.
The code has been based on the work done in the Gutenberg plugin.

We've added an array with all the packages and the vendor packages to
loop through. This sets a convention so all packages will be
registered in the same way. This array can eventually be generated by
a webpack plugin.

We need to register TinyMCE explicitly. Previously TinyMCE was used
by inserting custom <script> tags into the relevant admin pages.
This is not suitable for the new editor, so we need to explicitly
register TinyMCE. We could, in the future, refactor the custom
<script> tags to use the registered TinyMCE script instead.

Polyfills are inserted into the page only when necessary using
document.write.

Props omarreiss, herregroen, youknowriad, gziolo, atimmer.

Merges [43723] to trunk.

Fixes #45065.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/wp-includes/script-loader.php

    r43571 r44114  
    3434/** WordPress Styles Functions */
    3535require( ABSPATH . WPINC . '/functions.wp-styles.php' );
     36
     37/**
     38 * Registers TinyMCE scripts.
     39 *
     40 * @since 5.0.0
     41 *
     42 * @param WP_Scripts $scripts WP_Scripts object.
     43 */
     44function wp_register_tinymce_scripts( &$scripts ) {
     45    global $tinymce_version, $concatenate_scripts, $compress_scripts;
     46    $suffix     = SCRIPT_DEBUG ? '' : '.min';
     47    $compressed = $compress_scripts && $concatenate_scripts && isset( $_SERVER['HTTP_ACCEPT_ENCODING'] )
     48                  && false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' );
     49    // Load tinymce.js when running from /src, otherwise load wp-tinymce.js.gz (in production) or
     50    // tinymce.min.js (when SCRIPT_DEBUG is true).
     51    $mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
     52    if ( $compressed ) {
     53        $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array(), $tinymce_version );
     54    } else {
     55        $scripts->add( 'wp-tinymce-root', includes_url( 'js/tinymce/' ) . "tinymce{$mce_suffix}.js", array(), $tinymce_version );
     56        $scripts->add( 'wp-tinymce', includes_url( 'js/tinymce/' ) . "plugins/compat3x/plugin{$suffix}.js", array( 'wp-tinymce-root' ), $tinymce_version );
     57    }
     58
     59    $scripts->add( 'wp-tinymce-lists', includes_url( 'js/tinymce/plugins/lists/index' . $suffix . '.js', array( 'wp-tinymce' ), $tinymce_version ) );
     60}
     61
     62/**
     63 * Registers all the WordPress vendor scripts that are in the standardized
     64 * `js/dist/vendor/` location.
     65 *
     66 * For the order of `$scripts->add` see `wp_default_scripts`.
     67 *
     68 * @since 5.0.0
     69 *
     70 * @param WP_Scripts $scripts WP_Scripts object.
     71 */
     72function wp_default_packages_vendor( &$scripts, $dev_suffix ) {
     73    wp_register_tinymce_scripts( $scripts );
     74
     75    $vendor_scripts = array(
     76        'react',
     77        'react-dom' => array( 'react' ),
     78        'moment',
     79        'lodash',
     80        'wp-polyfill-fetch',
     81        'wp-polyfill-formdata',
     82        'wp-polyfill-node-contains',
     83        'wp-polyfill-element-closest',
     84        'wp-polyfill-ecmascript',
     85    );
     86
     87    foreach ( $vendor_scripts as $handle => $dependencies ) {
     88        if ( is_string( $dependencies ) ) {
     89            $handle       = $dependencies;
     90            $dependencies = array();
     91        }
     92
     93        $path = '/js/dist/vendor/' . $handle . $dev_suffix . '.js';
     94
     95        $scripts->add( $handle, $path, $dependencies, false, 1 );
     96    }
     97
     98    $scripts->add( 'wp-polyfill', null, array( 'wp-polyfill-ecmascript' ) );
     99    did_action( 'init' ) && $scripts->add_inline_script(
     100        'wp-polyfill',
     101        'data',
     102        wp_get_script_polyfill(
     103            $scripts,
     104            array(
     105                '\'fetch\' in window' => 'wp-polyfill-fetch',
     106                'document.contains'   => 'wp-polyfill-node-contains',
     107                'window.FormData && window.FormData.prototype.keys' => 'wp-polyfill-formdata',
     108                'Element.prototype.matches && Element.prototype.closest' => 'wp-polyfill-element-closest',
     109            )
     110        )
     111    );
     112
     113    did_action( 'init' ) && $scripts->add_inline_script( 'lodash', 'window.lodash = _.noConflict();' );
     114}
     115
     116/**
     117 * Returns contents of an inline script used in appending polyfill scripts for
     118 * browsers which fail the provided tests. The provided array is a mapping from
     119 * a condition to verify feature support to its polyfill script handle.
     120 *
     121 * @since 5.0.0
     122 *
     123 * @param WP_Scripts $scripts WP_Scripts object.
     124 * @param array      $tests   Features to detect.
     125 * @return string Conditional polyfill inline script.
     126 */
     127function wp_get_script_polyfill( $scripts, $tests ) {
     128    $polyfill = '';
     129    foreach ( $tests as $test => $handle ) {
     130        if ( ! array_key_exists( $handle, $scripts->registered ) ) {
     131            continue;
     132        }
     133
     134        $polyfill .= (
     135            // Test presence of feature...
     136            '( ' . $test . ' ) || ' .
     137            // ...appending polyfill on any failures. Cautious viewers may balk
     138            // at the `document.write`. Its caveat of synchronous mid-stream
     139            // blocking write is exactly the behavior we need though.
     140            'document.write( \'<script src="' .
     141            esc_url( $scripts->registered[ $handle ]->src ) .
     142            '"></scr\' + \'ipt>\' );'
     143        );
     144    }
     145
     146    return $polyfill;
     147}
     148
     149/**
     150 * Register all the WordPress packages scripts that are in the standardized
     151 * `js/dist/` location.
     152 *
     153 * For the order of `$scripts->add` see `wp_default_scripts`.
     154 *
     155 * @since 5.0.0
     156 *
     157 * @param WP_Scripts $scripts WP_Scripts object.
     158 * @param string     $suffix  The suffix to use before `.js`.
     159 */
     160function wp_default_packages_scripts( &$scripts, $suffix ) {
     161    $packages_dependencies = array(
     162        'api-fetch'                          => array( 'wp-polyfill', 'wp-hooks', 'wp-i18n' ),
     163        'a11y'                               => array( 'wp-dom-ready', 'wp-polyfill' ),
     164        'autop'                              => array( 'wp-polyfill' ),
     165        'blob'                               => array( 'wp-polyfill' ),
     166        'blocks'                             => array(
     167            'wp-autop',
     168            'wp-blob',
     169            'wp-block-serialization-default-parser',
     170            'wp-data',
     171            'wp-deprecated',
     172            'wp-dom',
     173            'wp-element',
     174            'wp-hooks',
     175            'wp-i18n',
     176            'wp-is-shallow-equal',
     177            'wp-polyfill',
     178            'wp-shortcode',
     179            'lodash',
     180            'wp-rich-text',
     181        ),
     182        'block-library'                      => array(
     183            'editor',
     184            'lodash',
     185            'moment',
     186            'wp-api-fetch',
     187            'wp-autop',
     188            'wp-blob',
     189            'wp-blocks',
     190            'wp-components',
     191            'wp-compose',
     192            'wp-core-data',
     193            'wp-data',
     194            'wp-editor',
     195            'wp-element',
     196            'wp-html-entities',
     197            'wp-i18n',
     198            'wp-keycodes',
     199            'wp-polyfill',
     200            'wp-url',
     201            'wp-viewport',
     202            'wp-rich-text',
     203        ),
     204        'block-serialization-default-parser' => array(),
     205        'block-serialization-spec-parser'    => array( 'wp-polyfill' ),
     206        'components'                         => array(
     207            'lodash',
     208            'moment',
     209            'wp-a11y',
     210            'wp-api-fetch',
     211            'wp-compose',
     212            'wp-deprecated',
     213            'wp-dom',
     214            'wp-element',
     215            'wp-hooks',
     216            'wp-html-entities',
     217            'wp-i18n',
     218            'wp-is-shallow-equal',
     219            'wp-keycodes',
     220            'wp-polyfill',
     221            'wp-url',
     222            'wp-rich-text',
     223        ),
     224        'compose'                            => array( 'lodash', 'wp-element', 'wp-is-shallow-equal', 'wp-polyfill' ),
     225        'core-data'                          => array( 'wp-data', 'wp-api-fetch', 'wp-polyfill', 'wp-url', 'lodash' ),
     226        'data'                               => array(
     227            'lodash',
     228            'wp-compose',
     229            'wp-deprecated',
     230            'wp-element',
     231            'wp-is-shallow-equal',
     232            'wp-polyfill',
     233            'wp-redux-routine',
     234        ),
     235        'date'                               => array( 'moment', 'wp-polyfill' ),
     236        'deprecated'                         => array( 'wp-polyfill', 'wp-hooks' ),
     237        'dom'                                => array( 'lodash', 'wp-polyfill', 'wp-tinymce' ),
     238        'dom-ready'                          => array( 'wp-polyfill' ),
     239        'edit-post'                          => array(
     240            'jquery',
     241            'lodash',
     242            'postbox',
     243            'media-models',
     244            'media-views',
     245            'wp-a11y',
     246            'wp-api-fetch',
     247            'wp-block-library',
     248            'wp-blocks',
     249            'wp-components',
     250            'wp-compose',
     251            'wp-core-data',
     252            'wp-data',
     253            'wp-deprecated',
     254            'wp-dom-ready',
     255            'wp-editor',
     256            'wp-element',
     257            'wp-embed',
     258            'wp-i18n',
     259            'wp-keycodes',
     260            'wp-nux',
     261            'wp-plugins',
     262            'wp-polyfill',
     263            'wp-url',
     264            'wp-viewport',
     265        ),
     266        'editor'                             => array(
     267            'jquery',
     268            'lodash',
     269            'wp-tinymce-lists',
     270            'wp-a11y',
     271            'wp-api-fetch',
     272            'wp-blob',
     273            'wp-blocks',
     274            'wp-components',
     275            'wp-compose',
     276            'wp-core-data',
     277            'wp-data',
     278            'wp-date',
     279            'wp-deprecated',
     280            'wp-dom',
     281            'wp-element',
     282            'wp-hooks',
     283            'wp-html-entities',
     284            'wp-i18n',
     285            'wp-is-shallow-equal',
     286            'wp-keycodes',
     287            'wp-nux',
     288            'wp-polyfill',
     289            'wp-tinymce',
     290            'wp-token-list',
     291            'wp-url',
     292            'wp-viewport',
     293            'wp-wordcount',
     294            'wp-rich-text',
     295        ),
     296        'element'                            => array( 'wp-polyfill', 'react', 'react-dom', 'lodash', 'wp-escape-html' ),
     297        'escape-html'                        => array( 'wp-polyfill' ),
     298        'hooks'                              => array( 'wp-polyfill' ),
     299        'html-entities'                      => array( 'wp-polyfill' ),
     300        'i18n'                               => array( 'wp-polyfill' ),
     301        'is-shallow-equal'                   => array( 'wp-polyfill' ),
     302        'keycodes'                           => array( 'lodash', 'wp-polyfill' ),
     303        'list-reusable-blocks'               => array(
     304            'lodash',
     305            'wp-api-fetch',
     306            'wp-components',
     307            'wp-compose',
     308            'wp-element',
     309            'wp-i18n',
     310            'wp-polyfill',
     311        ),
     312        'nux'                                => array(
     313            'wp-element',
     314            'wp-components',
     315            'wp-compose',
     316            'wp-data',
     317            'wp-i18n',
     318            'wp-polyfill',
     319            'lodash',
     320        ),
     321        'plugins'                            => array( 'lodash', 'wp-compose', 'wp-element', 'wp-hooks', 'wp-polyfill' ),
     322        'redux-routine'                      => array( 'wp-polyfill' ),
     323        'rich-text'                          => array( 'wp-polyfill', 'wp-escape-html', 'lodash' ),
     324        'shortcode'                          => array( 'wp-polyfill', 'lodash' ),
     325        'token-list'                         => array( 'lodash', 'wp-polyfill' ),
     326        'url'                                => array( 'wp-polyfill' ),
     327        'viewport'                           => array( 'wp-polyfill', 'wp-element', 'wp-data', 'wp-compose', 'lodash' ),
     328        'wordcount'                          => array( 'wp-polyfill' ),
     329    );
     330
     331    foreach ( $packages_dependencies as $package => $dependencies ) {
     332        $handle = 'wp-' . $package;
     333        $path   = '/js/dist/' . $package . $suffix . '.js';
     334
     335        $scripts->add( $handle, $path, $dependencies, false, 1 );
     336    }
     337}
     338
     339/**
     340 * Adds inline scripts required for the WordPress JavaScript packages.
     341 *
     342 * @since 5.0.0
     343 *
     344 * @param WP_Scripts $scripts WP_Scripts object.
     345 */
     346function wp_default_packages_inline_scripts( &$scripts ) {
     347    global $wp_locale;
     348
     349    $scripts->add_inline_script(
     350        'wp-api-fetch',
     351        sprintf(
     352            'wp.apiFetch.use( wp.apiFetch.createNonceMiddleware( "%s" ) );',
     353            ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' )
     354        ),
     355        'after'
     356    );
     357    $scripts->add_inline_script(
     358        'wp-api-fetch',
     359        sprintf(
     360            'wp.apiFetch.use( wp.apiFetch.createRootURLMiddleware( "%s" ) );',
     361            esc_url_raw( get_rest_url() )
     362        ),
     363        'after'
     364    );
     365
     366    $scripts->add_inline_script(
     367        'wp-data',
     368        implode(
     369            "\n",
     370            array(
     371                '( function() {',
     372                '   var userId = ' . get_current_user_ID() . ';',
     373                '   var storageKey = "WP_DATA_USER_" + userId;',
     374                '   wp.data',
     375                '       .use( wp.data.plugins.persistence, { storageKey: storageKey } )',
     376                '       .use( wp.data.plugins.asyncGenerator )',
     377                '       .use( wp.data.plugins.controls );',
     378                '} )()',
     379            )
     380        )
     381    );
     382
     383    $scripts->add_inline_script(
     384        'wp-date',
     385        sprintf(
     386            'wp.date.setSettings( %s );',
     387            wp_json_encode(
     388                array(
     389                    'l10n'     => array(
     390                        'locale'        => get_user_locale(),
     391                        'months'        => array_values( $wp_locale->month ),
     392                        'monthsShort'   => array_values( $wp_locale->month_abbrev ),
     393                        'weekdays'      => array_values( $wp_locale->weekday ),
     394                        'weekdaysShort' => array_values( $wp_locale->weekday_abbrev ),
     395                        'meridiem'      => (object) $wp_locale->meridiem,
     396                        'relative'      => array(
     397                            /* translators: %s: duration */
     398                            'future' => __( '%s from now', 'default' ),
     399                            /* translators: %s: duration */
     400                            'past'   => __( '%s ago', 'default' ),
     401                        ),
     402                    ),
     403                    'formats'  => array(
     404                        'time'     => get_option( 'time_format', __( 'g:i a', 'default' ) ),
     405                        'date'     => get_option( 'date_format', __( 'F j, Y', 'default' ) ),
     406                        'datetime' => __( 'F j, Y g:i a', 'default' ),
     407                    ),
     408                    'timezone' => array(
     409                        'offset' => get_option( 'gmt_offset', 0 ),
     410                        'string' => get_option( 'timezone_string', 'UTC' ),
     411                    ),
     412                )
     413            )
     414        ),
     415        'after'
     416    );
     417
     418    // Loading the old editor and its config to ensure the classic block works as expected.
     419    $scripts->add_inline_script(
     420        'editor',
     421        'window.wp.oldEditor = window.wp.editor;',
     422        'after'
     423    );
     424
     425    $tinymce_settings = apply_filters(
     426        'tiny_mce_before_init',
     427        array(
     428            'plugins'          => implode(
     429                ',',
     430                array_unique(
     431                    apply_filters(
     432                        'tiny_mce_plugins',
     433                        array(
     434                            'charmap',
     435                            'colorpicker',
     436                            'hr',
     437                            'lists',
     438                            'media',
     439                            'paste',
     440                            'tabfocus',
     441                            'textcolor',
     442                            'fullscreen',
     443                            'wordpress',
     444                            'wpautoresize',
     445                            'wpeditimage',
     446                            'wpemoji',
     447                            'wpgallery',
     448                            'wplink',
     449                            'wpdialogs',
     450                            'wptextpattern',
     451                            'wpview',
     452                        )
     453                    )
     454                )
     455            ),
     456            'toolbar1'         => implode(
     457                ',',
     458                array_merge(
     459                    apply_filters(
     460                        'mce_buttons',
     461                        array(
     462                            'formatselect',
     463                            'bold',
     464                            'italic',
     465                            'bullist',
     466                            'numlist',
     467                            'blockquote',
     468                            'alignleft',
     469                            'aligncenter',
     470                            'alignright',
     471                            'link',
     472                            'unlink',
     473                            'wp_more',
     474                            'spellchecker',
     475                            'wp_add_media',
     476                        ),
     477                        'editor'
     478                    ),
     479                    array( 'kitchensink' )
     480                )
     481            ),
     482            'toolbar2'         => implode(
     483                ',',
     484                apply_filters(
     485                    'mce_buttons_2',
     486                    array(
     487                        'strikethrough',
     488                        'hr',
     489                        'forecolor',
     490                        'pastetext',
     491                        'removeformat',
     492                        'charmap',
     493                        'outdent',
     494                        'indent',
     495                        'undo',
     496                        'redo',
     497                        'wp_help',
     498                    ),
     499                    'editor'
     500                )
     501            ),
     502            'toolbar3'         => implode( ',', apply_filters( 'mce_buttons_3', array(), 'editor' ) ),
     503            'toolbar4'         => implode( ',', apply_filters( 'mce_buttons_4', array(), 'editor' ) ),
     504            'external_plugins' => apply_filters( 'mce_external_plugins', array() ),
     505        ),
     506        'editor'
     507    );
     508
     509    if ( isset( $tinymce_settings['style_formats'] ) && is_string( $tinymce_settings['style_formats'] ) ) {
     510        // Decode the options as we used to recommende json_encoding the TinyMCE settings.
     511        $tinymce_settings['style_formats'] = json_decode( $tinymce_settings['style_formats'] );
     512    }
     513
     514    $scripts->localize(
     515        'wp-block-library',
     516        'wpEditorL10n',
     517        array(
     518            'tinymce' => array(
     519                'baseURL'  => includes_url( 'js/tinymce' ),
     520                'suffix'   => SCRIPT_DEBUG ? '' : '.min',
     521                'settings' => $tinymce_settings,
     522            ),
     523        )
     524    );
     525}
    36526
    37527/**
     
    7211211    $scripts->add( 'wp-api', "/wp-includes/js/wp-api$suffix.js", array( 'jquery', 'backbone', 'underscore', 'wp-api-request' ), false, 1 );
    7221212
     1213    wp_default_packages_vendor( $scripts, $dev_suffix );
     1214    wp_default_packages_scripts( $scripts, $suffix );
     1215    if ( did_action( 'init' ) ) {
     1216        wp_default_packages_inline_scripts( $scripts );
     1217    }
     1218
    7231219    if ( is_admin() ) {
    7241220        $scripts->add( 'admin-tags', "/wp-admin/js/tags$suffix.js", array( 'jquery', 'wp-ajax-response' ), false, 1 );
     
    12381734        }
    12391735    }
     1736
     1737    // Packages styles
     1738    $fonts_url = '';
     1739
     1740    /*
     1741     * Translators: If there are characters in your language that are not supported
     1742     * by Noto Serif, translate this to 'off'. Do not translate into your own language.
     1743     */
     1744    if ( 'off' !== _x( 'on', 'Noto Serif font: on or off' ) ) {
     1745        $fonts_url = 'https://fonts.googleapis.com/css?family=Noto+Serif%3A400%2C400i%2C700%2C700i';
     1746    }
     1747    $styles->add( 'wp-editor-font', $fonts_url );
     1748
     1749    $styles->add( 'wp-block-library-theme', '/styles/dist/block-library/theme.css' );
     1750    $styles->add_data( 'wp-block-library-theme', 'rtl', 'replace' );
     1751
     1752    $styles->add(
     1753        'wp-edit-blocks',
     1754        '/styles/dist/block-library/editor.css',
     1755        array(
     1756            'wp-components',
     1757            'wp-editor',
     1758            // Always include visual styles so the editor never appears broken.
     1759            'wp-block-library-theme',
     1760        )
     1761    );
     1762    $styles->add_data( 'wp-edit-blocks', 'rtl', 'replace' );
     1763
     1764    $package_styles = array(
     1765        'block-library'        => array(),
     1766        'components'           => array(),
     1767        'edit-post'            => array( 'wp-components', 'wp-editor', 'wp-edit-blocks', 'wp-block-library', 'wp-nux' ),
     1768        'editor'               => array( 'wp-components', 'wp-editor-font', 'wp-nux' ),
     1769        'list-reusable-blocks' => array( 'wp-components' ),
     1770        'nux'                  => array( 'wp-components' ),
     1771    );
     1772
     1773    foreach ( $package_styles as $package => $dependencies ) {
     1774        $handle = 'wp-' . $package;
     1775        $path   = '/styles/dist/' . $package . '/style.css';
     1776
     1777        $styles->add( $handle, $path, $dependencies );
     1778        $styles->add_data( $handle, 'rtl', 'replace' );
     1779    }
    12401780}
    12411781
  • trunk/tools/webpack/packages.js

    r43719 r44114  
    9292    const vendors = {
    9393        'lodash.js': 'lodash/lodash.js',
    94         'wp-polyfill.js': '@babel/polyfill/dist/polyfill.js',
     94        'wp-polyfill-ecmascript.js': '@babel/polyfill/dist/polyfill.js',
    9595        'wp-polyfill-fetch.js': 'whatwg-fetch/dist/fetch.umd.js',
    9696        'wp-polyfill-element-closest.js': 'element-closest/element-closest.js',
     
    104104    const minifiedVendors = {
    105105        'lodash.min.js': 'lodash/lodash.min.js',
    106         'wp-polyfill.min.js': '@babel/polyfill/dist/polyfill.min.js',
     106        'wp-polyfill-ecmascript.min.js': '@babel/polyfill/dist/polyfill.min.js',
    107107        'wp-polyfill-formdata.min.js': 'formdata-polyfill/formdata.min.js',
    108108        'moment.min.js': 'moment/min/moment.min.js',
Note: See TracChangeset for help on using the changeset viewer.