Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@wordpress/scripts - webpack runtime issue: blocks importing code from a local package, when compiled with npm run build, do not load in editor #23607

Closed
leoloso opened this issue Jul 1, 2020 · 25 comments · Fixed by #27985
Labels
[Tool] WP Scripts /packages/scripts [Type] Bug An existing feature does not function as intended

Comments

@leoloso
Copy link

leoloso commented Jul 1, 2020

Describe the bug
I am scaffolding a single-script plugin (not a single-block plugin) as documented here:

  1. Create a single-block plugin using @wordpress/create-block
  2. Modify the code to register and enqueue a script, instead of a block:
  $index_js     = 'build/index.js';
  $script_asset = require( $script_asset_path );
  wp_register_script(
    'my-script',
    plugins_url( $index_js, __FILE__ ),
    $script_asset['dependencies'],
    $script_asset['version']
  );
  wp_enqueue_script(
    'my-script'
  );

index.asset.php contains the following dependencies:

<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives'), 'version' => '971e8417567c9d3ce3c96515622562c3');

Please notice that wp_register_script is inserting the script in the header, not in the footer. Then, an anomaly happens: the code below is also added on the header, and not on the footer (where it is normally added):

<script>
( function() {
	window._wpLoadBlockEditor = new Promise( function( resolve ) {
		wp.domReady( function() {
			resolve( wp.editPost.initializeEditor( 'editor', "graphql-query", 143, {"alignWide":true,"availableTemplates":[],"allowedBlockTypes":["graphql-api\/graphiql","graphql-api\/schema-configuration","graphql-api\/persisted-query-options"],...}
		} );
	} );
} )();
</script>

I guess this happens from one of the dependencies from the script triggering that code to be added right then, which is the header.

Now, there is a weird thing happening: if the blocks registered for that page have been compiled using npm start, then it works fine. However, if they were compiled using npm run build, then the script fails, the screen is all white, and the console shows this error:

Uncaught (in promise) TypeError: Cannot read property 'attributes' of undefined
    at Ue (index.js?ver=a7f027f7a7f6565d71c3f8ba71ca024b:2)
    at index.js?ver=a7f027f7a7f6565d71c3f8ba71ca024b:2
    at c (lodash.min.js?ver=4.17.15:6)
    at ru (lodash.min.js?ver=4.17.15:67)
    at ta (index.js?ver=a7f027f7a7f6565d71c3f8ba71ca024b:2)
    at index.js?ver=3fcda6127c1ee7664a2ad523505e984c:11
    at tryCatch (wp-polyfill.min.js?ver=7.4.4:1)
    at Generator.invoke [as _invoke] (wp-polyfill.min.js?ver=7.4.4:1)
    at Generator.t.<computed> [as next] (wp-polyfill.min.js?ver=7.4.4:1)
    at index.js?ver=e914d7e3651bead726dfe4f55dc18bfa:1

Please see the screenshot below from the debugging, on the line where the error happens. As can be observed:

  • n is undefined, so n.attributes explodes
  • Block "graphql-api/schema-configuration" was compiled using npm run build
  • window.wp.blocks.getBlockTypes() doesn't contain block "graphql-api/schema-configuration" among them!

Hence, the last item is the problem: the block was not added to the list of types, and so doing select( 'core/blocks' ).getBlockType( "graphql-api/schema-configuration" ) returns null, and then the script fails.

If the script is registered in the footer, then it works fine (with both npm start and npm run build).

Then, at least 1 of the following must be fixed:

  • If the script must be placed in the footer, then this must be clear somewhere in the documentation, and taken into account when tackling Create a @wordpress/create-project package to scaffold scripts and styles #23514
  • If it doesn't matter, find out why the block compiled with npm run build is not being added to window.wp.blocks.getBlockTypes()
  • Also, inspect why the behavior when doing npm start and npm run build is different

To reproduce
Steps to reproduce the behavior:

  1. Register a single-script plugin, placing it on the header
  2. Register a block, compiling it with npm run build from the @wordpress/scripts package
  3. Load Gutenberg

Expected behavior
Either the error should not happen, or the documentation must mention that registering a script with certain dependencies can only be done in the footer

Screenshots

Screenshot 2020-07-01 at 6 02 53 PM

Editor version (please complete the following information):

  • WordPress version: 5.4
  • Does the website has Gutenberg plugin installed, or is it using the block editor that comes by default? I tried with and without Gutenberg, it fails with both
  • If the Gutenberg plugin is installed, which version is it? 8.4
  • @wordpress/scripts v12.0.0, but I believe it fails since v10.0.0

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: Firefox/Chrome
@leoloso leoloso changed the title @wordpress/scripts: loading the editor fails when a block was compiled using npm run build does not, and when including a regular script on the header @wordpress/scripts: loading the editor fails when a block was compiled using npm run build, and including a regular script on the header Jul 1, 2020
@leoloso
Copy link
Author

leoloso commented Jul 1, 2020

This same problem happens when registering blocks too, when a block is referencing a package from the same plugin.

My plugin has this layout:

- blocks/
+    block1/
+    block2/
- packages/
+   components/

The package under packages/components is called @graphqlapi/components. It contains shared code to be used by blocks block1 and block2. Its name defined in its package.json is:

{
  "name": "@graphqlapi/components",
}

To reference this package, the blocks have this internal dependency in their package.json:

{
  "devDependencies": {
    "@graphqlapi/components": "file:../../packages/components",
  }
}

When any of the blocks imports a component from this package, like this:

import { getEditableOnFocusComponentClass } from '@graphqlapi/components';

And it is compiled using npm run build, it compiles well, generates build/index.js, but in the browser it produces a JS exception:

index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:38 Uncaught TypeError: Cannot read property 'call' of undefined
    at s (index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:38)
    at Module.<anonymous> (index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:1817)
    at s (index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:38)
    at n (index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:21)
    at index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:106
    at index.js?ver=8623106e228cb892f3ce8cbeb1789e98:formatted:107

from line return e[t].call(n.exports, n, n.exports, s) here (I can't provide the original source, because with npm start the problem doesn't happen):

function s(t) {
  if (r[t])
      return r[t].exports;
  var n = r[t] = {
      i: t,
      l: !1,
      exports: {}
  };
  return e[t].call(n.exports, n, n.exports, s),
  n.l = !0,
  n.exports
}

Because e[t] is undefined. Same as before, it happens because the block is not added to the types. Doing this in the console:

wp.blocks.getBlockTypes()

returns the list without that block!

@gziolo gziolo added [Tool] WP Scripts /packages/scripts Needs Testing Needs further testing to be confirmed. labels Jul 3, 2020
@leoloso leoloso changed the title @wordpress/scripts: loading the editor fails when a block was compiled using npm run build, and including a regular script on the header @wordpress/scripts: blocks compiled using npm run build do not appear on wp.blocks.getBlockTypes() Jul 6, 2020
@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

I found out a hack that solves the issue.

For some obscure reason, running npm run build fails when the block references a package in the same plugin, using an alias, as I explained in my previous comment.

But if we add this piece of code, on any .js file in the block, then it works well!

const fixIncrediblyWeirdBug = {
  ...{},
}

The issue seems to be related to the spread operator ... and to possibly webpack, or babel, or a combination of them. When compiling this code, it will change many array indexes in the output in build/index.js, and add this code:

function(e, t) {
  e.exports = function(e, t, n) {
    return t in e ? Object.defineProperty(e, t, {
      value: n,
      enumerable: !0,
      configurable: !0,
      writable: !0
    }) : e[t] = n, e
  }
}

I wonder if that e[t] in this added code, is related to the e[t] mentioned in the error in the browser console:

Uncaught TypeError: e[t] is undefined

Screenshot 2020-07-06 at 7 37 09 PM

@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

I created gists with the working and non-working build/index.js files, generated after running npm run build:

Non-working: https://gist.github.com/leoloso/98c1673018dbdacfefd835ebbde70bc9

Working: https://gist.github.com/leoloso/f6de95659fc2c92b0fdedf42d1aa3087

@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

The output from npm run build shows that there is an extra module built, in the case when it works.

I executed wp-scripts build using the --display-modules flag to see the list of all modules, and the extra one is this one:

.../blocks/my-block/node_modules/@babel/runtime/helpers/defineProperty.js 289 bytes {0} [built]

Please notice that this dependency is under the block.

This same module is also built, but belonging to the referenced package, for both when it works and when it fails:

.../packages/my-package/node_modules/@babel/runtime/helpers/defineProperty.js 289 bytes {0} [built]

So maybe the problem is related to Babel?

@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

Alright, this is just crazy.

In another block, it happens the opposite situation! The block already has a spread operator:

return {
  ...state,
  schemaConfigurations: action.schemaConfigurations,
  hasRetrievedSchemaConfigurations: true,
  retrievingSchemaConfigurationsErrorMessage: action.errorMessage,
};

In this case, the block compiled with npm run build doesn't work!

It works after removing the spread operator:

return Object.assign( {}, state, {
  schemaConfigurations: action.schemaConfigurations,
  hasRetrievedSchemaConfigurations: true,
  retrievingSchemaConfigurationsErrorMessage: action.errorMessage,
} );

@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

If I have to guess, the problem is related to library babel-plugin-transform-object-rest-spread.

Currently it is not part of Gutenberg. I wonder if it is the solution to make the spread operator work consistently well.

@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

I have one block that also doesn't work, and the reason is connected to passing props to a React component through the spread operator:

<SchemaConfigOptionsCard
  isPublicPrivateSchemaEnabled={ isPublicPrivateSchemaEnabled }
  isSchemaNamespacingEnabled={ isSchemaNamespacingEnabled }
  { ...props }
/>

Yes, having that { ...props } makes the block not work well with npm run build.

For this issue, I see no solution (I can't pass all the properties, one by one). So until solved, I must compile this block using:

wp-scripts start --webpack--devtool=

@leoloso leoloso changed the title @wordpress/scripts: blocks compiled using npm run build do not appear on wp.blocks.getBlockTypes() @wordpress/scripts - issue with spread operator and Babel: blocks compiled with npm run build do not load in editor, JS error thrown Jul 6, 2020
@leoloso
Copy link
Author

leoloso commented Jul 6, 2020

To summarize, the error is this one:

  • Blocks built with npm run build compile well, but when loaded in the editor it throws a JS error: Uncaught TypeError: e[t] is undefined

The environment for the error is:

  • A multi-block plugin contains blocks referencing shared code from packages in the same plugin, using an alias, resolved to a local folder.

For instance, some block block-name, located under blocks/, imports code from package-name, located under packages/, like this: import { SomeModule } from '@my-domain/package-name'; , and in its package.json it resolves the package as: "devDependencies": { "@my-domain/package-name": "file:../../packages/package-name" }

The error happens with either of these situations:

  1. When the block contains no spread operator
    In this case, adding const fixIncrediblyWeirdBug = { ...{} } anywhere in the block solves the issue
  2. When the block contains a spread operator for an object ({ ...object })
    In this case, replacing the spread operator with Object.assign solves the issue
  3. When passing props to a React component (<Component { ...props } />)
    I have no solution for this case

In 1., adding the fixIncrediblyWeirdBug hack adds this code below to build/index.js as coming from the block, and this code is also added by the referenced package, then it contains this code twice:

function(e, t) {
  e.exports = function(e, t, n) {
    return t in e ? Object.defineProperty(e, t, {
      value: n,
      enumerable: !0,
      configurable: !0,
      writable: !0
    }) : e[t] = n, e
  }
}

Please notice that this code deals with e[t], as the thrown JS error does.

In 2., build/index.js already has that code for the block, using the Object.assign removes it, and only that code from the referenced package remains, then this code appears only once.

Hence, it seems that the issue is not about code being in the output or not, but when/where it is added.

In addition to this code, the working/non-working build/index.js output has many array indexes modified, and it's when accessing one of these indexes that the error is thrown, because at that position the array is undefined.

From what I've read in other repos (eg: here, here, here), babel-plugin-transform-object-rest-spread should fix the problem.

@ocean90
Copy link
Member

ocean90 commented Jul 8, 2020

Can you share a link to the plugin to reproduce the issue?

Are you defining "sideEffects": false in your package.json? Does setting a custom output.jsonpFunction change anything?

@leoloso
Copy link
Author

leoloso commented Jul 9, 2020

Are you defining "sideEffects": false in your package.json

I tried just now. I don't know if it fixed the error, but for sure it generated others. Now, the console shows two errors:

An error occurred while running 'mapSelect': t is null index.js:1:26975

and:

TypeError: u is undefined

One block doesn't work at all, and another one lost its styles:

Screenshot 2020-07-09 at 8 08 54 AM

I also added config.optimization.sideEffects => false in a custom webpack.config.js (without sideEffects => false in package.json), and it didn't fix the issue.

Does setting a custom output.jsonpFunction change anything?

I just tried this:

module.exports = {
  //...
  output: {
    jsonpFunction: 'wpJsonpFlightsWidget'
  }
};

I've got mixed results from it.

On one side, it seems to fix the issue when registering the script for the Document TabPanel, which was the issue in the first comment.

But on the other, for the blocks importing code from a package within the same plugin, now 3 blocks are failing, and it shows some other errors on the console:

An error occurred while running 'mapSelect': t is null 3 index.js:1:26975

and, for each failing block:

TypeError: t is undefined

Screenshot 2020-07-09 at 8 35 43 AM

Can you share a link to the plugin to reproduce the issue?

My repo is private, but I can invite you to it. Are you OK with that?

Otherwise, I plan to make the repo public in around 2 months (once the plugin in it is ready).

@leoloso
Copy link
Author

leoloso commented Jul 9, 2020

I've got mixed results from it.

Actually, results from using config.output.jsonpFunction = 'wpJsonpFlightsWidget' are not consistent. It seemed to fix the issue with one script, but another one now it's completely messed up:

Screenshot 2020-07-09 at 8 50 17 AM

But on the other, for the blocks importing code from a package within the same plugin, now 3 blocks are failing

I just discovered something. I had added config.output.jsonpFunction = 'wpJsonpFlightsWidget' to all 4 blocks in my CPT, but only 1 had the problem.

Actually, those new JS errors belong to the other 3 blocks. I removed again that code for those 3 and kept it for the failing block only, and now they all work!

In summary, doing config.output.jsonpFunction = 'wpJsonpFlightsWidget' seems to:

  • Sometimes solve the issue, sometimes not, for the case with the regular script (not block)
  • Fix the issue when the block imports modules from a local package

@ocean90
Copy link
Member

ocean90 commented Jul 9, 2020

Are you defining "sideEffects": false in your package.json

I tried just now. I don't know if it fixed the error, but for sure it generated others. Now, the console shows two errors:

Sorry, I wasn't clear here. It's better to not have this setting, at least not until you know what it does.

If config.output.jsonpFunction works (sometimes?) I suspect a conflict in your build config or another webpack runtime which is conflicting with yours (see the warning on the page). That's why I suggested looking into jsonpFunction since I recently had the same issue. Every runtime should have it's own name.

@leoloso
Copy link
Author

leoloso commented Jul 9, 2020

Thanks for the link and suggestion, I'll check it out.

since I recently had the same issue

Did it happen also in Gutenberg? Is this something that should be taken care of already in Gutenberg core? Or at least, to add documentation in Gutenberg warning about the issue? (after all, I have no experience with webpack, I only use it through Gutenberg)

I offered to share access to my private repo, but it makes no sense. I'll get to work already on making my repo public, so I can share the reproduction of the error, and maybe we can check if the error applies to a generic case, or it's just too specific to my plugin. I'll share the link in a few days.

@leoloso
Copy link
Author

leoloso commented Jul 10, 2020

@gziolo @ocean90 I can announce that Dominik's solution fixes the issue 😀

The error is happening when a block imports code from a local package. Then, there are two webpack configurations involved in the compilation of the block: the block's, and the package's.

So I created a custom webpack.config.js for the block, with this code:

const config = require( '@wordpress/scripts/config/webpack.config' );

/**
 * Because the block and the package have their own webpack configuration,
 * they must provide a unique name for the global scope (which is used to lazy-load chunks),
 * otherwise it throws a JS error when loading blocks compiled with `npm run build`
 * @see https://github.com/WordPress/gutenberg/issues/23607
 * @see https://webpack.js.org/configuration/output/#outputjsonpfunction
 */
// ------------------------------------------------------
config.output.jsonpFunction = 'SOME_UNIQUE_NAME_HERE';
// ------------------------------------------------------

module.exports = config;

By adding config.output.jsonpFunction with a unique ID, the issue is solved 🥳

Is this something to be considered for @wordpress/create-block? Or maybe as documentation for @wordpress/scripts?

@leoloso
Copy link
Author

leoloso commented Jul 10, 2020

Btw, I tried two alternatives, only one works:

  1. ✅ Works: Adding a unique config.output.jsonpFunction on the block, and nothing on the packages
  2. ❌ Doesn't work (still same error): Adding a unique config.output.jsonpFunction on each referenced package, and nothing on the block

@leoloso leoloso changed the title @wordpress/scripts - issue with spread operator and Babel: blocks compiled with npm run build do not load in editor, JS error thrown @wordpress/scripts - webpack runtime issue: blocks importing code from a local package, when compiled with npm run build, do not load in editor Jul 10, 2020
@bobbingwide
Copy link
Contributor

Is this something to be considered for @wordpress/create-block? Or maybe as documentation for @wordpress/scripts?

In my opinion this is definitely something to be considered.
My preferences would be in this order:

  1. for the wp-scripts routine to set the jsonpFunction value automatically - based on the "name"
  2. for @wordpress/create-block to generate a webpack.config.js
  3. for the documentation to clearly state what's needed in order to create a production build that's compatible with other plugins

@dnpg
Copy link

dnpg commented Sep 14, 2020

Thanks @leoloso for a deep look into the issue. I was having a similar issue. And thanks to you I was able to fix it in my project.

I'm using npx @wordpress/create-block to create my blocks. When I run yarn start (which runs wp-scripts start) the block works for me fine. As soon as I build it yarn build (which runs wp-scripts build )everything breaks.

In my case the error I was getting in my console was (Uncaught TypeError: Object(...) is not a function at Module.

Screen Shot 2020-09-15 at 9 31 01 am

After reading all this thread I decided to do what you did, create a webpack.config.js file in my block and add an id to config.output.jsonFunction.

const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );

/**
 * Because the block and the package have their own webpack configuration,
 * they must provide a unique name for the global scope (which is used to lazy-load chunks),
 * otherwise it throws a JS error when loading blocks compiled with `npm run build`
 * @see https://github.com/WordPress/gutenberg/issues/23607
 * @see https://webpack.js.org/configuration/output/#outputjsonpfunction
 */
// ------------------------------------------------------
defaultConfig.output.jsonpFunction = 'SOME_UNIQUE_NAME_HERE';
// ------------------------------------------------------

module.exports = defaultConfig;

I agree with @bobbingwide recommendation. Specially point 3. Documentation is a key.

@gziolo
Copy link
Member

gziolo commented Dec 22, 2020

for the wp-scripts routine to set the jsonpFunction value automatically - based on the "name"

I did a quick check how react-scripts resolved this issue and I found this:

https://github.com/facebook/create-react-app/blob/282c03f9525fdf8061ffa1ec50dce89296d916bd/packages/react-scripts/config/webpack.config.js#L234

It aligns with the proposal shared. Let's do it! Unless webpack 5 upgrade that we are almost ready to land resolves it 😄

@artpi
Copy link
Contributor

artpi commented Dec 29, 2020

Thank you @dnpg - I can confirm that this fixes it for me.

My GPT-3 block was conflicting with Jetpack and Stackable blocks. I had the same error message as others in this thread.

One way to avoid the problem for me was to use wp-scripts start to generate output and commit that to the repository. That solution is not causing any conflicts with other plugins.

@gziolo
Copy link
Member

gziolo commented Dec 29, 2020

It looks like this issue was resolved in webpack 5:

https://webpack.js.org/blog/2020-10-10-webpack-5-release/#automatic-unique-naming

In webpack 4 multiple webpack runtimes could conflict on the same HTML page, because they use the same global variable for chunk loading. To fix that it was needed to provide a custom name to the output.jsonpFunction configuration.

Webpack 5 does automatically infer a unique name for the build from package.json name and uses this as default for output.uniqueName.

This value is used to make all potential conflicting globals unique.

MIGRATION: Remove output.jsonpFunction in favor of a unique name in your package.json.

We are about to switch to webpack 5 so it should non issue when that happens ✨

@leeshadle
Copy link

leeshadle commented Dec 30, 2020

Thank you for opening this @leoloso!! I have been tearing my hair out trying to figure out why this was happening...

For reference, I have 5 block plugins installed and they would all conflict with one another after running the wp-scripts build script, no problems with the start script...

I finally was able to pinpoint the issue to any plugins that import ./style.scss files. Removing the style.scss import would resolve the issue.

Ultimately, this solution fixed the issue for me as well so I could import those ./style.scss files again:
#23607 (comment)

@gziolo
Copy link
Member

gziolo commented Jan 4, 2021

We are about to switch to webpack 5 so it should non issue when that happens ✨

In the meantime, we landed webpack 5 upgrade in #26382 but we had to revert because of several incompatibilities with the plugins used, plus Storybook that Gutenberg uses doesn't work with webpack 4 yet.

Anyway, I plan to apply the patch to resolve the issue for webpack 4 as proposed in #23607 (comment) by @ocean90.

@gziolo gziolo added [Type] Bug An existing feature does not function as intended and removed Needs Testing Needs further testing to be confirmed. labels Jan 5, 2021
@gziolo
Copy link
Member

gziolo commented Jan 5, 2021

I hope that #27985 will fix it.

@smakinson
Copy link

@leoloso I am seeing this issue. I did not use create-block. I have a plugin with 3 blocks in it. It looks like:

- my-plugin
-  src
-    blocks
+      block1  
+      block2
+      block3

package.json is at the root. If I register all the blocks I have one that works & 2 that do not. If I comment out 2 of them, the one that did not then does work. I wondered if you can point me to how to specify the webpack.config.js for each block. Currently I have a script in package.json for building each block and one that builds all 3.

wp-scripts build ./src/blocks/block1/src/index.js --output-path=./src/blocks/block1/build

Can I point wp-scripts build at the config? Or do I need to set up package.json in each block's directory?

@smakinson
Copy link

@leoloso sorry about that, right after I hit comment I noticed something that I looks like it fixes it and accomplishes the same thing.

This is what I did (for each blocks script):

wp-scripts build ./src/blocks/block1/src/index.js --output-jsonp-function=UNIQUE_NAME --output-path=./src/blocks/block1/build

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Tool] WP Scripts /packages/scripts [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants