s-KaiNet / spfx-fast-serve

Improve your SharePoint Framework development flow by speeding up the "serve" command :rocket:
MIT License
132 stars 11 forks source link

fast-serve no longer serves. It fails shortly after the build is finished, but before serving content. #128

Closed Zarepheth closed 3 months ago

Zarepheth commented 3 months ago

spfx-fast-serve@4.0.0, installed globally though docs indicate calling CLI with npx ... would remove the need for this.

VS Code Terminal, fast-serve Logs (expand for content) ``` > fast-serve [15:51:24] [fast-serve] Running fast-serve in debug mode [15:51:24] [fast-serve] fast-serve: 1.18.11 [15:51:24] [fast-serve] node: v18.19.0 [15:51:24] [fast-serve] platform: win32 [15:51:24] [fast-serve] command: npm[serve]="fast-serve" [15:51:24] [fast-serve] Settings: { port: 4321, memory: 8192, isLibraryComponent: false, loggingLevel: 'normal', fullScreenErrors: true, eslint: true, hotRefresh: false, reactProfiling: false, containers: false, debug: true } [15:51:24] [fast-serve] Running SPFx bundle [15:51:26] Node flags detected: --max-old-space-size=8192 [15:51:26] Respawned to PID: 6664 Build target: DEBUG [15:51:38] Using gulpfile ~\my-project\client\gulpfile.js [15:51:38] Starting 'bundle'... [15:51:38] Starting gulp [15:51:38] Starting subtask 'pre-copy'... [15:51:38] Finished subtask 'pre-copy' after 219 ms [15:51:38] Starting subtask 'copy-static-assets'... [15:51:38] Starting subtask 'sass'... [15:51:40] Finished subtask 'sass' after 1.49 s [15:51:40] Starting subtask 'lint'... [15:51:40] [lint] eslint version: 8.7.0 [15:51:40] Starting subtask 'tsc'... [15:51:40] [tsc] typescript version: 4.7.4 [15:51:40] Finished subtask 'copy-static-assets' after 1.86 s [15:51:52] Finished subtask 'tsc' after 12 s [15:51:52] Starting subtask 'save-webpack-config'... [15:51:52] Finished subtask 'save-webpack-config' after 1.14 ms [15:51:58] Finished subtask 'lint' after 19 s [15:51:58] Starting subtask 'post-copy'... [15:51:58] Finished subtask 'post-copy' after 508 μs [15:51:58] Starting subtask 'configure-webpack'... [15:52:00] Finished subtask 'configure-webpack' after 1.69 s [15:52:00] Starting subtask 'webpack'... [15:52:17] Warning - [webpack] 'dist': The "output.publicPath" option is set in the Webpack configuration. The SetPublicPathCurrentScriptPlugin plugin may produce unexpected results. It is recommended that the "output.publicPath" configuration option be unset when using this plugin. [15:52:17] Finished subtask 'webpack' after 17 s [15:52:17] Finished 'bundle' after 40 s [15:52:18] ==================[ Finished ]================== Warning - [webpack] 'dist': The "output.publicPath" option is set in the Webpack configuration. The SetPublicPathCurrentScriptPlugin plugin may produce unexpected results. It is recommended that the "output.publicPath" configuration option be unset when using this plugin. [15:52:18] Project text-replacement-tool version:0.0.1 [15:52:18] Build tools version:3.18.1 [15:52:18] Node version:v18.19.0 [15:52:18] Total duration:50 s [15:52:18] Task warnings:1 [15:52:19] [fast-serve] Finished SPFx bundle in 54.83s [helpers.js, getEntryPoints] for loop { key: 'my-project', 'entry[key]': { import: 'C:\\my-project\\client\\lib\\webparts\\myProject\\myProjectWebPart.js', library: { type: 'amd', name: '57fae6c5-2682-443b-a5c4-f4cd85f892f0_0.0.1' } } } [15:52:23] [fast-serve] entryPath.indexOf is not a function C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:92 if (entryPath.indexOf('bundle-entries') === -1) { ^ TypeError: entryPath.indexOf is not a function at getEntryPoints (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:92:23) at C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:59:57 at Generator.next () at fulfilled (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:28:58) Node.js v18.19.0 [15:52:23] [fast-serve] The process terminated unexpectedly ```

The important piece:

[15:52:23] [fast-serve] entryPath.indexOf is not a function
C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:92
        if (entryPath.indexOf('bundle-entries') === -1) {

I've added a console.log(...) statement to the helpers.js file in my node_modules folder. That puts the following content in the logs, just before the error occurs:

[helpers.js, getEntryPoints] for loop {
  key: 'my-project',
  'entry[key]': {
    import: 'C:\\my-project\\client\\lib\\webparts\\myProject\\myProjectWebPart.js',
    library: { type: 'amd', name: '57fae6c5-2682-443b-a5c4-f4cd85f892f0_0.0.1' }
  }
}

It appears that the helper.js method is looking for something different in the generated temp/_webpack_config.json file than what it is finding.

{
  // ...
  "entry": {
    "my-project": {
      "import": "C:\\my-project\\client\\lib\\webparts\\myProject\\myProjectWebPart.js",
      "library": {
        "type": "amd",
        "name": "57fae6c5-2682-443b-a5c4-f4cd85f892f0_0.0.1"
      }
    }
  },
  // ...
}

Should build process be generating something different in the temp/_webpack_config.json file? What can I do to fix this error?

package.json ```json { "name": "my-project", "version": "0.0.1", "private": true, "engines": { "node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0" }, "main": "lib/index.js", "scripts": { "build": "gulp bundle", "serve": "fast-serve", "package": "gulp bundle --ship && gulp package-solution --ship", "clean": "gulp clean", "test": "gulp test", "trust-cert": "gulp trust-dev-cert" }, "dependencies": { "@fluentui/react": "^8.106.4", "@fluentui/react-hooks": "^8.6.36", "@microsoft/sp-component-base": "^1.18.2", "@microsoft/sp-core-library": "^1.18.2", "@microsoft/sp-lodash-subset": "^1.18.2", "@microsoft/sp-odata-types": "^1.18.2", "@microsoft/sp-office-ui-fabric-core": "^1.18.2", "@microsoft/sp-page-context": "^1.18.2", "@microsoft/sp-property-pane": "^1.18.2", "@microsoft/sp-webpart-base": "^1.18.2", "@pnp/spfx-controls-react": "^3.17.0", "react": "17.0.1", "react-dom": "17.0.1", "tslib": "2.3.1" }, "devDependencies": { "@microsoft/eslint-config-spfx": "^1.18.2", "@microsoft/eslint-plugin-spfx": "^1.18.2", "@microsoft/rush-stack-compiler-4.7": "0.1.0", "@microsoft/sp-build-web": "^1.18.2", "@microsoft/sp-module-interfaces": "^1.18.2", "@rushstack/eslint-config": "2.5.1", "@types/node": "^20.11.28", "@types/react": "17.0.45", "@types/react-dom": "17.0.17", "@types/webpack-env": "~1.15.2", "ajv": "^6.12.5", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "eslint": "8.7.0", "eslint-plugin-react-hooks": "4.3.0", "gulp": "4.0.2", "prettier": "^3.2.5", "spfx-fast-serve-helpers": "~1.18.0", "typescript": "4.7.4", "webpack": "^4.47.0" } } ```
gulpfile.js ```js /* eslint-disable @typescript-eslint/no-var-requires */ 'use strict'; const build = require('@microsoft/sp-build-web'); const webpack = require("webpack"); const getClientEnvironment = require("./process-env.cjs"); build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); build.configureWebpack.mergeConfig({ additionalConfiguration: (cfg => { let pluginDefine = null; for (var i = 0; i < cfg.plugins.length; i++) { var plugin = cfg.plugins[i]; if (plugin instanceof webpack.DefinePlugin) { pluginDefine = plugin; } } const currentEnv = getClientEnvironment().stringified; if (pluginDefine) { pluginDefine.definitions = { ...pluginDefine.definitions, ...currentEnv }; } else { cfg.plugins.push(new webpack.DefinePlugin(currentEnv)); } if (build.getConfig().production) { var basePath = build.writeManifests?.taskConfig?.cdnBasePath || ''; if (!basePath.endsWith('/')) { basePath += '/'; } cfg.output.publicPath = basePath; } else { cfg.output.publicPath = "https://localhost:4321/dist/"; } return cfg; }) }); var getTasks = build.rig.getTasks; build.rig.getTasks = function () { var result = getTasks.call(build.rig); result.set('serve', result.get('serve-deprecated')); return result; }; /* fast-serve */ const { addFastServe } = require("spfx-fast-serve-helpers"); addFastServe(build); /* end of fast-serve */ build.initialize(require('gulp')); ```
webpack.extend.js ```js /* eslint-disable @typescript-eslint/no-var-requires */ /* * User webpack settings file. You can add your own settings here. * Changes from this file will be merged into the base webpack configuration file. * This file will not be overwritten by the subsequent spfx-fast-serve calls. */ const build = require("@microsoft/sp-build-web"); const getClientEnvironment = require("../process-env.cjs"); /** * you can add your project related webpack configuration here, it will be merged using webpack-merge module * i.e. plugins: [new webpack.Plugin()] */ let webpackConfig = {} /** * For even more fine-grained control, you can apply custom webpack settings using below function * @param {object} cfg - initial webpack config object * @param {object} webpack - webpack object, used by SPFx pipeline * @returns webpack config object */ const transformConfig = function (cfg, webpack) { // transform the initial webpack config here, i.e. // initialWebpackConfig.plugins.push(new webpack.Plugin()); etc. console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - start", { cfg }); let pluginDefine = null; for (var i = 0; i < cfg.plugins.length; i++) { var plugin = cfg.plugins[i]; if (plugin instanceof webpack.DefinePlugin) { pluginDefine = plugin; } } const currentEnv = getClientEnvironment().stringified; console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - currentEnv", { currentEnv }); if (pluginDefine) { pluginDefine.definitions = { ...pluginDefine.definitions, ...currentEnv }; console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - pluginDefine.definitions", { "pluginDefine.definitions": pluginDefine.definitions, }); } else { cfg.plugins.push(new webpack.DefinePlugin(currentEnv)); } if (build.getConfig().production) { var basePath = build.writeManifests?.taskConfig?.cdnBasePath || ''; if (!basePath.endsWith("/")) { basePath += "/"; } cfg.output.publicPath = basePath; } else { cfg.output.publicPath = "https://localhost:4321/dist/"; } console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - finished", { cfg }); return cfg; }; module.exports = { webpackConfig, transformConfig, }; ```

More environment info:

>npm list -g --depth 0
C:\Program Files\nodejs -> .\
+-- @microsoft/generator-sharepoint@1.18.2
+-- @pnp/cli-microsoft365@7.5.0
+-- gulp-cli@2.3.0
+-- npm@10.2.3
+-- spfx-fast-serve@4.0.0
`-- yo@5.0.0

Node: 18.19.0

s-KaiNet commented 3 months ago

Thank you for the very detailed report! It always helps to faster identify the problem.
Meanwhile, I have one question - you said "fast-serve no longer serves" - do you know, what was changed\added in your project so that fast-serve stopped working? Or the project was migrated from earlier versions?

Zarepheth commented 3 months ago

We spent time setting up GitLab CI to build and deploy the app to an SPO tenant's App Catalog.

I can take a closer look at a diff between when Fast Serve was working and now. But, outside of the CI setup, the biggest change was adding dotenv to enable environment specific builds. You'll some of the code for that in the gulpfile.js and webpack.extend.js files. The process-env.cjs file just contains the recommended logic for finding and applying settings from .env and various .env.* files.

Due to our project using ES modules and many of the build files using CommonJS, I renamed the extension from .js to .cjs to work around a module type conflict during build.

Zarepheth commented 3 months ago

I do notice that we replaced . with ?. for a chain of references in the gulpfile.js and webpack.extend.js files.

That works for TypeScript, which is most of the app's code. But I'm not so sure about it's validity for JavaScript... (no compiler errors during build, only after Fast Serve tries to start serving)

Zarepheth commented 3 months ago

As I've been trouble-shooting this, I've made some minor changes to some files in the node_modules/spfx-fast-serve-helpers/... folders.

Besides adding some console.log(...) to display variable contents, I added this just before the if statement in the getEntryPoints(entry) method of the helper.js file:

if (typeof (entryPath) === "object" && entryPath["import"]) {
  entryPath = createTsEntryPath(entryPath.import);
} else

For my case, the entryPath variable is an object, not an array or string. I was just guessing that the import member's path corresponded with the expected path string. But it did get me past my initial error. But then, the Fast Server helpers were looking for temp/manifests.json. This file does not exist, but the build process does produce a temp/manifests.js file (a file of minimized JavaScript logic).

Replacing json with js for the requested file's extension has it find and start processing the manifests.js file, but then it fails with this error:

[18:01:40] [fast-serve] Automatic publicPath is not supported in this browser
C:\my-project\client\temp\manifests.js:1

At this point, no browser is involved. Just processing in Node before the Webpack Dev Server starts. I think my modifications are leading down the wrong path...

s-KaiNet commented 3 months ago

The problem is in your entry webpack property. In a normal SPFx solution, the entry is in a format Record<string, string>, i.e. in your case fast-serve expects the entry as

 "entry": {
    "my-project": "C:\\my-project\\client\\lib\\webparts\\myProject\\myProjectWebPart.js"
  },

but for some reason, your entry looks like what you posted before:

"entry": {
    "my-project": {
      "import": "C:\\my-project\\client\\lib\\webparts\\myProject\\myProjectWebPart.js",
      "library": {
        "type": "amd",
        "name": "57fae6c5-2682-443b-a5c4-f4cd85f892f0_0.0.1"
      }
    }
  },

TBH I first time see this type of entry. Also, I noticed the below warning in a normal gulp bundle output from your project:

The "output.publicPath" option is set in the Webpack configuration. The SetPublicPathCurrentScriptPlugin plugin may produce unexpected results. It is recommended that the "output.publicPath" configuration option be unset when using this plugin.

Seems it's related to changes you have inside build.configureWebpack.mergeConfig and setting cfg.output.publicPath. The thing is, that SPFx uses a custom public path plugin to control behavior related to public paths. Maybe this is the reason why you have so weird entry as well.

My suggestion would be applying some modifications.

in gulpfile.js, the code:

  if (build.getConfig().production) {
    var basePath = build.writeManifests?.taskConfig?.cdnBasePath || '';
    if (!basePath.endsWith("/")) {
      basePath += "/";
    }
    cfg.output.publicPath = basePath;
  } else {
    cfg.output.publicPath = "https://localhost:4321/dist/";
  }

to replace with just

  if (build.getConfig().production) {
    var basePath = build.writeManifests?.taskConfig?.cdnBasePath || '';
    if (!basePath.endsWith("/")) {
      basePath += "/";
    }
    cfg.output.publicPath = basePath;
  }

so that in debug mode we don't modify publicPath property. I don't know if it's good option to modify it in production, but it's another story.

For fast-serve modify webpack.extend.js and completely remove if\else code block with if (build.getConfig().production) because it doesn't make sense for fast-serve, cause fast-serve always runs in debug mode. Furthermore, build object will be invalid, because it's not initialized properly inside fast-serve, thus not reliable inside webpack.extend.js.

webpack.extend.js ```js /* eslint-disable @typescript-eslint/no-var-requires */ /* * User webpack settings file. You can add your own settings here. * Changes from this file will be merged into the base webpack configuration file. * This file will not be overwritten by the subsequent spfx-fast-serve calls. */ const getClientEnvironment = require("../process-env.cjs"); /** * you can add your project related webpack configuration here, it will be merged using webpack-merge module * i.e. plugins: [new webpack.Plugin()] */ let webpackConfig = {} /** * For even more fine-grained control, you can apply custom webpack settings using below function * @param {object} cfg - initial webpack config object * @param {object} webpack - webpack object, used by SPFx pipeline * @returns webpack config object */ const transformConfig = function (cfg, webpack) { // transform the initial webpack config here, i.e. // initialWebpackConfig.plugins.push(new webpack.Plugin()); etc. console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - start", { cfg }); let pluginDefine = null; for (var i = 0; i < cfg.plugins.length; i++) { var plugin = cfg.plugins[i]; if (plugin instanceof webpack.DefinePlugin) { pluginDefine = plugin; } } const currentEnv = getClientEnvironment().stringified; console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - currentEnv", { currentEnv }); if (pluginDefine) { pluginDefine.definitions = { ...pluginDefine.definitions, ...currentEnv }; console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - pluginDefine.definitions", { "pluginDefine.definitions": pluginDefine.definitions, }); } else { cfg.plugins.push(new webpack.DefinePlugin(currentEnv)); } console.log("[Fast-Serve, 'webpack.extend.js'] transformConfig function - finished", { cfg }); return cfg; }; module.exports = { webpackConfig, transformConfig, }; ```

Try to apply above changes and see if it works for you.

Zarepheth commented 3 months ago

Thank you, I'll try your recommendations when I return to work on Monday.

Zarepheth commented 3 months ago

FYI - the public path changes in my gulpfile.js and webpack.extend.js files were to reroute image paths from the SPO site's workbench to my local dev-server. I came across some old articles that recommended the approach so that images would display properly in the SPO workbench. There are probably better ways to accomplish that, but I've noticed Microsoft's SPFx components use outdated packages...

s-KaiNet commented 3 months ago

Just a side note - in your build.configureWebpack.mergeConfig you don't need to search for DefinePlugin, because you can add as many define plugins as you want, all the configurations specified there will be merged. I.e. you could simplify below code:

let pluginDefine = null;
    for (var i = 0; i < cfg.plugins.length; i++) {
      var plugin = cfg.plugins[i];
      if (plugin instanceof webpack.DefinePlugin) {
        pluginDefine = plugin;
      }
    }

    const currentEnv = getClientEnvironment().stringified;

    if (pluginDefine) {
      pluginDefine.definitions = { ...pluginDefine.definitions, ...currentEnv };
    } else {
      cfg.plugins.push(new webpack.DefinePlugin(currentEnv));
    }

to just

const currentEnv = getClientEnvironment().stringified;
cfg.plugins.push(new webpack.DefinePlugin(currentEnv));
Zarepheth commented 3 months ago

I made the changes, but still getting the error:

[09:56:21] [fast-serve] entryPath.indexOf is not a function
C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:91
        if (entryPath.indexOf('bundle-entries') === -1) {
                      ^

TypeError: entryPath.indexOf is not a function
    at getEntryPoints (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:91:23)
    at C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:59:57
    at Generator.next (<anonymous>)
    at fulfilled (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:28:58)
Full Log ``` * Executing task: npm run serve > my-project@0.0.1 serve > fast-serve [09:55:09] [fast-serve] Running fast-serve in debug mode [09:55:09] [fast-serve] fast-serve: 1.18.11 [09:55:09] [fast-serve] node: v18.19.0 [09:55:09] [fast-serve] platform: win32 [09:55:09] [fast-serve] command: npm[serve]="fast-serve" [09:55:09] [fast-serve] Settings: { port: 4321, memory: 8192, isLibraryComponent: false, loggingLevel: 'normal', fullScreenErrors: true, eslint: true, hotRefresh: false, reactProfiling: false, containers: false, debug: true } [09:55:09] [fast-serve] Running SPFx bundle [09:55:12] Node flags detected: --max-old-space-size=8192 [09:55:12] Respawned to PID: 4560 Build target: DEBUG [09:55:24] Using gulpfile ~\Documents\Projects\my-project\client\gulpfile.js [09:55:24] Starting 'bundle'... [09:55:24] Starting gulp [09:55:24] Starting subtask 'pre-copy'... [09:55:24] Finished subtask 'pre-copy' after 213 ms [09:55:24] Starting subtask 'copy-static-assets'... [09:55:24] Starting subtask 'sass'... [09:55:26] Finished subtask 'sass' after 1.8 s [09:55:26] Starting subtask 'lint'... [09:55:26] [lint] eslint version: 8.7.0 [09:55:26] Starting subtask 'tsc'... [09:55:26] [tsc] typescript version: 4.7.4 [09:55:26] Finished subtask 'copy-static-assets' after 2.26 s [09:55:39] Finished subtask 'tsc' after 13 s [09:55:39] Starting subtask 'save-webpack-config'... [09:55:39] Finished subtask 'save-webpack-config' after 1.2 ms [09:55:45] Finished subtask 'lint' after 19 s [09:55:45] Starting subtask 'post-copy'... [09:55:45] Finished subtask 'post-copy' after 1.69 ms [09:55:45] Starting subtask 'configure-webpack'... [09:55:47] Finished subtask 'configure-webpack' after 2.25 s [09:55:47] Starting subtask 'webpack'... [09:56:13] Finished subtask 'webpack' after 25 s [09:56:13] Finished 'bundle' after 49 s [09:56:13] ==================[ Finished ]================== [09:56:14] Project my-project version:0.0.1 [09:56:14] Build tools version:3.18.1 [09:56:14] Node version:v18.19.0 [09:56:14] Total duration:58 s [09:56:14] [fast-serve] Finished SPFx bundle in 65.19s [09:56:21] [fast-serve] entryPath.indexOf is not a function C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:91 if (entryPath.indexOf('bundle-entries') === -1) { ^ TypeError: entryPath.indexOf is not a function at getEntryPoints (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\common\helpers.js:91:23) at C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:59:57 at Generator.next () at fulfilled (C:\my-project\client\node_modules\spfx-fast-serve-helpers\lib\webpack\configureWebPack.js:28:58) Node.js v18.19.0 [09:56:21] [fast-serve] The process terminated unexpectedly * The terminal process "C:\Program Files\Git\bin\bash.exe '--login', '-i', '-c', 'npm run serve'" terminated with exit code: 1. * Terminal will be reused by tasks, press any key to close it. ```
Updated gulpfile.js ```js /* eslint-disable @typescript-eslint/no-var-requires */ 'use strict'; const build = require('@microsoft/sp-build-web'); const webpack = require("webpack"); const getClientEnvironment = require("./process-env.cjs"); build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); build.configureWebpack.mergeConfig({ additionalConfiguration: (cfg => { const currentEnv = getClientEnvironment().stringified; cfg.plugins.push(new webpack.DefinePlugin(currentEnv)); return cfg; }) }); var getTasks = build.rig.getTasks; build.rig.getTasks = function () { var result = getTasks.call(build.rig); result.set('serve', result.get('serve-deprecated')); return result; }; /* fast-serve */ const { addFastServe } = require("spfx-fast-serve-helpers"); addFastServe(build); /* end of fast-serve */ build.initialize(require('gulp')); ```
Updated webpack.extend.js ```js /* eslint-disable @typescript-eslint/no-var-requires */ /* * User webpack settings file. You can add your own settings here. * Changes from this file will be merged into the base webpack configuration file. * This file will not be overwritten by the subsequent spfx-fast-serve calls. */ const getClientEnvironment = require("../process-env.cjs"); /** * you can add your project related webpack configuration here, it will be merged using webpack-merge module * i.e. plugins: [new webpack.Plugin()] */ let webpackConfig = {} /** * For even more fine-grained control, you can apply custom webpack settings using below function * @param {object} cfg - initial webpack config object * @param {object} webpack - webpack object, used by SPFx pipeline * @returns webpack config object */ const transformConfig = function (cfg, webpack) { // transform the initial webpack config here, i.e. // initialWebpackConfig.plugins.push(new webpack.Plugin()); etc. const currentEnv = getClientEnvironment().stringified; cfg.plugins.push(new webpack.DefinePlugin(currentEnv)); return cfg; }; module.exports = { webpackConfig, transformConfig, }; ```
Zarepheth commented 3 months ago

I also commented out the DefinePlugin related code and still got the same error.

I'll keep debugging, but I currently have no clue why the generated _webpack_config.json file's entry element is not in the expected format.

Zarepheth commented 3 months ago

Digging through our source-control commits, I finally pinned down the place where Fast Serve stopped working versus the previous commit where it did work. The package-lock.json file is the only file with changes between a commit where Fast Serve works and one where Fast Serve fails.

I typically run npm install to refresh the npm packages when switching branches or checking out different commits. And that can change the package-lock.json file, though usually the changes are inconsequential. Between these two commits, I think the node_modules folder was deleted, the package-lock.json file deleted, and the npm install run.

The changes in the file are quite extensive.

Excerpts from diff of package-lock.json file. Specifically dependencies with new major versions! (I stopped looking around 20% of the way through the file...)
PreviousNew
```json "node_modules/@azure/core-http": { "version": "2.3.2", ``` ```json "node_modules/@azure/core-http": { "version": "3.0.4", ```
```json "node_modules/@azure/identity": { "version": "2.1.0", ``` ```json "node_modules/@azure/identity": { "version": "4.0.1", ```
```json "node_modules/@azure/msal-node": { "version": "1.18.4", ``` ```json "node_modules/@azure/msal-node": { "version": "2.6.5", ```
```json "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { "version": "13.3.1", ``` ```json "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { "version": "14.8.0", ```
```json "node_modules/@microsoft/gulp-core-build-webpack": { "version": "5.4.0", ``` ```json "node_modules/@microsoft/gulp-core-build-webpack": { "version": "6.0.0", ```
```json "node_modules/@microsoft/rush-lib/node_modules/@rushstack/node-core-library": { "version": "3.60.1", ``` ```json "node_modules/@microsoft/rush-lib/node_modules/@rushstack/node-core-library": { "version": "4.0.2", ```
```json "node_modules/@microsoft/sp-build-core-tasks/node_modules/@rushstack/node-core-library": { "version": "3.61.0", ``` ```json "node_modules/@microsoft/sp-build-core-tasks/node_modules/@rushstack/node-core-library": { "version": "4.0.2", ```
```json "node_modules/@microsoft/sp-build-web/node_modules/@rushstack/node-core-library": { "version": "3.61.0", ``` ```json "node_modules/@microsoft/sp-build-web/node_modules/@rushstack/node-core-library": { "version": "4.0.2", ```
```json "node_modules/@microsoft/sp-css-loader/node_modules/@rushstack/node-core-library": { "version": "3.61.0", ``` ```json "node_modules/@microsoft/sp-css-loader/node_modules/@rushstack/node-core-library": { "version": "4.0.2", ```

My coworker and I did some experimenting with Webpack, either explicitly specifying it as a dev-dependency or using "overrides" to ensure all references were either version 4 or version 5. Webpack v5 does not work with SPFx 1.18, so we settled on Webpack v4. As I was looking for packages with different major versions, I was wondering if those experiments caused major changes to the package-lock.json file. All of the major version changes I saw in the first 20% of the file were related Microsoft packages, making me think MS pushed some updates to NPM which included changes intended for the next release of SPFx. Just guessing on my part...

I'm going to see what happens if I again delete node_modules, package-lock.json, and re-run npm install. Maybe that will clean up this mess?

Zarepheth commented 3 months ago

So... Deleting the package-lock.json file and node_modules directory, then re-running npm install did not fix the problem.

But, Deleting the node_modules directory and checking out the previous commit's package-lock.json, and then re-running npm install did get Fast Serve working again.

I thought the package-lock.json was only used when running npm ci, not when doing normal npm install. And I am also confused about why deleting it and running npm install would result in major, breaking, differences with the previous version of the file.

s-KaiNet commented 3 months ago

Ok, now I know where the problem is. Your package-lock.json and webpack 5 mention helped me to figured it out. Surprisingly, you have issues because of release SPFx 1.19-beta.0.

They introduced webpack 5 in the SPFx beta, moreover, they introduced a new feature called "Build time packages release is decoupled from the SPFx release". It means, that npm packages, used to build SPFx project (like @microsoft/sp-build-web) now have separate versioning. Also, the latest published version of @microsoft/sp-build-web is 1.20.1 and has a dependency on webpack 5.

So what happens in your project - since you have caret (^) in dependencies, npm install command now (after SPFx 1.19-beta.0) installs some build packages of version 1.20.1, which is webpack 5 based and currently not compatible with fast-serve.

How to fix - remove node_modules, package-lock.json, update package.json and remove ^ from all microsoft-related dependencies (basically ^1.18.2 should be just 1.18.2). Reinstall and it should work.

Thank again for your deep dig into the problem, it helps!

Zarepheth commented 3 months ago

I replaced ^ with ~ in the package.json file (just with the @microsoft items), deleted the package-lock.json, deleted node_modules, and then ran npm install.

Shifting from allowing "minor" updates to only permitting "patch" updates solved the problem.

Thank you!

s-KaiNet commented 3 months ago

Glad the issue is finally resolved!