SharePoint / sp-dev-docs

SharePoint & Viva Connections Developer Documentation
https://docs.microsoft.com/en-us/sharepoint/dev/
Creative Commons Attribution 4.0 International
1.24k stars 1k forks source link

1.16.1 with heft #8767

Open amcmahon-VInet opened 1 year ago

amcmahon-VInet commented 1 year ago

Target SharePoint environment

SharePoint Online

What SharePoint development model, framework, SDK or API is this about?

πŸ’₯ SharePoint Framework

Developer environment

Windows

What browser(s) / client(s) have you tested

Additional environment details

SPFx version: 1.16.1 Heft version: 0.47.11 Node version: v16.14.1

Describe the bug / error

Sorry, this is a bit of a long one...

We have been looking at speeding up our dev by taking advantage of heft and hot module replacement, but have found a couple of issues trying to get a solution working.

The main one being that an ootb spfx solution scaffolded with --use-heft fails to run.

This seems to be an issue with 1.16.1 using a mixture of webpack 4 and 5 in the build pipeline. We can work around this by overriding the webpack version in the package.json with something like:

"overrides": {
    "webpack": "^4.44.0"
}

This gets the solution to run, but HMR isn't working ootb as it seems the updated modules are not accepted, so the page bubbles up to the top and reloads.

image

We can again work around this by using react-hot-loader to manage the updated modules by wrapping the top level component with its HOC hot:

import * as React from 'react';
import styles from './HelloWorld.module.scss';
import { IHelloWorldProps } from './IHelloWorldProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { hot } from "react-hot-loader/root";

export class HelloWorld extends React.Component<IHelloWorldProps, {}> {
    [Templated Component Code]
}

export default hot(HelloWorld);

And this gets HMR working! Which is great until we update the template code to remove the require('../assets/welcome-dark.png') for the placeholder asset images. If you stop and start the server, or refresh the page fully, then HMR breaks by emitting a relative link to the hot-update.json to the workbench on update, which 404s trying to find it in SPO instead of the local devserver and we get a json error and a failure to reload anything.

image

We found that the WebpackConfigurationGenerator.js in the spfx-heft-plugins package is not setting a publicPath on the output object in the default webpack config:

            output: {
                hashFunction: 'md5',
                chunkFilename: `chunk.${outputFilename}`,
                filename: outputFilename,
                library: webpackLibraryName,
                libraryTarget: options.libraryTarget || 'amd',
                path: options.folders.outputFolder,
                devtoolModuleFilenameTemplate: 'webpack:///../[resource-path]',
                devtoolFallbackModuleFilenameTemplate: 'webpack:///../[resource-path]?[hash]',
                crossOriginLoading: options.crossOriginLoading !== false && 'anonymous'
            },

Adding this property manually in the file made HMR working again (publicPath: "https://localhost:4321/dist/",). OR if we webpack-require something in a component it works as well.

For some reason, adding a webpack-require in the solution has the same effect (thinking it somehow hooks up a path that webpack uses that points to localhost? 🀷).

These workarounds are fine and dramatically speed up dev time, but when we try to then build using npm run build, it fails again because it seems that eslint is throwing errors that seem to be because of incompatibilities with webpack versions again:

Encountered 3 errors:
  [eslint] src/webparts/helloWorld/components/IHelloWorldProps.ts - Parsing error: Cannot read properties of undefined (reading 'map')
  [eslint] src/webparts/helloWorld/components/HelloWorld.tsx - Parsing error: Debug Failure. False expression: position cannot precede the beginning of the file
  [eslint] src/webparts/helloWorld/HelloWorldWebPart.ts - Parsing error: Cannot read properties of undefined (reading 'map')

The build works fine if we use the lite flag (heft build --lite) as this skips linting.

As a side note, the scaffolded package.json when using heft has the build-watch script set to use the lite flag not the watch flag ("build-watch": "heft build --lite",).

Sorry for the info dump, but we'd love to get some insight into using heft and HMR, as we can already see this speeding up dev and build times.

Cheers!

Steps to reproduce

  1. yo @microsoft/sharepoint --use-heft
  2. npm run start
  3. explode
  4. update package.json overrides
  5. npm i
  6. run again - works, but no HMR
  7. add react-hot-loader and hook up hot hoc
  8. refresh page
  9. make change, save, works with hmr
  10. remove boilerplate with webpack-requires from tsx
  11. refresh workbench page and make change, save - hmr fails
  12. add webpack-require or update webpack generator script with publicPath
  13. refresh page, hmr working again
  14. npm run build
  15. explode

Expected behavior

  1. yo @microsoft/sharepoint --use-heft
  2. npm run start
  3. success
ghost commented 1 year ago

Thank you for reporting this issue. We will be triaging your incoming issue as soon as possible.

amcmahon-VInet commented 1 year ago

I have also tried to override the publicPath by extending the webpack.config:

module.exports = {
    output: {
        publicPath: "https://localhost:4321/dist/"
    }
}

but I get an error in the console during compile stating spfx does not want more than 1 config: [spfx-webpack-configuration-plugin] Error: A webpack configuration must not already have been provided.

which comes from here in the WebpackConfigurationPlugin.js:

            build.hooks.bundle.tap(PluginNames.SpfxWebpackConfigurationPlugin, (bundleSubstage) => {
                bundleSubstage.hooks.configureWebpack.tapPromise(PluginNames.SpfxWebpackConfigurationPlugin, async (webpackConfiguration) => {
                    const scopedLogger = heftSession.requestScopedLogger('spfx-webpack-configuration-plugin');
                    if (webpackConfiguration) {
                        scopedLogger.emitError(new Error('A webpack configuration must not already have been provided.'));
                    }
                    return await this._generateSpfxWebpackConfigurationAsync(heftConfiguration, build, scopedLogger, emitStatsFlag.value);
                });
            });
        });

I know we can extend webpack config when using gulp (SPFx - Extending Webpack), but is there an official way to extend the config when using heft?

stevebeauge commented 1 year ago

Heft is still not usable with latest 1.17.2.

Have you found a workaround ?