humanmade / webpack-helpers

Reusable Webpack configuration components & related helper utilities.
https://humanmade.github.io/webpack-helpers
29 stars 3 forks source link

Conflict with `externals` #42

Open kucrut opened 4 years ago

kucrut commented 4 years ago

The way externals are currently defined makes it impossible for to export own's library to the global this.

Example config:

module.exports = {
    // ...
    name: 'myPlugin',
    entry: {
        components: filePath( 'assets/src/components/index.js' ),
        dashboard: filePath( 'assets/src/dashboard.js' ),
        preview: filePath( 'assets/src/preview.js' ),
    },
    output: {
        library: [ 'myPlugin', '[name]' ],
        libraryTarget: 'this',
        path: filePath( '/assets/dist/' ),
    },
    resolve: {
        alias: {
            '@myPlugin/components': filePath( '/assets/src/components/' ),
        },
    },
};

The intention was to make the entrypoints available under this.myPlugin object. However, since webpack helpers defined the externals differently, they're overriden by this config.

From webpack docs:

Note that not setting a output.library will cause all properties returned by the entry point to be assigned to the given object; there are no checks against existing property names.

One possible solution is to update externals definition to:

[ `@wordpress/${ name }` ]: { this: [ 'wp', camelCaseDash( name ) ] }
kadamwhite commented 4 years ago

@kucrut Rereading the Webpack docs, it looks like the this or root options of the externals definition object would only apply if that libraryTarget is in use. If I'm understanding what that means correctly, then, it seems like it would be very verbose to enumerate every variant in our externals config.

Given that this was originally conceived of as a solution for the majority of cases, which covers theme and plugins bundles but does not necessarily include library targets (yet anyway), what would you think about leaving things as is but providing this guidance around how to handle it if you are targeting a library:

const { externals: originalExternals } = require( '@humanmade/webpack-helpers' );

// Remap externals to { this: [ 'wp', 'packageName' ] } format for library use.
const externals = Object.keys( originalExternals ).reduce( ( externals, key ) => {
    const path = originalExternals[ key ].split( '.' );
    return {
        ...externals,
        [ key ]: {
            this: path,
        },
    };
}, {} );

This is untested pseudocode but I think that would potentially address this use case without adding complexity or specificity to the base usage. Thoughts?

kadamwhite commented 4 years ago

@kucrut Remind me to remind you about this thread :)

kucrut commented 4 years ago

@kadamwhite I think it's a good idea to add a code sample in the docs to cover the use case :+1:

I remember the early days when Gutenberg wasn't in core yet and I was creating my very first block. I kept having issues with externals until I followed they way Gutenberg set its output, so I tend to stick with it :slightly_smiling_face:

kadamwhite commented 3 years ago

@kucrut This keeps slipping off my radar. Would you be interested in preparing a pull request for this?