├── .editorconfig ├── .eslintrc.json ├── .github └── workflows │ └── no-response.yml ├── .gitignore ├── .npmignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── FAQ.md ├── HMR.md ├── LibraryComponents.md ├── MS Teams Toolkit.md ├── Migrate-from-3-to-4.md ├── NgrokServe.md └── VersionMatrix.md ├── img ├── missing-module-error.png ├── ms-teams-tk.png └── ngrok.jpg ├── package.json ├── pnpm-lock.yaml ├── samples ├── .gitignore ├── advanced │ ├── .editorconfig │ ├── .eslintrc.js │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ └── settings.json │ ├── .yo-rc.json │ ├── README.md │ ├── config │ │ ├── config.json │ │ ├── copy-assets.json │ │ ├── deploy-azure-storage.json │ │ ├── package-solution.json │ │ ├── sass.json │ │ ├── serve.json │ │ └── write-manifests.json │ ├── fast-serve │ │ └── webpack.extend.js │ ├── gulpfile.js │ ├── package.json │ ├── src │ │ ├── adaptiveCardExtensions │ │ │ └── myCard │ │ │ │ ├── MyCardAdaptiveCardExtension.manifest.json │ │ │ │ ├── MyCardAdaptiveCardExtension.ts │ │ │ │ ├── MyCardPropertyPane.ts │ │ │ │ ├── assets │ │ │ │ └── SharePointLogo.svg │ │ │ │ ├── cardView │ │ │ │ └── CardView.ts │ │ │ │ ├── loc │ │ │ │ ├── en-us.js │ │ │ │ └── mystring.d.ts │ │ │ │ └── quickView │ │ │ │ ├── QuickView.ts │ │ │ │ └── template │ │ │ │ └── QuickViewTemplate.json │ │ ├── index.ts │ │ └── webparts │ │ │ ├── contextInfo │ │ │ ├── ContextInfoPropertyPane.ts │ │ │ ├── ContextInfoWebPart.manifest.json │ │ │ ├── ContextInfoWebPart.ts │ │ │ ├── components │ │ │ │ ├── ContextInfo │ │ │ │ │ ├── ContextInfo.module.scss │ │ │ │ │ ├── ContextInfo.tsx │ │ │ │ │ └── ContextInfoProps.ts │ │ │ │ └── Header │ │ │ │ │ ├── Header.module.scss │ │ │ │ │ └── Header.tsx │ │ │ └── loc │ │ │ │ ├── en-us.js │ │ │ │ ├── mystrings.d.ts │ │ │ │ └── nl-nl.js │ │ │ ├── graphToolkitTest │ │ │ ├── GraphToolkitTestWebPart.manifest.json │ │ │ ├── GraphToolkitTestWebPart.ts │ │ │ ├── assets │ │ │ │ ├── welcome-dark.png │ │ │ │ └── welcome-light.png │ │ │ ├── components │ │ │ │ └── GraphToolkitTest.tsx │ │ │ └── loc │ │ │ │ ├── en-us.js │ │ │ │ └── mystrings.d.ts │ │ │ ├── helloWorld │ │ │ ├── HelloWorldWebPart.manifest.json │ │ │ ├── HelloWorldWebPart.ts │ │ │ ├── components │ │ │ │ ├── HelloWorld.module.scss │ │ │ │ ├── HelloWorld.tsx │ │ │ │ └── IHelloWorldProps.ts │ │ │ └── loc │ │ │ │ ├── en-us.js │ │ │ │ ├── mystrings.d.ts │ │ │ │ └── nl-nl.js │ │ │ └── sampleWebPart │ │ │ ├── SampleWebPartWebPart.manifest.json │ │ │ ├── SampleWebPartWebPart.ts │ │ │ ├── assets │ │ │ └── parker-spfx.png │ │ │ ├── components │ │ │ ├── ISampleWebPartProps.ts │ │ │ ├── SampleWebPart.module.scss │ │ │ └── SampleWebPart.tsx │ │ │ └── loc │ │ │ ├── en-us.js │ │ │ ├── mystrings.d.ts │ │ │ └── nl-nl.js │ ├── teams │ │ ├── 0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_color.png │ │ ├── 0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_outline.png │ │ ├── 709495d2-f2f9-4527-90a2-d36e61eb4433_color.png │ │ ├── 709495d2-f2f9-4527-90a2-d36e61eb4433_outline.png │ │ ├── a9c3e764-8dc5-436a-a66b-57122d33bb0d_color.png │ │ ├── a9c3e764-8dc5-436a-a66b-57122d33bb0d_outline.png │ │ ├── bb40bd87-dfa5-40dd-b333-6c643222727b_color.png │ │ └── bb40bd87-dfa5-40dd-b333-6c643222727b_outline.png │ └── tsconfig.json ├── basic │ ├── .eslintrc.js │ ├── .gitignore │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ └── settings.json │ ├── .yo-rc.json │ ├── README.md │ ├── config │ │ ├── config.json │ │ ├── copy-assets.json │ │ ├── deploy-azure-storage.json │ │ ├── package-solution.json │ │ ├── sass.json │ │ ├── serve.json │ │ └── write-manifests.json │ ├── gulpfile.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── webparts │ │ │ └── basicWebpart │ │ │ ├── BasicWebpartWebPart.manifest.json │ │ │ ├── BasicWebpartWebPart.ts │ │ │ ├── components │ │ │ ├── BasicWebpart.module.scss │ │ │ ├── BasicWebpart.tsx │ │ │ └── IBasicWebpartProps.ts │ │ │ └── loc │ │ │ ├── en-us.js │ │ │ └── mystrings.d.ts │ ├── teams │ │ ├── 0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_color.png │ │ ├── 0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_outline.png │ │ ├── 1a1105ce-e53a-4cab-b728-27171ac56b6a_color.png │ │ ├── 1a1105ce-e53a-4cab-b728-27171ac56b6a_outline.png │ │ ├── adba4f0a-6084-4cb4-8721-5d4008abbd52_color.png │ │ └── adba4f0a-6084-4cb4-8721-5d4008abbd52_outline.png │ └── tsconfig.json └── library-components │ ├── README.md │ ├── spfx-library │ ├── .eslintrc.js │ ├── .gitignore │ ├── .npmignore │ ├── .vscode │ │ ├── launch.json │ │ └── settings.json │ ├── .yo-rc.json │ ├── README.md │ ├── config │ │ ├── config.json │ │ ├── deploy-azure-storage.json │ │ ├── package-solution.json │ │ ├── sass.json │ │ ├── serve.json │ │ └── write-manifests.json │ ├── fast-serve │ │ └── config.json │ ├── gulpfile.js │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── libraries │ │ │ └── myApi │ │ │ ├── MyApiLibrary.manifest.json │ │ │ ├── MyApiLibrary.ts │ │ │ └── loc │ │ │ ├── en-us.js │ │ │ └── mystrings.d.ts │ └── tsconfig.json │ └── spfx-webparts │ ├── .eslintrc.js │ ├── .gitignore │ ├── .npmignore │ ├── .vscode │ ├── launch.json │ └── settings.json │ ├── .yo-rc.json │ ├── README.md │ ├── config │ ├── config.json │ ├── deploy-azure-storage.json │ ├── package-solution.json │ ├── sass.json │ ├── serve.json │ └── write-manifests.json │ ├── gulpfile.js │ ├── package.json │ ├── src │ ├── index.ts │ └── webparts │ │ └── helloWorld │ │ ├── HelloWorldWebPart.manifest.json │ │ ├── HelloWorldWebPart.ts │ │ ├── assets │ │ ├── welcome-dark.png │ │ └── welcome-light.png │ │ ├── components │ │ ├── HelloWorld.module.scss │ │ ├── HelloWorld.tsx │ │ └── IHelloWorldProps.ts │ │ └── loc │ │ ├── en-us.js │ │ └── mystrings.d.ts │ ├── teams │ ├── 0da4216e-f0ff-4e10-8e67-c6e02680b941_color.png │ └── 0da4216e-f0ff-4e10-8e67-c6e02680b941_outline.png │ └── tsconfig.json ├── schema ├── config.1.0.schema.json ├── config.1.1.schema.json ├── config.latest.schema.json └── config.v2.schema.json ├── src ├── commands │ ├── BaseCommand.ts │ ├── Install.ts │ ├── PatchGulpFile.ts │ ├── PatchPackageJson.ts │ ├── Pipeline.ts │ └── index.ts ├── common │ ├── Logger.ts │ ├── consts.ts │ └── utils.ts ├── index.ts └── templates │ └── gulpfile.js └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": [ 5 | "@typescript-eslint" 6 | ], 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended" 11 | ], 12 | "ignorePatterns": [ 13 | "*.js" 14 | ], 15 | "rules": { 16 | "no-console": "error", 17 | "eqeqeq": "error", 18 | "@typescript-eslint/no-explicit-any": "off", 19 | "@typescript-eslint/interface-name-prefix": "off", 20 | "quotes": [ 21 | 2, 22 | "single" 23 | ], 24 | "@typescript-eslint/explicit-module-boundary-types": "off" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/no-response.yml: -------------------------------------------------------------------------------- 1 | name: No Response 2 | 3 | # Both `issue_comment` and `scheduled` event types are required for this Action 4 | # to work properly. 5 | on: 6 | issue_comment: 7 | types: [created] 8 | schedule: 9 | # Schedule for five minutes after the hour, every hour 10 | - cron: '5 0 * * *' 11 | 12 | jobs: 13 | noResponse: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: lee-dohm/no-response@v0.5.0 17 | with: 18 | token: ${{ github.token }} 19 | daysUntilClose: 14 20 | responseRequiredLabel: awaiting-response 21 | closeComment: This issue has been automatically closed because we haven't received any response back. Please feel free to reopen if needed. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | samples 2 | src -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll": "explicit" 4 | }, 5 | "explorer.compactFolders": false 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [4.0.2] - 20 Nov 2024 4 | 5 | ### Added 6 | 7 | - `--force-install` parameter to run dependencies installation automatically [#157](https://github.com/s-KaiNet/spfx-fast-serve/issues/157) 8 | 9 | ## [4.0.1] - 02 Apr 2024 10 | 11 | ### Fixed 12 | 13 | - problem with detecting installed SPFx version [#127](https://github.com/s-KaiNet/spfx-fast-serve/issues/127) 14 | 15 | ## [4.0.0] - 20 Dec 2023 16 | 17 | ### BREAKING CHANGES 18 | 19 | - drop support for older SPFx versions (less then 1.17) 20 | 21 | ### Features 22 | 23 | - less footprint on projects (by default the tool doesn't create `fast-serve` folder) 24 | - simplified `serve` command 25 | 26 | ## [3.0.7] - 17 Sep 2023 27 | 28 | ### Features 29 | 30 | - grammar fix 31 | - added additional webpack parameter to the extend function 32 | 33 | ## [3.0.6] - 19 Nov 2022 34 | 35 | ### Fixed 36 | 37 | - `-rc.[number]` now also recognized as beta SPFx version 38 | 39 | ## [3.0.5] - 11 Mar 2021 40 | 41 | ### Features 42 | 43 | - minor changes 44 | 45 | ## [3.0.4] - 11 Mar 2021 46 | 47 | ### Features 48 | 49 | - self-update checker 50 | 51 | ## [3.0.3] - 09 Dec 2021 52 | 53 | ### Features 54 | 55 | - automatic "beta" detection 56 | 57 | ## [3.0.2] - 10 Jun 2021 58 | 59 | ### Features 60 | 61 | - schema changed to support latest SPFx 62 | - minor improvements 63 | 64 | ## [3.0.1] - 08 Jun 2021 65 | 66 | ### Features 67 | 68 | - added `port` option to support more than one library component projects 69 | 70 | ### Features 71 | 72 | - moved all the logic into a separate package 73 | - deprecated `--usePnpm` (now supported OOB) and `--useRestProxy` (supported via webpack extensibility) 74 | - simplified SPFx migration process 75 | 76 | ## [2.1.1] - 09 May 2021 77 | 78 | ### Features 79 | 80 | - added a message about potential webpack modifications 81 | 82 | ## [2.1.0] - 08 May 2021 83 | 84 | ### Features 85 | 86 | - significantly simplified `gulpfile.js` merging 87 | 88 | ## [2.0.4] - 08 May 2021 89 | 90 | ### Features 91 | 92 | - "manual merge" was removed in favor of a better solution 93 | 94 | ## [2.0.3] - 06 May 2021 95 | 96 | ### Fixed 97 | 98 | - [CSS module system applied twice to third party js libs](https://github.com/s-KaiNet/spfx-fast-serve/issues/22). css-loader changed the default behavior for modules and now it's ON by default. 99 | 100 | ## [2.0.2] - 06 May 2021 101 | 102 | ### Features 103 | 104 | - simplified localized resource resolution process 105 | 106 | ## [2.0.1] - 05 May 2021 107 | 108 | ### Fixed 109 | 110 | - [a problem with localized resources inside library component](https://github.com/s-KaiNet/spfx-fast-serve/issues/21) 111 | 112 | ## [2.0.0] - 30 Apr 2021 113 | 114 | Full codebase re-write with TypeScript, better extensibility and SPFx 1.12 support. 115 | 116 | ## [1.11.0] - 13 Dec 2020 117 | 118 | ### Features 119 | 120 | - added better support for pnpm package manager 121 | 122 | ## [1.10.13] - 24 May 2020 123 | 124 | ### Fixed 125 | 126 | - wrong file loader Regexp 127 | 128 | ## [1.10.12] - 20 May 2020 129 | 130 | ### Features 131 | 132 | - added support for sp-rest-proxy 133 | 134 | ## [1.10.11] - 04 May 2020 135 | 136 | ### Features 137 | 138 | - simplified library components configuration 139 | 140 | ## [1.10.9 - 1.10.10] - 30 Apr 2020 141 | 142 | ### Features 143 | 144 | - added `writeToDisk:false` by default 145 | 146 | ### Fixed 147 | 148 | - prevent deletion of .manifest files from `/dist` since they needed for library components 149 | 150 | ## [1.10.7 - 1.10.8] - 29 Apr 2020 151 | 152 | ### Fixed 153 | 154 | - an error is thrown when trying to generate files for library components 155 | - simplified port settings inside webpack.js file 156 | 157 | ## [1.10.5 - 1.10.6] - 24 Apr 2020 158 | 159 | ### Fixed 160 | 161 | - webpack config for SPFx 1.7.x and 1.4.x 162 | - Bundles loading issue [#4](https://github.com/s-KaiNet/spfx-fast-serve/issues/4) 163 | 164 | ## [1.10.4] - 29 Mar 2020 165 | 166 | ### Features 167 | 168 | - refactored to support different SharePoint Framework versions in the same branch 169 | 170 | ## [1.10.3] - 28 Mar 2020 171 | 172 | ### Features 173 | 174 | - minimized webpack output - small performance gain 175 | 176 | ## [1.10.2] - 28 Mar 2020 177 | 178 | ### Fixed 179 | 180 | - VSCode Chrome debugging issue 181 | - css modules class names are not generated with original class name as a prefix 182 | - out of memory for some projects - fixed with cross-env and `NODE_OPTIONS=--max_old_space_size=4096` 183 | - `.scss` files were treated as modules, thus break css styling 184 | - on a clean project sometimes it throws `Cannot find module './.module.scss'` 185 | 186 | ## [1.10.1] - 24 Mar 2020 187 | 188 | ### Fixed 189 | 190 | - default `*.module.scss.ts` conflicts with webpack loader generated `*.module.scss.d.ts` 191 | 192 | ## [1.10.0] - 23 Mar 2020 193 | 194 | ### Initial release 195 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergei Sergeev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :rocket: SPFx Fast Serve Tool 2 | 3 | [![npm version](https://badge.fury.io/js/spfx-fast-serve.svg)](https://badge.fury.io/js/spfx-fast-serve) 4 | 5 | A command line utility, which modifies your SharePoint Framework solution, so that it runs continuous `serve` command 10-15x times faster, than the regular `gulp serve`. 6 | 7 | Curious how it works under the hood? Read my [blog post here](https://spblog.net/post/2020/03/24/spfx-overclockers-or-how-significantly-speed-up-the-gulp-serve-command). 8 | 9 | > [!IMPORTANT] 10 | > 11 | > `spfx-fast-serve` version `4.x` (current) supports SPFx starting from version 1.17. Read more [here](#which-sharepoint-framework-versions-are-supported) 12 | 13 | ## How to use 14 | 15 | 1. `npm install spfx-fast-serve -g` 16 | 2. Open a command line in a folder with your SharePoint Framework solution you want to speed up. 17 | 3. Run `spfx-fast-serve` and follow instructions. In most cases you shouldn't do anything specific and the CLI "just works". 18 | 4. Run `npm install` 19 | 5. Run `npm run serve` and enjoy the incredible speed of `serve` command! 20 | 21 | ## `fast-serve` CLI 22 | 23 | The `spfx-fast-serve` command simply adds necessary things to run your `serve` faster. Among them, it installs `spfx-fast-serve-helpers` NodeJS package. The package contains the `fast-serve` CLI, which does all the magic "serve" things. Each CLI option could be provided as a command line parameter or could be stored inside the `fast-serve` configuration file under `/fast-serve/config.json`. The config file is not created by default, but you could create it using `fast-serve` CLI [commands](#fast-serve-commands). 24 | 25 | > Since `fast-serve` is not a global CLI, but a part of the `spfx-fast-serve-helpers` module, you should use tools like [npx](https://docs.npmjs.com/cli/v10/commands/npx) to run `fast-serve` from command line. So instead of `fast-serve [options]`, you should run `npx fast-serve [options]`. When running from npm scripts (`package.json`) you don't need `npx`, as everything is resolved internally. 26 | 27 | ### `fast-serve` CLI options 28 | 29 | | option | type | defaults | description | 30 | |----------------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 31 | | `port` | integer | 4321 | HTTP port to use to serve the bundles | 32 | | `memory` | integer | 8192 | Memory limits for the dev server in MB | 33 | | `locale` | string | - | Local code when running in a multi-language scenario, i.e. `--locale=nl-nl` | 34 | | `config` | string | - | Serve configuration to run on a startup. It works exactly the same as the OOB `gulp serve --config=[config-name]` | 35 | | `openUrl` | string | - | URL to open on a startup. If empty, no URL will be opened. Supports SPFx {tenantDomain} placeholder | 36 | | `loggingLevel` | enum | normal | Logging level, 'minimal' notifies about errors and new builds only, 'normal' adds bundle information, 'detailed' displays maximum information about each bundle | 37 | | `fullScreenErrors` | boolean | true | Whether to show errors with a full-screen overlay on UI or not (only in console) | 38 | | `isLibraryComponent` | boolean | false | Should be true, when running inside library component project type | 39 | | `eslint` | boolean | true | When `true`, adds [eslint-webpack-plugin](https://github.com/webpack-contrib/eslint-webpack-plugin) to lint your code with `lintDirtyModulesOnly:true` option for performance | 40 | | `hotRefresh` | boolean | false | Enables webpack's [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/) (HMR). This feature is considered as experimental, meaning that you can try and use it if it works well for your project. Read [more here](/docs/HMR.md) | 41 | | `reactProfiling` | boolean | false | When `true`, enables react profiling mode through [React Chrome extension](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en). By default profiling doesn't work in SPFx solutions (even in dev mode). | 42 | | `containers` | boolean | false | Explicitly enables containerized environment support. By default, `fast-serve` automatically detects a containerized environment (like Docker) and applies needed configuration. But if it doesn't work for you, you can explicitly disable or enable support for containers using this option. | 43 | | `debug` | boolean | false | Enables debug mode for `fast-serve` | 44 | 45 | Here is a sample configuration: 46 | 47 | ```json 48 | { 49 | "$schema": "https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/master/schema/config.v2.schema.json", 50 | "serve": { 51 | "config": "my-config", 52 | "fullScreenErrors": false, 53 | "debug": true 54 | } 55 | } 56 | ``` 57 | 58 | If you call `fast-serve` with the above configuration file, it will be the equivalent of calling the CLI with the below parameters (taken from file): 59 | 60 | ```bash 61 | fast-serve --config=my-config --fullScreenErrors=false --debug 62 | ``` 63 | 64 | If you have the same option provided in both file and CLI, the CLI option will take the precedence. 65 | 66 | ### `fast-serve` commands 67 | 68 | `fast-serve` CLI supports below commands: 69 | 70 | - `fast-serve webpack extend` - adds fast-serve webpack extensibility file to the project. Read more on webpack extensibility [here](#webpack-extensibility) 71 | - `fast-serve config add` - adds `fast-serve` configuration file to the project 72 | 73 | ## Migration between SPFx versions 74 | 75 | The migration is as easy as just changing the version of `spfx-fast-serve-helpers` in your `package.json` to match the corresponding SPFx **minor** version (**do not** change the patch version). 76 | 77 | For example, if your project is based on SPFx 1.17, then you have the below dependency: 78 | > "spfx-fast-serve-helpers": "~1.17.0" 79 | 80 | To migrate `fast-serve` to SPFx 1.18 you just need to change it like this (patch version should be `0`, we change only minor version): 81 | > "spfx-fast-serve-helpers": "~1.18.0" 82 | 83 | Reinstall all dependencies and that's it! 84 | 85 | ## Webpack extensibility 86 | 87 | If you use custom webpack loaders or other webpack modifications via `build.configureWebpack.mergeConfig` feature, you should manually apply them to `webpack.extend.js` file created by the CLI to make everything work. Apply only those webpack modifications, which work on a regular `gulp serve` command, since `spfx-fast-serve` works only in development mode. 88 | 89 | By default, you don't have `webpack.extend.js` file. Run 90 | 91 | ```bash 92 | npx fast-serve webpack extend 93 | ``` 94 | 95 | to create it. In this file you can put your own logic for webpack, it will not be overwritten by the subsequent `spfx-fast-serve` calls. 96 | 97 | You can either provide custom `webpackConfig` object, which will be merged using [webpack-merge](https://github.com/survivejs/webpack-merge) module, or use `transformConfig` to even better control over configuration. 98 | 99 | Check out [this sample](https://github.com/s-KaiNet/spfx-fast-serve/blob/master/samples/advanced/fast-serve/webpack.extend.js) to see how it works. The sample configures custom path aliases for SPFx. 100 | 101 | ## Which SharePoint Framework versions are supported 102 | 103 | The latest `spfx-fast-serve@4.x` version supports SPFx 1.17 and onwards. 104 | 105 | Version `3.x` supports SPFx 1.4.1 and above. If you need to run the tool for SPFx < 1.17, you could use `npx` tool for `npm` or `dlx` for `pnpm`: 106 | 107 | ```bash 108 | npx -p spfx-fast-serve@3.0.7 -- spfx-fast-serve 109 | ``` 110 | 111 | ```bash 112 | pnpm --package=spfx-fast-serve@3.0.7 dlx spfx-fast-serve 113 | ``` 114 | 115 | You could also use [3.x branch](https://github.com/s-KaiNet/spfx-fast-serve/tree/3.x) to see the documentation for `3.x` version. 116 | 117 | SharePoint 2016 is **NOT** supported. 118 | 119 | ## MS Teams Toolkit integration 120 | 121 | Follow [this guide](./docs/MS%20Teams%20Toolkit.md) to configure MS Teams Toolkit with `fast-serve`. Also checkout the [sample repository](https://github.com/s-KaiNet/fast-serve-teams-tk) where everything is configured. 122 | 123 | ## `spfx-fast-serve` command options 124 | 125 | - `--force-install` - installs dependencies without asking for a confirmation 126 | 127 | ## How it works 128 | 129 | The tool adds necessary files to run your own webpack based build with webpack dev server. Technically it's a custom webpack build, which produces the same output files as SharePoint Framework build pipeline, but does it a lot faster, because of a number of improvements: 130 | 131 | - all compilation are done in a memory with webpack, no additional "copy", "prepare", "typescript", "whatever" tasks. 132 | - incremental TypeScript compilation when a file is being changed. It means only necessary files are compiled, not everything. 133 | - asynchronous type checking and linting. 134 | 135 | Also 136 | 137 | - live reloading for hosted workbench, MS Teams host, mobile devices (with ngrok serve) 138 | - debugging from VSCode with Chrome Debugger extension 139 | - supports WSL2 140 | - Hot Module Replacement (HMR) - experimental support 141 | - doesn't mess up your default SPFx build. If you have troubles, simply switch back to regular `gulp serve` 142 | - supports all major node package managers 143 | 144 | ## NGROK serve plugin 145 | 146 | `spfx-fast-serve` supports ngrok as a proxy between webpack dev server and SharePoint. This is possible through the *NgrokServePlugin* webpack plugin. This option allows you to test your SPFx solution live on mobile devices in development mode. 147 | 148 | Read more [here](/docs/NgrokServe.md) on how you can configure it. 149 | 150 | ## Library components 151 | 152 | Please use [this guide](/docs/LibraryComponents.md) to configure `spfx-fast-serve` with library components. 153 | 154 | ## Privacy policy 155 | 156 | `spfx-fast-serve` tracks every run using "fast serve" option. The "run" data includes time, when you run `npm run serve` and irreversible hash of computer name (to track unique computers). It **does NOT** collect nor store any personal, computer, network or project information. "Run" data needed to analyze, how many runs using "fast serve" scenario we have per day\month\year and what is the trend. Based on the data I can make a decision whether to further invest time into this project or not. 157 | 158 | ## Having troubles? Please try to find the answer under [FAQs](/docs/FAQ.md) or raise an issue 159 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQs / known issues 2 | 3 | - [1. When I run `npm run serve` I see the error](#1-when-i-run-npm-run-serve-i-see-the-error) 4 | - [2. After I applied `sfpx-fast-serve` tool I have formatting broken in `gulpfile.js`](#2-after-i-applied-sfpx-fast-serve-tool-i-have-formatting-broken-in-gulpfilejs) 5 | - [3. I added a new dependency in my solution (or started using new import from "@microsoft/\*" modules) and now I see some strange errors](#3-i-added-a-new-dependency-in-my-solution-or-started-using-new-import-from-microsoft-modules-and-now-i-see-some-strange-errors) 6 | - [4. Does it support React Hot Module Replacement (aka HMR)?](#4-does-it-support-react-hot-module-replacement-aka-hmr) 7 | - [5. How to debug with Chrome Debugger extension from VSCode?](#5-how-to-debug-with-chrome-debugger-extension-from-vscode) 8 | - [6. How to run with different locale?](#6-how-to-run-with-different-locale) 9 | 10 | ## 1. When I run `npm run serve` I see the error 11 | 12 | > `ERROR in .tsx Cannot find module './.module.scss'`: 13 | 14 | ![Error](../img/missing-module-error.png) 15 | 16 | *a*. Try to explicitly change and then save any of `.tsx` files in the solution in order to trigger the build. Maybe the error will disappear automatically. If not, go to `#b` 17 | 18 | *b*. Check that you use `styles` variable in `.tsx` file. For example, if you have `import styles from './.module.scss';` and you don't have usages of `styles` in your `.tsx`, you will see the error. Simply delete unused import. If it's not the case, goto `#c`. 19 | 20 | *c*. Maybe you don't have `.module.scss.d.ts` which is generated automatically. Request generation by going to `.module.scss` and explicitly saving the file using `Ctrl+S` or just by changing something and saving. This should generate `.module.scss.d.ts` and fix the issue. 21 | 22 | *d*. The last thing to check is whether your Component is actually used inside your codebase. If it's unused, then the corresponding `.module.scss.d.ts` will not be generated. To fix either include the component in your codebase using import statement or just run `fast-serve` from scratch (on the initial run it should ignore unused components). 23 | 24 | If nothing above works, please raise an [issue](https://github.com/s-KaiNet/spfx-fast-serve/issues). 25 | 26 | ## 2. After I applied `sfpx-fast-serve` tool I have formatting broken in `gulpfile.js` 27 | 28 | `sfpx-fast-serve` patches files and doesn't respect original file formatting (tabs vs whitespace, size, etc.). You have to fix it afterwards, if needed. 29 | 30 | ## 3. I added a new dependency in my solution (or started using new import from "@microsoft/*" modules) and now I see some strange errors 31 | 32 | Every time you introduce a new dependency for your solution, you should re-run `npm run serve` command, so that it picks up all new dependencies correctly. 33 | 34 | ## 4. Does it support React Hot Module Replacement (aka HMR)? 35 | 36 | HMR is supported, however considered as experimental. Please checkout config settings [option `hotRefresh`](../README.md#configuration-options). 37 | 38 | ## 5. How to debug with Chrome Debugger extension from VSCode? 39 | 40 | Just refer to the official [documentation](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/debug-in-vscode). The only difference is that instead of `gulp serve` you will use `npm run serve` 41 | 42 | ## 6. How to run with different locale? 43 | 44 | You have two options here. If you support only one or two additional locales, you can create additional npm serve scripts (inside `package.json`) with different locales support, i.e. 45 | 46 | ```json 47 | "serve-nl": "fast-serve --locale=nl-nl", 48 | ``` 49 | 50 | Take a note that I added `--locale=nl-nl` to support NL locale. 51 | 52 | Alternatively, if you need a lot of locales, you can create dynamic solution with environmental variables. To support this scenario, set a new environmental variable to be your locale code, i.e. 53 | 54 | ```bash 55 | set SPFX_LOCALE=nl-nl 56 | ``` 57 | 58 | Then install [`cross-env`](https://www.npmjs.com/package/cross-env) npm module: 59 | 60 | ```bash 61 | npm i cross-env --save-dev --save-exact 62 | ``` 63 | 64 | Then update npm script to use this variable: 65 | 66 | ```json 67 | "serve-loc": "cross-env-shell fast-serve --locale=$SPFX_LOCALE" 68 | ``` 69 | 70 | Take a note on `--locale=$SPFX_LOCALE` special syntax and using `cross-env-shell` (part of `cross-env` package, you don't need to install anything additionally). 71 | -------------------------------------------------------------------------------- /docs/HMR.md: -------------------------------------------------------------------------------- 1 | # HMR with SharePoint Framework 2 | 3 | ## Prerequisites 4 | 5 | ### SPFx 1.16+ 6 | 7 | The HMR feature was tested with SPFx 1.16. 8 | 9 | ## Full page refresh 10 | 11 | Sometimes it performs a full page refresh instead of a partial update. That's ok because the HMR feature is smart enough to detect when a partial update is available. If your component depends on external state or performs HTTP requests, then most likely a full-page refresh will be executed. Partial update works well only for small single-purpose components without lots of dependencies. 12 | 13 | ## Experimental 14 | 15 | At the time being this feature is marked as experimental, because I can't guarantee that it works on all different cases and configurations. Also, it relies on some nodejs modules, which are still in development. Thus my recommendation is to try it if you wish. If it works for your project - then use it. In future versions of SPFx, this feature should be more stable. 16 | -------------------------------------------------------------------------------- /docs/LibraryComponents.md: -------------------------------------------------------------------------------- 1 | # How to configure `spfx-fast-serve` with library components 2 | 3 | There are many libraries and built-in tools, which add convenient multi-packaged support for repository (Lerna, Rush, npm\pnpm\yarn workspaces and others). It's out of scope for this guide to cover all of them. Instead, it covers the most straightforward way of implementing the library components using just npm. 4 | 5 | ## Procedure 6 | 7 | Using this option you just need to scaffold library components, webpart project and add `spfx-fast-serve`. 8 | 9 | ### How to configure 10 | 11 | 1. Scaffold two projects - library component inside `spfx-library` folder (I will use `corporate-library` as a library component name) and regular web part project into another folder `spfx-webparts`. 12 | 13 | 2. Both in `spfx-webparts` and `spfx-library` folders run 14 | 15 | ```bash 16 | spfx-fast-serve 17 | ``` 18 | 19 | 3. Inside `spfx-library` run 20 | 21 | ```bash 22 | npx fast-serve config add 23 | ``` 24 | 25 | And update created config with below `serve` settings: 26 | 27 | - `port` is any free TCP port except `4321`, because it's the default for main SPFx web server. For example, you can use `4322`, `4323`, etc. The only rule is that you should use a unique port for every library component project per your SPFx solution 28 | - `isLibraryComponent` - true 29 | 30 | 4. In both folders run 31 | 32 | ```bash 33 | npm install 34 | ``` 35 | 36 | to restore dependencies (if not did before). 37 | 38 | 5. Inside `spfx-webparts` add dependency on `corporate-library` by running below command: 39 | 40 | ```bash 41 | npm install ../spfx-library --save 42 | ``` 43 | 44 | That's a special syntax, supported by npm, which installs dependency from parent **folder** name. It searches for `package.json` and adds corresponding dependency using symbolic links. 45 | 46 | 6. Run `npm run serve` inside `spfx-library` folder, wait till the full compilation, then `npm run serve` inside `spfx-webparts`. The order is important. 47 | -------------------------------------------------------------------------------- /docs/MS Teams Toolkit.md: -------------------------------------------------------------------------------- 1 | # How to integrate fast-serve with MS Teams Toolkit 2 | 3 | The pre-configured sample is available [here](https://github.com/s-KaiNet/fast-serve-teams-tk). You can also see [the commit details](https://github.com/s-KaiNet/fast-serve-teams-tk/commit/d8f50ccedccdfb600be221bbf30c17c4c80a5406) to figure out what was changed. Below is the detailed guide. 4 | 5 | 1. Configure your SPFx solution with `fast-serve` like you normally do. 6 | 7 | > [!NOTE] 8 | > You should run `spfx-fast-serve` from your SPFx solution folder, not from the root MS Teams Toolkit project. Normally SPFx solution is located inside `src` folder, i.e. `[your root MS Team Toolkit project]/src`. 9 | 10 | 2. In the root of your MS Teams Toolkit project, open `.vscode/tasks.json` and add two tasks: 11 | 12 | ```json 13 | { 14 | "label": "Start Teams App with fast serve", 15 | "dependsOn": [ 16 | "Validate prerequisites", 17 | "Provision", 18 | "fast serve" 19 | ], 20 | "dependsOrder": "sequence" 21 | } 22 | ``` 23 | 24 | and 25 | 26 | ```json 27 | { 28 | "label": "fast serve", 29 | "type": "shell", 30 | "command": "npx", 31 | "args": [ 32 | "fast-serve" 33 | ], 34 | "problemMatcher": [ 35 | { 36 | "pattern": [ 37 | { 38 | "regexp": ".", 39 | "file": 1, 40 | "location": 2, 41 | "message": 3 42 | } 43 | ], 44 | "background": { 45 | "activeOnStart": true, 46 | "beginsPattern": "^.*Starting gulp.*", 47 | "endsPattern": "^.*Compiled successfully|webpack compiled.*" 48 | } 49 | } 50 | ], 51 | "isBackground": true, 52 | "options": { 53 | "cwd": "${workspaceFolder}/src" 54 | } 55 | }, 56 | ``` 57 | 58 | By doing this we create fast-serve task config, which will be used by our launch configuration. 59 | 60 | 3. Open `.vscode/launch.json` and add two more commands under `compounds` array: 61 | 62 | ```json 63 | { 64 | "name": "Teams workbench (Edge) with fast serve", 65 | "configurations": [ 66 | "Start Teams workbench (Edge)" 67 | ], 68 | "preLaunchTask": "Start Teams App with fast serve", 69 | "presentation": { 70 | "group": "forteams", 71 | "order": 3 72 | }, 73 | "stopAll": true 74 | }, 75 | { 76 | "name": "Teams workbench (Chrome) with fast serve", 77 | "configurations": [ 78 | "Start Teams workbench (Chrome)" 79 | ], 80 | "preLaunchTask": "Start Teams App with fast serve", 81 | "presentation": { 82 | "group": "forteams", 83 | "order": 4 84 | }, 85 | "stopAll": true 86 | }, 87 | ``` 88 | 89 | Above code adds new configuration options for local debug scenarios. 90 | 91 | 4. Click on the MS Teams Toolkit extension in VSCode, under Environment select local -> debug. You will see two additional options: 92 | 93 | ![ms teams toolkit](../img/ms-teams-tk.png) 94 | 95 | Select the one you prefer. 96 | -------------------------------------------------------------------------------- /docs/Migrate-from-3-to-4.md: -------------------------------------------------------------------------------- 1 | # How to migrate your SPFx solution to spfx-fast-serve 4.x 2 | 3 | > **IMPORTANT** 4 | > 5 | > `spfx-fast-serve@4.x` supports SPFx 1.17+, you cannot migrate for earlier versions. 6 | 7 | ## Steps 8 | 9 | Good news - everything is fully backward-compatible! If you don't want to apply any steps, you could leave it as is. 10 | 11 | However, `spfx-fast-serve@4.x` introduced some improvements and simplicity, which is recommended to apply to your SPFx solution. 12 | 13 | 1. Fix `serve` command in `package.json` scripts. 14 | 15 | In version 3.x the command was `"gulp bundle --custom-serve --max_old_space_size=4096 && fast-serve"`, now you could use just `fast-serve`, everything is handled internally. If you have customized `serve` command, consider [available options](../README.md#fast-serve-cli-options) to migrate your customizations. 16 | 2. If you don't have any customizations inside `fast-serve/webpack.extend.js` file, you could delete it. 17 | 3. If you use just default [settings](../README.md#fast-serve-cli-options) inside `fast-serve/config.json` (like `"isLibraryComponent": false`), then you could also delete the whole config file. 18 | -------------------------------------------------------------------------------- /docs/NgrokServe.md: -------------------------------------------------------------------------------- 1 | # NGROK serve plugin 2 | 3 | That's a new webpack plugin from "spfx-fast-serve-helpers" module, aimed to make mobile testing a bit easier. 4 | 5 | ## The problem 6 | 7 | This is something completely new introduced for SPFx 1.13 and onwards. SPFx 1.13 added a new type of extension called [Adaptive Cards](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/viva/get-started/build-first-sharepoint-adaptive-card-extension). Mostly it's used inside Viva Connection Dashboards and mobile experiences. In the hosted workbench you can test adaptive cards. But what if you want to continue local development and see how it looks like inside your Viva MS Team's mobile app? Of course, you can use mobile view in dev tools, however on the actual device, it might be different. Or what if you have a bug, which is reproducible on a mobile device only? 8 | 9 | With `spfx-fast-serve` you can create a kind of proxy between your locally running dev server and Viva app, loading development javascript. You can use the [ngrok](https://ngrok.com/) tool as such a proxy and the _NgrokServePlugin_ from `spfx-fast-serve`. This approach works also for Teams Tabs if you wish to test them on mobile. 10 | 11 | ## How to configure 12 | 13 | ### Scaffold a new project 14 | 15 | You can use existing or just scaffold a brand new SPFx project with an adaptive card extension. Make sure to set 16 | _Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites?:_ **Yes.** 17 | 18 | ### Run ngrok 19 | 20 | Ngrok service will act as a proxy to our locally hosted javascript sources. So run ngrok first: 21 | 22 | ```bash 23 | ngrok http https://localhost:4321 24 | ``` 25 | 26 | You will see something like below: 27 | 28 | [![](/img/ngrok.jpg)](image) 29 | 30 | Take a note of ngrok host URL and save it, we'll need it later. In my case, it will be "_657f90095ec8.ngrok.io_". 31 | 32 | ### Update configuration 33 | 34 | Open _config/serve.json_ and change configuration -"port": 443, "hostname": "657f90095ec8.ngrok.io". This is the whole configuration (for the default scaffolded project): 35 | 36 | ```json 37 | { 38 | "$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json", 39 | "port": 443, 40 | "hostname": "657f90095ec8.ngrok.io", 41 | "https": true, 42 | "initialPage": "https://localhost:5432/workbench", 43 | "api": { 44 | "port": 5432, 45 | "entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/" 46 | } 47 | } 48 | ``` 49 | 50 | Now open _fast-serve/webpack.extend.js_ and add _NgrokServePlugin_ plugin (use the same host as before for the "host" plugin parameter): 51 | 52 | ```js 53 | const { NgrokServePlugin } = require("spfx-fast-serve-helpers"); 54 | 55 | // you can add your project related webpack configuration here, it will be merged using webpack-merge module 56 | // i.e. plugins: [new webpack.Plugin()] 57 | const webpackConfig = { 58 | plugins: [new NgrokServePlugin({ host: "657f90095ec8.ngrok.io" })] 59 | } 60 | ``` 61 | 62 | ### Build, package and deploy it 63 | 64 | Now you should build and package. The trick is to use dev build and not production (without --ship parameters). So run: 65 | 66 | ```bash 67 | gulp bundle 68 | gulp package-solution 69 | ``` 70 | 71 | Go to the tenant app catalog and globally deploy the app. 72 | 73 | Now, if you add the webpart or adaptive card to any page, it will request javascript sources from ngrok URL, which is mapped to your localhost server. 74 | 75 | ### Add to the Viva Dashboard and test it 76 | 77 | Inside the solution folder run 78 | 79 | ```bash 80 | npm run serve 81 | ``` 82 | 83 | Go to your Viva Dashboard and add the adaptive card. The card should be available and fully functional using a desktop browser. Usually the dashboard URL is /SitePages/Dashboard.aspx. 84 | 85 | If it works inside Dashboard, you can open the mobile app and see it in the mobile dashboard as well! Change the code and your changes will be reflected in both mobile and desktop dashboards. 86 | 87 | The below video demonstrates how it works: 88 | 89 | 90 | 91 | You can use this approach not only for adaptive cards but actually for any SPFx component. For example, you can see how your Teams tab looks like on the mobile Teams experience and update it live! 92 | -------------------------------------------------------------------------------- /docs/VersionMatrix.md: -------------------------------------------------------------------------------- 1 | # SharePoint Framework to `spfx-fast-serve-helpers` version matrix 2 | 3 | | SPFx version | `spfx-fast-serve-helpers` version | 4 | |-------------------|-------------------------------------------------------------------| 5 | | < 1.4.1 | not supported | 6 | | [1.4.1 - 1.8] | ~1.4.0 | 7 | | [1.9 - 1.11] | ~1.11.0 | 8 | | [1.12 - the latest] | ~[SPFx version].0, i.e. for SPFx 1.14 the version will be ~1.14.0 | 9 | -------------------------------------------------------------------------------- /img/missing-module-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/img/missing-module-error.png -------------------------------------------------------------------------------- /img/ms-teams-tk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/img/ms-teams-tk.png -------------------------------------------------------------------------------- /img/ngrok.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/img/ngrok.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spfx-fast-serve", 3 | "version": "4.0.2", 4 | "author": "Sergei Sergeev (https://github.com/s-KaiNet)", 5 | "description": "Improve your SharePoint Framework development by speeding up 'serve' command", 6 | "main": "lib/index.js", 7 | "bin": "lib/index.js", 8 | "engines": { 9 | "node": ">=16" 10 | }, 11 | "scripts": { 12 | "build": "npm run lint && tsc -p . && npm run copy", 13 | "copy": "cpy **/*.* ../../lib/templates --parents --cwd=src/templates", 14 | "lint": "eslint -c .eslintrc.json --ext .ts src", 15 | "prepublishOnly": "rimraf -- lib && npm run build", 16 | "dev": "npm run copy && concurrently \"npm run watch:ts\" \"npm run watch:template\"", 17 | "watch:ts": "tsc -p . --watch", 18 | "watch:template": "onchange \"src/templates/**/*.*\" -- npm run copy" 19 | }, 20 | "keywords": [ 21 | "sharepoint framework", 22 | "performance", 23 | "cli" 24 | ], 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/s-KaiNet/spfx-fast-serve.git" 28 | }, 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/s-KaiNet/spfx-fast-serve/issues" 32 | }, 33 | "homepage": "https://github.com/s-KaiNet/spfx-fast-serve", 34 | "dependencies": { 35 | "@types/cross-spawn": "6.0.6", 36 | "@types/node": "20.4.1", 37 | "@types/update-notifier": "5.1.0", 38 | "@types/yargs": "16.0.3", 39 | "chalk": "4.1.2", 40 | "cross-spawn": "7.0.3", 41 | "detect-indent": "6.1.0", 42 | "detect-package-manager": "3.0.1", 43 | "enquirer": "2.4.1", 44 | "log-symbols": "4.1.0", 45 | "replace-in-file": "7.0.2", 46 | "update-notifier": "5.1.0", 47 | "yargs": "16.2.0" 48 | }, 49 | "devDependencies": { 50 | "@typescript-eslint/eslint-plugin": "^4.18.0", 51 | "@typescript-eslint/parser": "^4.18.0", 52 | "concurrently": "^6.0.0", 53 | "cpy-cli": "^3.1.1", 54 | "eslint": "^7.22.0", 55 | "onchange": "^7.1.0", 56 | "rimraf": "^3.0.2", 57 | "typescript": "^4.2.3" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | release 2 | package-lock.json 3 | pnpm-lock.yaml -------------------------------------------------------------------------------- /samples/advanced/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 -------------------------------------------------------------------------------- /samples/advanced/.eslintrc.js: -------------------------------------------------------------------------------- 1 | require('@rushstack/eslint-config/patch/modern-module-resolution'); 2 | module.exports = { 3 | extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'], 4 | ignorePatterns: ["*.js", "*.d.ts"], 5 | parserOptions: { tsconfigRootDir: __dirname }, 6 | overrides: [ 7 | { 8 | files: ['*.ts', '*.tsx'], 9 | parser: '@typescript-eslint/parser', 10 | 'parserOptions': { 11 | 'project': './tsconfig.json', 12 | 'ecmaVersion': 2018, 13 | 'sourceType': 'module' 14 | }, 15 | rules: { 16 | // Prevent usage of the JavaScript null value, while allowing code to access existing APIs that may require null. https://www.npmjs.com/package/@rushstack/eslint-plugin 17 | '@rushstack/no-new-null': 1, 18 | // Require Jest module mocking APIs to be called before any other statements in their code block. https://www.npmjs.com/package/@rushstack/eslint-plugin 19 | '@rushstack/hoist-jest-mock': 1, 20 | // Require regular expressions to be constructed from string constants rather than dynamically building strings at runtime. https://www.npmjs.com/package/@rushstack/eslint-plugin-security 21 | '@rushstack/security/no-unsafe-regexp': 1, 22 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 23 | '@typescript-eslint/adjacent-overload-signatures': 1, 24 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 25 | // 26 | // RATIONALE: Code is more readable when the type of every variable is immediately obvious. 27 | // Even if the compiler may be able to infer a type, this inference will be unavailable 28 | // to a person who is reviewing a GitHub diff. This rule makes writing code harder, 29 | // but writing code is a much less important activity than reading it. 30 | // 31 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 32 | '@typescript-eslint/explicit-function-return-type': [ 33 | 1, 34 | { 35 | 'allowExpressions': true, 36 | 'allowTypedFunctionExpressions': true, 37 | 'allowHigherOrderFunctions': false 38 | } 39 | ], 40 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 41 | // Rationale to disable: although this is a recommended rule, it is up to dev to select coding style. 42 | // Set to 1 (warning) or 2 (error) to enable. 43 | '@typescript-eslint/explicit-member-accessibility': 0, 44 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 45 | '@typescript-eslint/no-array-constructor': 1, 46 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 47 | // 48 | // RATIONALE: The "any" keyword disables static type checking, the main benefit of using TypeScript. 49 | // This rule should be suppressed only in very special cases such as JSON.stringify() 50 | // where the type really can be anything. Even if the type is flexible, another type 51 | // may be more appropriate such as "unknown", "{}", or "Record". 52 | '@typescript-eslint/no-explicit-any': 1, 53 | // RATIONALE: The #1 rule of promises is that every promise chain must be terminated by a catch() 54 | // handler. Thus wherever a Promise arises, the code must either append a catch handler, 55 | // or else return the object to a caller (who assumes this responsibility). Unterminated 56 | // promise chains are a serious issue. Besides causing errors to be silently ignored, 57 | // they can also cause a NodeJS process to terminate unexpectedly. 58 | '@typescript-eslint/no-floating-promises': 0, 59 | // RATIONALE: Catches a common coding mistake. 60 | '@typescript-eslint/no-for-in-array': 2, 61 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 62 | '@typescript-eslint/no-misused-new': 2, 63 | // RATIONALE: The "namespace" keyword is not recommended for organizing code because JavaScript lacks 64 | // a "using" statement to traverse namespaces. Nested namespaces prevent certain bundler 65 | // optimizations. If you are declaring loose functions/variables, it's better to make them 66 | // static members of a class, since classes support property getters and their private 67 | // members are accessible by unit tests. Also, the exercise of choosing a meaningful 68 | // class name tends to produce more discoverable APIs: for example, search+replacing 69 | // the function "reverse()" is likely to return many false matches, whereas if we always 70 | // write "Text.reverse()" is more unique. For large scale organization, it's recommended 71 | // to decompose your code into separate NPM packages, which ensures that component 72 | // dependencies are tracked more conscientiously. 73 | // 74 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 75 | '@typescript-eslint/no-namespace': [ 76 | 1, 77 | { 78 | 'allowDeclarations': false, 79 | 'allowDefinitionFiles': false 80 | } 81 | ], 82 | // RATIONALE: Parameter properties provide a shorthand such as "constructor(public title: string)" 83 | // that avoids the effort of declaring "title" as a field. This TypeScript feature makes 84 | // code easier to write, but arguably sacrifices readability: In the notes for 85 | // "@typescript-eslint/member-ordering" we pointed out that fields are central to 86 | // a class's design, so we wouldn't want to bury them in a constructor signature 87 | // just to save some typing. 88 | // 89 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 90 | // Set to 1 (warning) or 2 (error) to enable the rule 91 | '@typescript-eslint/parameter-properties': 0, 92 | // RATIONALE: When left in shipping code, unused variables often indicate a mistake. Dead code 93 | // may impact performance. 94 | // 95 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 96 | '@typescript-eslint/no-unused-vars': [ 97 | 1, 98 | { 99 | 'vars': 'all', 100 | // Unused function arguments often indicate a mistake in JavaScript code. However in TypeScript code, 101 | // the compiler catches most of those mistakes, and unused arguments are fairly common for type signatures 102 | // that are overriding a base class method or implementing an interface. 103 | 'args': 'none' 104 | } 105 | ], 106 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 107 | '@typescript-eslint/no-use-before-define': [ 108 | 2, 109 | { 110 | 'functions': false, 111 | 'classes': true, 112 | 'variables': true, 113 | 'enums': true, 114 | 'typedefs': true 115 | } 116 | ], 117 | // Disallows require statements except in import statements. 118 | // In other words, the use of forms such as var foo = require("foo") are banned. Instead use ES6 style imports or import foo = require("foo") imports. 119 | '@typescript-eslint/no-var-requires': 'error', 120 | // RATIONALE: The "module" keyword is deprecated except when describing legacy libraries. 121 | // 122 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 123 | '@typescript-eslint/prefer-namespace-keyword': 1, 124 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 125 | // Rationale to disable: it's up to developer to decide if he wants to add type annotations 126 | // Set to 1 (warning) or 2 (error) to enable the rule 127 | '@typescript-eslint/no-inferrable-types': 0, 128 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 129 | // Rationale to disable: declaration of empty interfaces may be helpful for generic types scenarios 130 | '@typescript-eslint/no-empty-interface': 0, 131 | // RATIONALE: This rule warns if setters are defined without getters, which is probably a mistake. 132 | 'accessor-pairs': 1, 133 | // RATIONALE: In TypeScript, if you write x["y"] instead of x.y, it disables type checking. 134 | 'dot-notation': [ 135 | 1, 136 | { 137 | 'allowPattern': '^_' 138 | } 139 | ], 140 | // RATIONALE: Catches code that is likely to be incorrect 141 | 'eqeqeq': 1, 142 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 143 | 'for-direction': 1, 144 | // RATIONALE: Catches a common coding mistake. 145 | 'guard-for-in': 2, 146 | // RATIONALE: If you have more than 2,000 lines in a single source file, it's probably time 147 | // to split up your code. 148 | 'max-lines': ['warn', { max: 2000 }], 149 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 150 | 'no-async-promise-executor': 2, 151 | // RATIONALE: Deprecated language feature. 152 | 'no-caller': 2, 153 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 154 | 'no-compare-neg-zero': 2, 155 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 156 | 'no-cond-assign': 2, 157 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 158 | 'no-constant-condition': 1, 159 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 160 | 'no-control-regex': 2, 161 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 162 | 'no-debugger': 1, 163 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 164 | 'no-delete-var': 2, 165 | // RATIONALE: Catches code that is likely to be incorrect 166 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 167 | 'no-duplicate-case': 2, 168 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 169 | 'no-empty': 1, 170 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 171 | 'no-empty-character-class': 2, 172 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 173 | 'no-empty-pattern': 1, 174 | // RATIONALE: Eval is a security concern and a performance concern. 175 | 'no-eval': 1, 176 | // RATIONALE: Catches code that is likely to be incorrect 177 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 178 | 'no-ex-assign': 2, 179 | // RATIONALE: System types are global and should not be tampered with in a scalable code base. 180 | // If two different libraries (or two versions of the same library) both try to modify 181 | // a type, only one of them can win. Polyfills are acceptable because they implement 182 | // a standardized interoperable contract, but polyfills are generally coded in plain 183 | // JavaScript. 184 | 'no-extend-native': 1, 185 | // Disallow unnecessary labels 186 | 'no-extra-label': 1, 187 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 188 | 'no-fallthrough': 2, 189 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 190 | 'no-func-assign': 1, 191 | // RATIONALE: Catches a common coding mistake. 192 | 'no-implied-eval': 2, 193 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 194 | 'no-invalid-regexp': 2, 195 | // RATIONALE: Catches a common coding mistake. 196 | 'no-label-var': 2, 197 | // RATIONALE: Eliminates redundant code. 198 | 'no-lone-blocks': 1, 199 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 200 | 'no-misleading-character-class': 2, 201 | // RATIONALE: Catches a common coding mistake. 202 | 'no-multi-str': 2, 203 | // RATIONALE: It's generally a bad practice to call "new Thing()" without assigning the result to 204 | // a variable. Either it's part of an awkward expression like "(new Thing()).doSomething()", 205 | // or else implies that the constructor is doing nontrivial computations, which is often 206 | // a poor class design. 207 | 'no-new': 1, 208 | // RATIONALE: Obsolete language feature that is deprecated. 209 | 'no-new-func': 2, 210 | // RATIONALE: Obsolete language feature that is deprecated. 211 | 'no-new-object': 2, 212 | // RATIONALE: Obsolete notation. 213 | 'no-new-wrappers': 1, 214 | // RATIONALE: Catches code that is likely to be incorrect 215 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 216 | 'no-octal': 2, 217 | // RATIONALE: Catches code that is likely to be incorrect 218 | 'no-octal-escape': 2, 219 | // RATIONALE: Catches code that is likely to be incorrect 220 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 221 | 'no-regex-spaces': 2, 222 | // RATIONALE: Catches a common coding mistake. 223 | 'no-return-assign': 2, 224 | // RATIONALE: Security risk. 225 | 'no-script-url': 1, 226 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 227 | 'no-self-assign': 2, 228 | // RATIONALE: Catches a common coding mistake. 229 | 'no-self-compare': 2, 230 | // RATIONALE: This avoids statements such as "while (a = next(), a && a.length);" that use 231 | // commas to create compound expressions. In general code is more readable if each 232 | // step is split onto a separate line. This also makes it easier to set breakpoints 233 | // in the debugger. 234 | 'no-sequences': 1, 235 | // RATIONALE: Catches code that is likely to be incorrect 236 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 237 | 'no-shadow-restricted-names': 2, 238 | // RATIONALE: Obsolete language feature that is deprecated. 239 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 240 | 'no-sparse-arrays': 2, 241 | // RATIONALE: Although in theory JavaScript allows any possible data type to be thrown as an exception, 242 | // such flexibility adds pointless complexity, by requiring every catch block to test 243 | // the type of the object that it receives. Whereas if catch blocks can always assume 244 | // that their object implements the "Error" contract, then the code is simpler, and 245 | // we generally get useful additional information like a call stack. 246 | 'no-throw-literal': 2, 247 | // RATIONALE: Catches a common coding mistake. 248 | 'no-unmodified-loop-condition': 1, 249 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 250 | 'no-unsafe-finally': 2, 251 | // RATIONALE: Catches a common coding mistake. 252 | 'no-unused-expressions': 1, 253 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 254 | 'no-unused-labels': 1, 255 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 256 | 'no-useless-catch': 1, 257 | // RATIONALE: Avoids a potential performance problem. 258 | 'no-useless-concat': 1, 259 | // RATIONALE: The "var" keyword is deprecated because of its confusing "hoisting" behavior. 260 | // Always use "let" or "const" instead. 261 | // 262 | // STANDARDIZED BY: @typescript-eslint\eslint-plugin\dist\configs\recommended.json 263 | 'no-var': 2, 264 | // RATIONALE: Generally not needed in modern code. 265 | 'no-void': 1, 266 | // RATIONALE: Obsolete language feature that is deprecated. 267 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 268 | 'no-with': 2, 269 | // RATIONALE: Makes logic easier to understand, since constants always have a known value 270 | // @typescript-eslint\eslint-plugin\dist\configs\eslint-recommended.js 271 | 'prefer-const': 1, 272 | // RATIONALE: Catches a common coding mistake where "resolve" and "reject" are confused. 273 | 'promise/param-names': 2, 274 | // RATIONALE: Catches code that is likely to be incorrect 275 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 276 | 'require-atomic-updates': 2, 277 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 278 | 'require-yield': 1, 279 | // "Use strict" is redundant when using the TypeScript compiler. 280 | 'strict': [ 281 | 2, 282 | 'never' 283 | ], 284 | // RATIONALE: Catches code that is likely to be incorrect 285 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 286 | 'use-isnan': 2, 287 | // STANDARDIZED BY: eslint\conf\eslint-recommended.js 288 | // Set to 1 (warning) or 2 (error) to enable. 289 | // Rationale to disable: !!{} 290 | 'no-extra-boolean-cast': 0, 291 | // ==================================================================== 292 | // @microsoft/eslint-plugin-spfx 293 | // ==================================================================== 294 | '@microsoft/spfx/import-requires-chunk-name': 1, 295 | '@microsoft/spfx/no-require-ensure': 2, 296 | '@microsoft/spfx/pair-react-dom-render-unmount': 1 297 | } 298 | }, 299 | { 300 | // For unit tests, we can be a little bit less strict. The settings below revise the 301 | // defaults specified in the extended configurations, as well as above. 302 | files: [ 303 | // Test files 304 | '*.test.ts', 305 | '*.test.tsx', 306 | '*.spec.ts', 307 | '*.spec.tsx', 308 | 309 | // Facebook convention 310 | '**/__mocks__/*.ts', 311 | '**/__mocks__/*.tsx', 312 | '**/__tests__/*.ts', 313 | '**/__tests__/*.tsx', 314 | 315 | // Microsoft convention 316 | '**/test/*.ts', 317 | '**/test/*.tsx' 318 | ], 319 | rules: {} 320 | } 321 | ] 322 | }; 323 | -------------------------------------------------------------------------------- /samples/advanced/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules 8 | 9 | # Build generated files 10 | dist 11 | lib 12 | solution 13 | temp 14 | *.sppkg 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | # Visual Studio files 23 | .ntvs_analysis.dat 24 | .vs 25 | bin 26 | obj 27 | 28 | # Resx Generated Code 29 | *.resx.ts 30 | 31 | # Styles Generated Code 32 | *.scss.ts 33 | *.scss.d.ts 34 | .heft 35 | -------------------------------------------------------------------------------- /samples/advanced/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "msjsdiag.debugger-for-chrome" 4 | ] 5 | } -------------------------------------------------------------------------------- /samples/advanced/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Hosted workbench", 6 | "type": "msedge", 7 | "request": "launch", 8 | "url": "https://{tenantDomain}/_layouts/workbench.aspx", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///.././src/*": "${webRoot}/src/*", 13 | "webpack:///../../../src/*": "${webRoot}/src/*", 14 | "webpack:///../../../../src/*": "${webRoot}/src/*", 15 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 16 | }, 17 | "runtimeArgs": [ 18 | "--remote-debugging-port=9222", 19 | "-incognito" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /samples/advanced/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Configure glob patterns for excluding files and folders in the file explorer. 4 | "files.exclude": { 5 | "**/.git": true, 6 | "**/.DS_Store": true, 7 | "**/bower_components": true, 8 | "**/coverage": true, 9 | "**/lib-amd": true, 10 | "src/**/*.scss.ts": true, 11 | "**/jest-output": true 12 | }, 13 | "typescript.tsdk": ".\\node_modules\\typescript\\lib" 14 | } 15 | -------------------------------------------------------------------------------- /samples/advanced/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@microsoft/generator-sharepoint": { 3 | "isCreatingSolution": false, 4 | "environment": "spo", 5 | "version": "1.20.0", 6 | "libraryName": "fast-serve-advanced-sample", 7 | "libraryId": "bf8d902b-0414-4a7a-992d-d5632b945ff0", 8 | "packageManager": "npm", 9 | "isDomainIsolated": false, 10 | "plusBeta": false, 11 | "templateType": "Image", 12 | "nodeVersion": "18.17.1", 13 | "sdkVersions": { 14 | "@microsoft/microsoft-graph-client": "3.0.2", 15 | "@microsoft/teams-js": "2.24.0" 16 | }, 17 | "componentType": "webpart" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /samples/advanced/README.md: -------------------------------------------------------------------------------- 1 | # Advanced sample 2 | 3 | ![SPFx](https://img.shields.io/badge/SPFx-1.20.0-green.svg) 4 | 5 | That's an advanced SharePoint Framework and `spfx-fast-serve` integration, which includes below features: 6 | 7 | - custom webpack modifications 8 | - using multiple locales 9 | - using custom bundles, which includes multiple web parts (checkout `./config/config.json`) 10 | - using third party libraries ([pnpjs](https://pnp.github.io/pnpjs/), [Reusable React controls for SPFx](https://pnp.github.io/sp-dev-fx-controls-react/)) 11 | - custom fonts and icons ([react-fontawesome](https://github.com/FortAwesome/react-fontawesome)) 12 | - loading images into a web part 13 | - Microsoft Graph Toolkit 4.x 14 | - dynamic imports (async imports) 15 | 16 | ## How to run 17 | 18 | 1. Restore dependencies `npm install` 19 | 2. `npm run serve` 20 | -------------------------------------------------------------------------------- /samples/advanced/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", 3 | "version": "2.0", 4 | "bundles": { 5 | "bundle-pack-1": { 6 | "components": [ 7 | { 8 | "entrypoint": "./lib/webparts/contextInfo/ContextInfoWebPart.js", 9 | "manifest": "./src/webparts/contextInfo/ContextInfoWebPart.manifest.json" 10 | }, 11 | { 12 | "entrypoint": "./lib/webparts/helloWorld/HelloWorldWebPart.js", 13 | "manifest": "./src/webparts/helloWorld/HelloWorldWebPart.manifest.json" 14 | } 15 | ] 16 | }, 17 | "sample-web-part-web-part": { 18 | "components": [ 19 | { 20 | "entrypoint": "./lib/webparts/sampleWebPart/SampleWebPartWebPart.js", 21 | "manifest": "./src/webparts/sampleWebPart/SampleWebPartWebPart.manifest.json" 22 | } 23 | ] 24 | }, 25 | "my-card-adaptive-card-extension": { 26 | "components": [ 27 | { 28 | "entrypoint": "./lib/adaptiveCardExtensions/myCard/MyCardAdaptiveCardExtension.js", 29 | "manifest": "./src/adaptiveCardExtensions/myCard/MyCardAdaptiveCardExtension.manifest.json" 30 | } 31 | ] 32 | }, 33 | "graph-toolkit-test-web-part": { 34 | "components": [ 35 | { 36 | "entrypoint": "./lib/webparts/graphToolkitTest/GraphToolkitTestWebPart.js", 37 | "manifest": "./src/webparts/graphToolkitTest/GraphToolkitTestWebPart.manifest.json" 38 | } 39 | ] 40 | } 41 | }, 42 | "externals": { 43 | "marked": "node_modules/marked/marked.min.js" 44 | }, 45 | "localizedResources": { 46 | "ContextInfoWebPartStrings": "lib/webparts/contextInfo/loc/{locale}.js", 47 | "HelloWorldWebPartStrings": "lib/webparts/helloWorld/loc/{locale}.js", 48 | "SampleWebPartWebPartStrings": "lib/webparts/sampleWebPart/loc/{locale}.js", 49 | "ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js", 50 | "MyCardAdaptiveCardExtensionStrings": "lib/adaptiveCardExtensions/myCard/loc/{locale}.js", 51 | "GraphToolkitTestWebPartStrings": "lib/webparts/graphToolkitTest/loc/{locale}.js" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /samples/advanced/config/copy-assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json", 3 | "deployCdnPath": "temp/deploy" 4 | } 5 | -------------------------------------------------------------------------------- /samples/advanced/config/deploy-azure-storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", 3 | "workingDir": "./temp/deploy/", 4 | "account": "", 5 | "container": "fast-serve-advanced-sample", 6 | "accessKey": "" 7 | } -------------------------------------------------------------------------------- /samples/advanced/config/package-solution.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", 3 | "solution": { 4 | "name": "fast-serve-advanced-sample-client-side-solution", 5 | "id": "bf8d902b-0414-4a7a-992d-d5632b945ff0", 6 | "version": "1.0.0.0", 7 | "includeClientSideAssets": true, 8 | "skipFeatureDeployment": true, 9 | "isDomainIsolated": false, 10 | "webApiPermissionRequests": [ 11 | { 12 | "resource": "Microsoft Graph", 13 | "scope": "User.Read" 14 | } 15 | ], 16 | "developer": { 17 | "name": "", 18 | "websiteUrl": "", 19 | "privacyUrl": "", 20 | "termsOfUseUrl": "", 21 | "mpnId": "" 22 | }, 23 | "metadata": { 24 | "shortDescription": { 25 | "default": "fast-serve-advanced-sample description" 26 | }, 27 | "longDescription": { 28 | "default": "fast-serve-advanced-sample description" 29 | }, 30 | "screenshotPaths": [], 31 | "videoUrl": "", 32 | "categories": [] 33 | }, 34 | "features": [ 35 | { 36 | "title": "fast-serve-advanced-sample Feature", 37 | "description": "The feature that activates elements of the fast-serve-advanced-sample solution.", 38 | "id": "83a54f7e-9efe-4fb2-bcb7-d18f9ad97fac", 39 | "version": "1.0.0.0" 40 | } 41 | ] 42 | }, 43 | "paths": { 44 | "zippedPackage": "solution/fast-serve-advanced-sample.sppkg" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/advanced/config/sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json" 3 | } 4 | -------------------------------------------------------------------------------- /samples/advanced/config/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", 3 | "port": 4321, 4 | "https": true, 5 | "initialPage": "https://{tenantDomain}/_layouts/workbench.aspx" 6 | } 7 | -------------------------------------------------------------------------------- /samples/advanced/config/write-manifests.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json", 3 | "cdnBasePath": "" 4 | } -------------------------------------------------------------------------------- /samples/advanced/fast-serve/webpack.extend.js: -------------------------------------------------------------------------------- 1 | /* 2 | * User webpack settings file. You can add your own settings here. 3 | * Changes from this file will be merged into the base webpack configuration file. 4 | * This file will not be overwritten by the subsequent spfx-fast-serve calls. 5 | */ 6 | 7 | const path = require("path"); 8 | 9 | // you can add your project related webpack configuration here, it will be merged using webpack-merge module 10 | // i.e. plugins: [new webpack.Plugin()] 11 | const webpackConfig = { 12 | resolve: { 13 | alias: { 14 | "@src": path.resolve(__dirname, "..", "src") 15 | } 16 | }, 17 | // module: { 18 | // rules: [{ 19 | // test: /\.js$/, 20 | // include: [ 21 | // /lit/, 22 | // /@lit/, 23 | // /lit-html/ 24 | // ], 25 | // use: { 26 | // loader: 'babel-loader', 27 | // options: { 28 | // plugins: [ 29 | // '@babel/plugin-transform-optional-chaining', 30 | // '@babel/plugin-transform-nullish-coalescing-operator', 31 | // '@babel/plugin-transform-logical-assignment-operators' 32 | // ] 33 | // } 34 | // } 35 | // }] 36 | // } 37 | } 38 | 39 | // for even more fine-grained control, you can apply custom webpack settings using below function 40 | const transformConfig = function (initialWebpackConfig) { 41 | // transform the initial webpack config here, i.e. 42 | // initialWebpackConfig.plugins.push(new webpack.Plugin()); etc. 43 | 44 | return initialWebpackConfig; 45 | } 46 | 47 | module.exports = { 48 | webpackConfig, 49 | transformConfig 50 | } 51 | -------------------------------------------------------------------------------- /samples/advanced/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('@microsoft/sp-build-web'); 4 | 5 | build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); 6 | 7 | const path = require('path'); 8 | 9 | build.tslintCmd.enabled = false; 10 | 11 | build.configureWebpack.mergeConfig({ 12 | additionalConfiguration: (generatedConfiguration) => { 13 | if (!generatedConfiguration.resolve.alias) { 14 | generatedConfiguration.resolve.alias = {}; 15 | } 16 | 17 | //root src folder 18 | generatedConfiguration.resolve.alias['@src'] = path.resolve(__dirname, 'lib') 19 | 20 | // mgt related loaders 21 | // generatedConfiguration.module.rules.push({ 22 | // test: /\.js$/, 23 | // // only run on lit packages in the root node_module folder 24 | // include: [ 25 | // /lit/, 26 | // /@lit/, 27 | // /lit-html/ 28 | // ], 29 | // use: { 30 | // loader: "babel-loader", 31 | // options: { 32 | // plugins: [ 33 | // "@babel/plugin-transform-optional-chaining", 34 | // "@babel/plugin-transform-nullish-coalescing-operator", 35 | // "@babel/plugin-transform-logical-assignment-operators" 36 | // ] 37 | // } 38 | // } 39 | // }); 40 | 41 | return generatedConfiguration; 42 | } 43 | }); 44 | 45 | var getTasks = build.rig.getTasks; 46 | build.rig.getTasks = function () { 47 | var result = getTasks.call(build.rig); 48 | 49 | result.set('serve', result.get('serve-deprecated')); 50 | 51 | return result; 52 | }; 53 | 54 | /* fast-serve */ 55 | const { addFastServe } = require("spfx-fast-serve-helpers"); 56 | addFastServe(build); 57 | /* end of fast-serve */ 58 | 59 | build.initialize(require('gulp')); 60 | -------------------------------------------------------------------------------- /samples/advanced/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fast-serve-advanced-sample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "main": "lib/index.js", 6 | "engines": { 7 | "node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0" 8 | }, 9 | "scripts": { 10 | "build": "gulp bundle", 11 | "clean": "gulp clean", 12 | "test": "gulp test", 13 | "serve-nl": "fast-serve --locale=nl-nl", 14 | "serve": "fast-serve", 15 | "serve-loc": "cross-env-shell fast-serve --locale=$SPFX_LOCALE" 16 | }, 17 | "dependencies": { 18 | "@fluentui/react": "^8.119.1", 19 | "@fortawesome/fontawesome-svg-core": "6.5.2", 20 | "@fortawesome/free-solid-svg-icons": "6.5.2", 21 | "@fortawesome/react-fontawesome": "0.2.0", 22 | "@lit/task": "1.0.1", 23 | "@microsoft/mgt-components": "4.4.0", 24 | "@microsoft/mgt-element": "4.4.0", 25 | "@microsoft/mgt-react": "4.4.0", 26 | "@microsoft/mgt-sharepoint-provider": "4.4.0", 27 | "@microsoft/mgt-spfx-utils": "4.4.0", 28 | "@microsoft/microsoft-graph-client": "3.0.2", 29 | "@microsoft/sp-adaptive-card-extension-base": "1.20.0", 30 | "@microsoft/sp-component-base": "1.20.0", 31 | "@microsoft/sp-core-library": "1.20.0", 32 | "@microsoft/sp-http": "1.20.0", 33 | "@microsoft/sp-lodash-subset": "1.20.0", 34 | "@microsoft/sp-office-ui-fabric-core": "1.20.0", 35 | "@microsoft/sp-property-pane": "1.20.0", 36 | "@microsoft/sp-webpart-base": "1.20.0", 37 | "@pnp/sp": "3.21.0", 38 | "@pnp/spfx-controls-react": "3.18.1", 39 | "marked": "2.1.3", 40 | "react": "17.0.1", 41 | "react-dom": "17.0.1", 42 | "tslib": "2.6.3" 43 | }, 44 | "devDependencies": { 45 | "@babel/plugin-transform-logical-assignment-operators": "7.24.7", 46 | "@babel/plugin-transform-nullish-coalescing-operator": "7.24.7", 47 | "@babel/plugin-transform-optional-chaining": "7.24.7", 48 | "@babel/preset-env": "7.24.7", 49 | "@microsoft/eslint-config-spfx": "1.20.2", 50 | "@microsoft/eslint-plugin-spfx": "1.20.2", 51 | "@microsoft/rush-stack-compiler-4.7": "0.1.0", 52 | "@microsoft/sp-build-web": "1.20.2", 53 | "@microsoft/sp-module-interfaces": "1.20.2", 54 | "@rushstack/eslint-config": "4.0.1", 55 | "@types/marked": "^2.0.3", 56 | "@types/react": "17.0.45", 57 | "@types/react-dom": "17.0.17", 58 | "@types/webpack-env": "1.18.5", 59 | "ajv": "6.12.5", 60 | "babel-loader": "8.2.5", 61 | "cross-env": "7.0.3", 62 | "eslint": "8.57.0", 63 | "eslint-plugin-react": "^7.34.3", 64 | "eslint-plugin-react-hooks": "4.6.2", 65 | "gulp": "~4.0.2", 66 | "spfx-fast-serve-helpers": "1.20.2", 67 | "typescript": "4.7.4" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/MyCardAdaptiveCardExtension.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "7db55053-0f3c-4cbc-9b7f-db386ca68153", 4 | "alias": "MyCardAdaptiveCardExtension", 5 | "componentType": "AdaptiveCardExtension", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["Dashboard", "SharePointWebPart"], 16 | "supportsThemeVariants": true, 17 | "preconfiguredEntries": [{ 18 | "groupId": "bd067b1e-3ad5-4d5d-a5fe-505f07d7f59c", // Dashboard 19 | "group": { "default": "Dashboard" }, 20 | "title": { "default": "MyCard" }, 21 | "description": { "default": "MyCard description" }, 22 | "officeFabricIconFontName": "SharePointLogo", 23 | "properties": { 24 | "title": "MyCard", 25 | "description": "MyCard description", 26 | "iconProperty": "" // Default to sharepointlogo 27 | } 28 | }] 29 | } -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/MyCardAdaptiveCardExtension.ts: -------------------------------------------------------------------------------- 1 | import { IPropertyPaneConfiguration } from '@microsoft/sp-property-pane'; 2 | import { BaseAdaptiveCardExtension } from '@microsoft/sp-adaptive-card-extension-base'; 3 | import { CardView } from './cardView/CardView'; 4 | import { QuickView } from './quickView/QuickView'; 5 | import { MyCardPropertyPane } from './MyCardPropertyPane'; 6 | 7 | export interface IMyCardAdaptiveCardExtensionProps { 8 | title: string; 9 | description: string; 10 | iconProperty: string; 11 | } 12 | 13 | export interface IMyCardAdaptiveCardExtensionState { 14 | description: string; 15 | } 16 | 17 | const CARD_VIEW_REGISTRY_ID = 'MyCard_CARD_VIEW'; 18 | export const QUICK_VIEW_REGISTRY_ID = 'MyCard_QUICK_VIEW'; 19 | 20 | export default class MyCardAdaptiveCardExtension extends BaseAdaptiveCardExtension< 21 | IMyCardAdaptiveCardExtensionProps, 22 | IMyCardAdaptiveCardExtensionState 23 | > { 24 | private _deferredPropertyPane: MyCardPropertyPane | undefined; 25 | 26 | public onInit(): Promise { 27 | this.state = { 28 | description: this.properties.description 29 | }; 30 | 31 | this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView()); 32 | this.quickViewNavigator.register(QUICK_VIEW_REGISTRY_ID, () => new QuickView()); 33 | 34 | return Promise.resolve(); 35 | } 36 | 37 | public get title(): string { 38 | return this.properties.title; 39 | } 40 | 41 | public get iconProperty(): string { 42 | return this.properties.iconProperty || require('./assets/SharePointLogo.svg'); 43 | } 44 | 45 | protected loadPropertyPaneResources(): Promise { 46 | return import( 47 | /* webpackChunkName: 'MyCard-property-pane'*/ 48 | './MyCardPropertyPane' 49 | ) 50 | .then( 51 | (component) => { 52 | this._deferredPropertyPane = new component.MyCardPropertyPane(); 53 | } 54 | ); 55 | } 56 | 57 | protected renderCard(): string | undefined { 58 | return CARD_VIEW_REGISTRY_ID; 59 | } 60 | 61 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 62 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 63 | return this._deferredPropertyPane!.getPropertyPaneConfiguration(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/MyCardPropertyPane.ts: -------------------------------------------------------------------------------- 1 | import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane'; 2 | import * as strings from 'MyCardAdaptiveCardExtensionStrings'; 3 | 4 | export class MyCardPropertyPane { 5 | public getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 6 | return { 7 | pages: [ 8 | { 9 | header: { description: strings.PropertyPaneDescription }, 10 | groups: [ 11 | { 12 | groupName: strings.BasicGroupName, 13 | groupFields: [ 14 | PropertyPaneTextField('title', { 15 | label: strings.TitleFieldLabel 16 | }), 17 | PropertyPaneTextField('iconProperty', { 18 | label: strings.IconPropertyFieldLabel 19 | }), 20 | PropertyPaneTextField('description', { 21 | label: strings.DescriptionFieldLabel, 22 | multiline: true 23 | }) 24 | ] 25 | } 26 | ] 27 | } 28 | ] 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/assets/SharePointLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/cardView/CardView.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseImageCardView, 3 | IImageCardParameters, 4 | IExternalLinkCardAction, 5 | IQuickViewCardAction 6 | } from '@microsoft/sp-adaptive-card-extension-base'; 7 | import * as strings from 'MyCardAdaptiveCardExtensionStrings'; 8 | import { IMyCardAdaptiveCardExtensionProps, IMyCardAdaptiveCardExtensionState } from '../MyCardAdaptiveCardExtension'; 9 | 10 | export class CardView extends BaseImageCardView { 11 | public get data(): IImageCardParameters { 12 | return { 13 | primaryText: strings.PrimaryText, 14 | imageUrl: 'https://blogs.microsoft.com/uploads/2017/09/WR-Microsoft-logo.jpg' 15 | }; 16 | } 17 | 18 | public get onCardSelection(): IQuickViewCardAction | IExternalLinkCardAction | undefined { 19 | return { 20 | type: 'ExternalLink', 21 | parameters: { 22 | target: 'https://www.bing.com' 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "TitleFieldLabel": "Card Title", 7 | "IconPropertyFieldLabel": "Card Icon", 8 | "Title": "Adaptive Card Extension", 9 | "SubTitle": "Quick View", 10 | "Description": "Create your SPFx Adaptive Card Extension solution!", 11 | "PrimaryText": "SPFx Adaptive Card Extension", 12 | "QuickViewButton": "Quick View" 13 | } 14 | }); -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/loc/mystring.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IMyCardAdaptiveCardExtensionStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | TitleFieldLabel: string; 6 | IconPropertyFieldLabel: string; 7 | Title: string; 8 | SubTitle: string; 9 | Description: string; 10 | PrimaryText: string; 11 | QuickViewButton: string; 12 | } 13 | 14 | declare module 'MyCardAdaptiveCardExtensionStrings' { 15 | const strings: IMyCardAdaptiveCardExtensionStrings; 16 | export = strings; 17 | } 18 | -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/quickView/QuickView.ts: -------------------------------------------------------------------------------- 1 | import { ISPFxAdaptiveCard, BaseAdaptiveCardView } from '@microsoft/sp-adaptive-card-extension-base'; 2 | import * as strings from 'MyCardAdaptiveCardExtensionStrings'; 3 | import { IMyCardAdaptiveCardExtensionProps, IMyCardAdaptiveCardExtensionState } from '../MyCardAdaptiveCardExtension'; 4 | 5 | export interface IQuickViewData { 6 | subTitle: string; 7 | title: string; 8 | description: string; 9 | } 10 | 11 | export class QuickView extends BaseAdaptiveCardView< 12 | IMyCardAdaptiveCardExtensionProps, 13 | IMyCardAdaptiveCardExtensionState, 14 | IQuickViewData 15 | > { 16 | public get data(): IQuickViewData { 17 | return { 18 | subTitle: strings.SubTitle, 19 | title: strings.Title, 20 | description: this.properties.description 21 | }; 22 | } 23 | 24 | public get template(): ISPFxAdaptiveCard { 25 | return require('./template/QuickViewTemplate.json'); 26 | } 27 | } -------------------------------------------------------------------------------- /samples/advanced/src/adaptiveCardExtensions/myCard/quickView/template/QuickViewTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": "http://adaptivecards.io/schemas/adaptive-card.json", 3 | "type": "AdaptiveCard", 4 | "version": "1.2", 5 | "body": [ 6 | { 7 | "type": "TextBlock", 8 | "weight": "Bolder", 9 | "text": "${title}" 10 | }, 11 | { 12 | "type": "ColumnSet", 13 | "columns": [ 14 | { 15 | "type": "Column", 16 | "items": [ 17 | { 18 | "type": "TextBlock", 19 | "weight": "Bolder", 20 | "text": "${subTitle}", 21 | "wrap": true 22 | } 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | "type": "TextBlock", 29 | "text": "${description}", 30 | "wrap": true 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /samples/advanced/src/index.ts: -------------------------------------------------------------------------------- 1 | // A file is required to be in the root of the /src directory by the TypeScript compiler 2 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/ContextInfoPropertyPane.ts: -------------------------------------------------------------------------------- 1 | import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane'; 2 | 3 | import * as strings from 'ContextInfoWebPartStrings'; 4 | 5 | export class ContextInfoPropertyPane { 6 | public getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 7 | return { 8 | pages: [ 9 | { 10 | header: { 11 | description: strings.PropertyPaneDescription 12 | }, 13 | groups: [ 14 | { 15 | groupName: strings.BasicGroupName, 16 | groupFields: [ 17 | PropertyPaneTextField('description', { 18 | label: strings.DescriptionFieldLabel 19 | }) 20 | ] 21 | } 22 | ] 23 | } 24 | ] 25 | }; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/ContextInfoWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "a9c3e764-8dc5-436a-a66b-57122d33bb0d", 4 | "alias": "ContextInfoWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart", "TeamsTab"], 16 | 17 | "preconfiguredEntries": [{ 18 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other 19 | "group": { "default": "Other" }, 20 | "title": { "default": "ContextInfo" }, 21 | "description": { "default": "Provides information about the current context either Teams or SharePoint depending on the running environment" }, 22 | "officeFabricIconFontName": "Page", 23 | "properties": { 24 | "description": "ContextInfo" 25 | } 26 | }] 27 | } 28 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/ContextInfoWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | IPropertyPaneConfiguration 6 | } from '@microsoft/sp-property-pane'; 7 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 8 | 9 | import ContextInfo from '@src/webparts/contextInfo/components/ContextInfo/ContextInfo'; 10 | import { ContextInfoPropertyPane } from './ContextInfoPropertyPane'; 11 | 12 | export interface IContextInfoWebPartProps { 13 | description: string; 14 | } 15 | 16 | export default class ContextInfoWebPart extends BaseClientSideWebPart { 17 | 18 | private propertyPane: ContextInfoPropertyPane; 19 | 20 | protected async onInit(): Promise { 21 | await super.onInit(); 22 | } 23 | 24 | public render(): void { 25 | const element = React.createElement(ContextInfo, { 26 | context: this.context 27 | }); 28 | 29 | ReactDom.render(element, this.domElement); 30 | } 31 | 32 | protected onDispose(): void { 33 | ReactDom.unmountComponentAtNode(this.domElement); 34 | } 35 | 36 | protected get dataVersion(): Version { 37 | return Version.parse('1.0'); 38 | } 39 | 40 | protected async onPropertyPaneConfigurationStart(): Promise { 41 | await this.loadPropertyPaneResources(); 42 | } 43 | 44 | protected async loadPropertyPaneResources(): Promise { 45 | const { ContextInfoPropertyPane } = await import( 46 | /* webpackChunkName: 'ContextInfoPropertyPane' */ 47 | './ContextInfoPropertyPane' 48 | ) 49 | 50 | this.propertyPane = new ContextInfoPropertyPane(); 51 | } 52 | 53 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 54 | return this.propertyPane.getPropertyPaneConfiguration(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/components/ContextInfo/ContextInfo.module.scss: -------------------------------------------------------------------------------- 1 | @import "~@fluentui/react/dist/sass/References.scss"; 2 | 3 | .contextInfo { 4 | .container { 5 | max-width: 700px; 6 | margin: 0px auto; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/components/ContextInfo/ContextInfo.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect, useState } from 'react'; 2 | import { SPHttpClient } from '@microsoft/sp-http'; 3 | import { spfi, SPFx } from '@pnp/sp'; 4 | import '@pnp/sp/webs'; 5 | 6 | import styles from './ContextInfo.module.scss'; 7 | import { ContextInfoProps } from './ContextInfoProps'; 8 | import { Header } from './../Header/Header'; 9 | 10 | const ContextInfo: FC = ({ context }) => { 11 | const [loading, setLoading] = useState(true); 12 | const [info, setInfo] = useState(); 13 | 14 | useEffect(() => { 15 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 16 | const getInfo = async () => { 17 | 18 | if (context.sdks.microsoftTeams) { 19 | setInfo(JSON.stringify(context.sdks.microsoftTeams.context, null, 2)); 20 | setLoading(false); 21 | return; 22 | } 23 | 24 | const res = await (await context.spHttpClient.get(`${context.pageContext.web.absoluteUrl}/_api/web`, SPHttpClient.configurations.v1, { 25 | headers: { 26 | 'Accept': 'application/json;odata.metadata=none' 27 | } 28 | })).json(); 29 | 30 | const sp = spfi().using(SPFx({ pageContext: context.pageContext })); 31 | const web = await sp.web(); 32 | 33 | console.log(web); 34 | 35 | setInfo(JSON.stringify(res, null, 2)); 36 | setLoading(false); 37 | }; 38 | 39 | getInfo(); 40 | }, []); 41 | 42 | if (loading) { 43 | return
Loading....
; 44 | } 45 | 46 | return ( 47 |
48 |
49 |
50 |

51 |           {info}
52 |         
53 |         
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default ContextInfo; 60 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/components/ContextInfo/ContextInfoProps.ts: -------------------------------------------------------------------------------- 1 | import { WebPartContext } from '@microsoft/sp-webpart-base'; 2 | 3 | export interface ContextInfoProps { 4 | context: WebPartContext; 5 | } 6 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/components/Header/Header.module.scss: -------------------------------------------------------------------------------- 1 | .header { 2 | margin: 5px 0; 3 | .subHeader { 4 | color: black; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import * as strings from 'ContextInfoWebPartStrings'; 3 | import styles from './Header.module.scss'; 4 | 5 | interface Props { 6 | context: 'Teams' | 'Sharepoint'; 7 | } 8 | 9 | export const Header: FC = ({ context }) => { 10 | return ( 11 |
12 |

Your context: {context}

13 |
{strings.ContextInfoLabel}
14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "ContextInfoLabel": "Here is your context details:" 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IContextInfoWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | ContextInfoLabel: string; 6 | } 7 | 8 | declare module 'ContextInfoWebPartStrings' { 9 | const strings: IContextInfoWebPartStrings; 10 | export = strings; 11 | } 12 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/contextInfo/loc/nl-nl.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "ContextInfoLabel": "Hier zijn uw contextdetails:" 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/GraphToolkitTestWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "709495d2-f2f9-4527-90a2-d36e61eb4433", 4 | "alias": "GraphToolkitTestWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"], 16 | "supportsThemeVariants": true, 17 | 18 | "preconfiguredEntries": [{ 19 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Advanced 20 | "group": { "default": "Advanced" }, 21 | "title": { "default": "GraphToolkitTest" }, 22 | "description": { "default": "GraphToolkitTest description" }, 23 | "officeFabricIconFontName": "Page", 24 | "properties": { 25 | "description": "GraphToolkitTest" 26 | } 27 | }] 28 | } 29 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/GraphToolkitTestWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | type IPropertyPaneConfiguration, 6 | PropertyPaneTextField 7 | } from '@microsoft/sp-property-pane'; 8 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 9 | import { lazyLoadComponent } from "@microsoft/mgt-spfx-utils"; 10 | import * as strings from 'GraphToolkitTestWebPartStrings'; 11 | import { Providers, customElementHelper } from "@microsoft/mgt-element"; 12 | import { SharePointProvider } from "@microsoft/mgt-sharepoint-provider"; 13 | 14 | const MgtDemo = React.lazy( 15 | () => 16 | import(/* webpackChunkName: 'mgt-demo-component' */ "./components/GraphToolkitTest") 17 | ); 18 | 19 | export interface IGraphToolkitTestWebPartProps { 20 | description: string; 21 | } 22 | 23 | customElementHelper.withDisambiguation("mgt-demo-client-side-solution"); 24 | 25 | export default class GraphToolkitTestWebPart extends BaseClientSideWebPart { 26 | public render(): void { 27 | const element = lazyLoadComponent(MgtDemo, { 28 | description: this.properties.description 29 | }); 30 | 31 | ReactDom.render(element, this.domElement); 32 | } 33 | 34 | protected async onInit(): Promise { 35 | if (!Providers.globalProvider) { 36 | Providers.globalProvider = new SharePointProvider(this.context); 37 | } 38 | } 39 | 40 | protected onDispose(): void { 41 | ReactDom.unmountComponentAtNode(this.domElement); 42 | } 43 | 44 | protected get dataVersion(): Version { 45 | return Version.parse('1.0'); 46 | } 47 | 48 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 49 | return { 50 | pages: [ 51 | { 52 | header: { 53 | description: strings.PropertyPaneDescription 54 | }, 55 | groups: [ 56 | { 57 | groupName: strings.BasicGroupName, 58 | groupFields: [ 59 | PropertyPaneTextField('description', { 60 | label: strings.DescriptionFieldLabel 61 | }) 62 | ] 63 | } 64 | ] 65 | } 66 | ] 67 | }; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/assets/welcome-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/src/webparts/graphToolkitTest/assets/welcome-dark.png -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/assets/welcome-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/src/webparts/graphToolkitTest/assets/welcome-light.png -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/components/GraphToolkitTest.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Login } from '@microsoft/mgt-react'; 3 | import { FC } from 'react'; 4 | 5 | export const GraphToolkitTest: FC = () => { 6 | return ( 7 |
8 | 9 |
ok
10 |
11 | ); 12 | } 13 | 14 | export default GraphToolkitTest; 15 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "AppLocalEnvironmentSharePoint": "The app is running on your local environment as SharePoint web part", 7 | "AppLocalEnvironmentTeams": "The app is running on your local environment as Microsoft Teams app", 8 | "AppLocalEnvironmentOffice": "The app is running on your local environment in office.com", 9 | "AppLocalEnvironmentOutlook": "The app is running on your local environment in Outlook", 10 | "AppSharePointEnvironment": "The app is running on SharePoint page", 11 | "AppTeamsTabEnvironment": "The app is running in Microsoft Teams", 12 | "AppOfficeEnvironment": "The app is running in office.com", 13 | "AppOutlookEnvironment": "The app is running in Outlook", 14 | "UnknownEnvironment": "The app is running in an unknown environment" 15 | } 16 | }); -------------------------------------------------------------------------------- /samples/advanced/src/webparts/graphToolkitTest/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IGraphToolkitTestWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | AppLocalEnvironmentSharePoint: string; 6 | AppLocalEnvironmentTeams: string; 7 | AppLocalEnvironmentOffice: string; 8 | AppLocalEnvironmentOutlook: string; 9 | AppSharePointEnvironment: string; 10 | AppTeamsTabEnvironment: string; 11 | AppOfficeEnvironment: string; 12 | AppOutlookEnvironment: string; 13 | UnknownEnvironment: string; 14 | } 15 | 16 | declare module 'GraphToolkitTestWebPartStrings' { 17 | const strings: IGraphToolkitTestWebPartStrings; 18 | export = strings; 19 | } 20 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/HelloWorldWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "0b56b7d1-dcd1-41b9-afe5-5db2f554c63a", 4 | "alias": "HelloWorldWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart"], 16 | 17 | "preconfiguredEntries": [{ 18 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other 19 | "group": { "default": "Other" }, 20 | "title": { "default": "HelloWorld" }, 21 | "description": { "default": "HelloWorld description" }, 22 | "officeFabricIconFontName": "Page", 23 | "properties": { 24 | "description": "HelloWorld" 25 | } 26 | }] 27 | } 28 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/HelloWorldWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | IPropertyPaneConfiguration, 6 | PropertyPaneTextField 7 | } from '@microsoft/sp-property-pane'; 8 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 9 | 10 | import * as strings from 'HelloWorldWebPartStrings'; 11 | import HelloWorld from './components/HelloWorld'; 12 | import { IHelloWorldProps } from './components/IHelloWorldProps'; 13 | 14 | export interface IHelloWorldWebPartProps { 15 | description: string; 16 | } 17 | 18 | export default class HelloWorldWebPart extends BaseClientSideWebPart { 19 | 20 | public render(): void { 21 | const element: React.ReactElement = React.createElement( 22 | HelloWorld, 23 | { 24 | description: this.properties.description 25 | } 26 | ); 27 | 28 | ReactDom.render(element, this.domElement); 29 | } 30 | 31 | protected onDispose(): void { 32 | ReactDom.unmountComponentAtNode(this.domElement); 33 | } 34 | 35 | protected get dataVersion(): Version { 36 | return Version.parse('1.0'); 37 | } 38 | 39 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 40 | return { 41 | pages: [ 42 | { 43 | header: { 44 | description: strings.PropertyPaneDescription 45 | }, 46 | groups: [ 47 | { 48 | groupName: strings.BasicGroupName, 49 | groupFields: [ 50 | PropertyPaneTextField('description', { 51 | label: strings.DescriptionFieldLabel 52 | }) 53 | ] 54 | } 55 | ] 56 | } 57 | ] 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/components/HelloWorld.module.scss: -------------------------------------------------------------------------------- 1 | @import '~@fluentui/react/dist/sass/References.scss'; 2 | 3 | .helloWorld { 4 | .container { 5 | max-width: 700px; 6 | margin: 0px auto; 7 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 8 | } 9 | 10 | .row { 11 | @include ms-Grid-row; 12 | @include ms-fontColor-white; 13 | background-color: $ms-color-themeDark; 14 | padding: 20px; 15 | } 16 | 17 | .column { 18 | @include ms-Grid-col; 19 | @include ms-lg10; 20 | @include ms-xl8; 21 | @include ms-xlPush2; 22 | @include ms-lgPush1; 23 | } 24 | 25 | .title { 26 | @include ms-font-xl; 27 | @include ms-fontColor-white; 28 | } 29 | 30 | .subTitle { 31 | @include ms-font-l; 32 | @include ms-fontColor-white; 33 | } 34 | 35 | .description { 36 | @include ms-font-l; 37 | @include ms-fontColor-white; 38 | } 39 | 40 | .button { 41 | // Our button 42 | text-decoration: none; 43 | height: 32px; 44 | 45 | // Primary Button 46 | min-width: 80px; 47 | background-color: $ms-color-themePrimary; 48 | border-color: $ms-color-themePrimary; 49 | color: $ms-color-white; 50 | 51 | // Basic Button 52 | outline: transparent; 53 | position: relative; 54 | font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif; 55 | -webkit-font-smoothing: antialiased; 56 | font-size: $ms-font-size-m; 57 | font-weight: $ms-font-weight-regular; 58 | border-width: 0; 59 | text-align: center; 60 | cursor: pointer; 61 | display: inline-block; 62 | padding: 0 16px; 63 | 64 | .label { 65 | font-weight: $ms-font-weight-semibold; 66 | font-size: $ms-font-size-m; 67 | height: 32px; 68 | line-height: 32px; 69 | margin: 0 4px; 70 | vertical-align: top; 71 | display: inline-block; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/components/HelloWorld.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './HelloWorld.module.scss'; 3 | import { IHelloWorldProps } from './IHelloWorldProps'; 4 | import { escape } from '@microsoft/sp-lodash-subset'; 5 | import * as strings from 'HelloWorldWebPartStrings'; 6 | 7 | export default class HelloWorld extends React.Component { 8 | public render(): React.ReactElement { 9 | return ( 10 |
11 |
12 |
13 |
14 | Welcome to SharePoint! 15 |

{strings.PropertyPaneDescription}

16 |

{escape(this.props.description)}

17 | 18 | Learn more 19 | 20 |
21 |
22 |
23 |
24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/components/IHelloWorldProps.ts: -------------------------------------------------------------------------------- 1 | export interface IHelloWorldProps { 2 | description: string; 3 | } 4 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IHelloWorldWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | } 6 | 7 | declare module 'HelloWorldWebPartStrings' { 8 | const strings: IHelloWorldWebPartStrings; 9 | export = strings; 10 | } 11 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/helloWorld/loc/nl-nl.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/SampleWebPartWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "bb40bd87-dfa5-40dd-b333-6c643222727b", 4 | "alias": "SampleWebPartWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart"], 16 | 17 | "preconfiguredEntries": [{ 18 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other 19 | "group": { "default": "Other" }, 20 | "title": { "default": "SampleWebPart" }, 21 | "description": { "default": "SampleWebPart description" }, 22 | "officeFabricIconFontName": "Page", 23 | "properties": { 24 | "description": "SampleWebPart" 25 | } 26 | }] 27 | } 28 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/SampleWebPartWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | IPropertyPaneConfiguration, 6 | PropertyPaneTextField 7 | } from '@microsoft/sp-property-pane'; 8 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 9 | 10 | import * as strings from 'SampleWebPartWebPartStrings'; 11 | import SampleWebPart from './components/SampleWebPart'; 12 | import { ISampleWebPartProps } from './components/ISampleWebPartProps'; 13 | 14 | export interface ISampleWebPartWebPartProps { 15 | description: string; 16 | } 17 | 18 | export default class SampleWebPartWebPart extends BaseClientSideWebPart { 19 | 20 | public render(): void { 21 | const element: React.ReactElement = React.createElement( 22 | SampleWebPart, 23 | { 24 | description: this.properties.description 25 | } 26 | ); 27 | 28 | ReactDom.render(element, this.domElement); 29 | } 30 | 31 | protected onDispose(): void { 32 | ReactDom.unmountComponentAtNode(this.domElement); 33 | } 34 | 35 | protected get dataVersion(): Version { 36 | return Version.parse('1.0'); 37 | } 38 | 39 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 40 | return { 41 | pages: [ 42 | { 43 | header: { 44 | description: strings.PropertyPaneDescription 45 | }, 46 | groups: [ 47 | { 48 | groupName: strings.BasicGroupName, 49 | groupFields: [ 50 | PropertyPaneTextField('description', { 51 | label: strings.DescriptionFieldLabel 52 | }) 53 | ] 54 | } 55 | ] 56 | } 57 | ] 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/assets/parker-spfx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/src/webparts/sampleWebPart/assets/parker-spfx.png -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/components/ISampleWebPartProps.ts: -------------------------------------------------------------------------------- 1 | export interface ISampleWebPartProps { 2 | description: string; 3 | } 4 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/components/SampleWebPart.module.scss: -------------------------------------------------------------------------------- 1 | @import "~@fluentui/react/dist/sass/References.scss"; 2 | 3 | .sampleWebPart { 4 | .img { 5 | background: url("../assets/parker-spfx.png"); 6 | width: 300px; 7 | height: 392px; 8 | } 9 | 10 | .container { 11 | max-width: 700px; 12 | margin: 0px auto; 13 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 14 | } 15 | 16 | .row { 17 | @include ms-Grid-row; 18 | @include ms-fontColor-white; 19 | background-color: $ms-color-themeDark; 20 | padding: 20px; 21 | } 22 | 23 | .column { 24 | @include ms-Grid-col; 25 | @include ms-lg10; 26 | @include ms-xl8; 27 | @include ms-xlPush2; 28 | @include ms-lgPush1; 29 | } 30 | 31 | .title { 32 | @include ms-font-xl; 33 | @include ms-fontColor-white; 34 | } 35 | 36 | .subTitle { 37 | @include ms-font-l; 38 | @include ms-fontColor-white; 39 | } 40 | 41 | .description { 42 | @include ms-font-l; 43 | @include ms-fontColor-white; 44 | } 45 | 46 | .button { 47 | // Our button 48 | text-decoration: none; 49 | height: 32px; 50 | 51 | // Primary Button 52 | min-width: 80px; 53 | background-color: $ms-color-themePrimary; 54 | border-color: $ms-color-themePrimary; 55 | color: $ms-color-white; 56 | 57 | // Basic Button 58 | outline: transparent; 59 | position: relative; 60 | font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", 61 | sans-serif; 62 | -webkit-font-smoothing: antialiased; 63 | font-size: $ms-font-size-m; 64 | font-weight: $ms-font-weight-regular; 65 | border-width: 0; 66 | text-align: center; 67 | cursor: pointer; 68 | display: inline-block; 69 | padding: 0 16px; 70 | 71 | .label { 72 | font-weight: $ms-font-weight-semibold; 73 | font-size: $ms-font-size-m; 74 | height: 32px; 75 | line-height: 32px; 76 | margin: 0 4px; 77 | vertical-align: top; 78 | display: inline-block; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/components/SampleWebPart.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { DateTimePicker, DateConvention, TimeConvention } from '@pnp/spfx-controls-react/lib/DateTimePicker'; 3 | import { Placeholder as PnPPlaceholder } from '@pnp/spfx-controls-react/lib/Placeholder'; 4 | import { library } from '@fortawesome/fontawesome-svg-core'; 5 | import { faCamera } from '@fortawesome/free-solid-svg-icons'; 6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 7 | import marked from 'marked'; 8 | library.add(faCamera); 9 | 10 | import styles from './SampleWebPart.module.scss'; 11 | import { ISampleWebPartProps } from './ISampleWebPartProps'; 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-var-requires 14 | const logo: string = require('../assets/parker-spfx.png'); 15 | export default class SampleWebPart extends React.Component { 16 | public render(): React.ReactElement { 17 | return ( 18 |
19 |
20 |
21 |
22 |
SPFx React controls Date Picker
23 | 26 |
27 | 28 |
29 | 30 |
31 | SPFx logo 32 |
33 |
{marked('I am using **markdown**.')}
34 |
35 |
36 |
37 |
38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface ISampleWebPartWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | } 6 | 7 | declare module 'SampleWebPartWebPartStrings' { 8 | const strings: ISampleWebPartWebPartStrings; 9 | export = strings; 10 | } 11 | -------------------------------------------------------------------------------- /samples/advanced/src/webparts/sampleWebPart/loc/nl-nl.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /samples/advanced/teams/0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_color.png -------------------------------------------------------------------------------- /samples/advanced/teams/0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/0b56b7d1-dcd1-41b9-afe5-5db2f554c63a_outline.png -------------------------------------------------------------------------------- /samples/advanced/teams/709495d2-f2f9-4527-90a2-d36e61eb4433_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/709495d2-f2f9-4527-90a2-d36e61eb4433_color.png -------------------------------------------------------------------------------- /samples/advanced/teams/709495d2-f2f9-4527-90a2-d36e61eb4433_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/709495d2-f2f9-4527-90a2-d36e61eb4433_outline.png -------------------------------------------------------------------------------- /samples/advanced/teams/a9c3e764-8dc5-436a-a66b-57122d33bb0d_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/a9c3e764-8dc5-436a-a66b-57122d33bb0d_color.png -------------------------------------------------------------------------------- /samples/advanced/teams/a9c3e764-8dc5-436a-a66b-57122d33bb0d_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/a9c3e764-8dc5-436a-a66b-57122d33bb0d_outline.png -------------------------------------------------------------------------------- /samples/advanced/teams/bb40bd87-dfa5-40dd-b333-6c643222727b_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/bb40bd87-dfa5-40dd-b333-6c643222727b_color.png -------------------------------------------------------------------------------- /samples/advanced/teams/bb40bd87-dfa5-40dd-b333-6c643222727b_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/advanced/teams/bb40bd87-dfa5-40dd-b333-6c643222727b_outline.png -------------------------------------------------------------------------------- /samples/advanced/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "forceConsistentCasingInFileNames": true, 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "jsx": "react", 9 | "declaration": true, 10 | "sourceMap": true, 11 | "experimentalDecorators": true, 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | "skipLibCheck": true, 15 | "outDir": "lib", 16 | "inlineSources": false, 17 | "strictNullChecks": false, 18 | "noUnusedLocals": false, 19 | "baseUrl": ".", 20 | "paths": { 21 | "@src/*": [ 22 | "src/*" 23 | ] 24 | }, 25 | "typeRoots": [ 26 | "./node_modules/@types", 27 | "./node_modules/@microsoft" 28 | ], 29 | "types": [ 30 | "webpack-env" 31 | ], 32 | "lib": [ 33 | "es5", 34 | "dom", 35 | "es2015.promise", 36 | "es2015.collection" 37 | ] 38 | }, 39 | "include": [ 40 | "src/**/*.ts", 41 | "src/**/*.tsx" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /samples/basic/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules 8 | 9 | # Build generated files 10 | dist 11 | lib 12 | solution 13 | temp 14 | *.sppkg 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # OSX 20 | .DS_Store 21 | 22 | # Visual Studio files 23 | .ntvs_analysis.dat 24 | .vs 25 | bin 26 | obj 27 | 28 | # Resx Generated Code 29 | *.resx.ts 30 | 31 | # Styles Generated Code 32 | *.scss.ts 33 | *.scss.d.ts 34 | .heft 35 | -------------------------------------------------------------------------------- /samples/basic/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "msjsdiag.debugger-for-chrome" 4 | ] 5 | } -------------------------------------------------------------------------------- /samples/basic/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Hosted workbench", 6 | "type": "msedge", 7 | "request": "launch", 8 | "url": "https://{tenantDomain}/_layouts/workbench.aspx", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///.././src/*": "${webRoot}/src/*", 13 | "webpack:///../../../src/*": "${webRoot}/src/*", 14 | "webpack:///../../../../src/*": "${webRoot}/src/*", 15 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 16 | }, 17 | "runtimeArgs": [ 18 | "--remote-debugging-port=9222", 19 | "-incognito" 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /samples/basic/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Configure glob patterns for excluding files and folders in the file explorer. 4 | "files.exclude": { 5 | "**/.git": true, 6 | "**/.DS_Store": true, 7 | "**/bower_components": true, 8 | "**/coverage": true, 9 | "**/lib-amd": true, 10 | "src/**/*.scss.ts": true, 11 | "**/jest-output": true 12 | }, 13 | "typescript.tsdk": ".\\node_modules\\typescript\\lib" 14 | } 15 | -------------------------------------------------------------------------------- /samples/basic/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@microsoft/generator-sharepoint": { 3 | "isCreatingSolution": false, 4 | "environment": "spo", 5 | "version": "1.20.0", 6 | "libraryName": "basic", 7 | "libraryId": "99057c54-0343-497d-90ae-dbba803e5023", 8 | "packageManager": "npm", 9 | "isDomainIsolated": false, 10 | "componentType": "webpart", 11 | "nodeVersion": "16.17.0", 12 | "sdkVersions": { 13 | "@microsoft/microsoft-graph-client": "3.0.2", 14 | "@microsoft/teams-js": "2.24.0" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /samples/basic/README.md: -------------------------------------------------------------------------------- 1 | # Basic sample 2 | 3 | ![SPFx](https://img.shields.io/badge/SPFx-1.20.0-green.svg) 4 | 5 | That's just a very simple "hello world" sample which demonstrates how to use `spfx-fast-serve` with SPFx. 6 | 7 | ## How to run 8 | 9 | 1. Restore dependencies `npm install` 10 | 2. `npm run serve` 11 | -------------------------------------------------------------------------------- /samples/basic/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", 3 | "version": "2.0", 4 | "bundles": { 5 | "my-solution-pack": { 6 | "components": [ 7 | { 8 | "entrypoint": "./lib/webparts/basicWebpart/BasicWebpartWebPart.js", 9 | "manifest": "./src/webparts/basicWebpart/BasicWebpartWebPart.manifest.json" 10 | } 11 | ] 12 | } 13 | }, 14 | "externals": {}, 15 | "localizedResources": { 16 | "BasicWebpartWebPartStrings": "lib/webparts/basicWebpart/loc/{locale}.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/basic/config/copy-assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json", 3 | "deployCdnPath": "temp/deploy" 4 | } 5 | -------------------------------------------------------------------------------- /samples/basic/config/deploy-azure-storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", 3 | "workingDir": "./temp/deploy/", 4 | "account": "", 5 | "container": "basic", 6 | "accessKey": "" 7 | } -------------------------------------------------------------------------------- /samples/basic/config/package-solution.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", 3 | "solution": { 4 | "name": "basic-client-side-solution", 5 | "id": "99057c54-0343-497d-90ae-dbba803e5023", 6 | "version": "1.0.0.0", 7 | "includeClientSideAssets": true, 8 | "isDomainIsolated": false, 9 | "developer": { 10 | "name": "", 11 | "privacyUrl": "", 12 | "termsOfUseUrl": "", 13 | "websiteUrl": "", 14 | "mpnId": "Undefined-1.15.0" 15 | }, 16 | "metadata": { 17 | "shortDescription": { 18 | "default": "basic description" 19 | }, 20 | "longDescription": { 21 | "default": "basic description" 22 | }, 23 | "screenshotPaths": [], 24 | "videoUrl": "", 25 | "categories": [] 26 | }, 27 | "features": [ 28 | { 29 | "title": "basic Feature", 30 | "description": "The feature that activates elements of the basic solution.", 31 | "id": "ddf570ca-d3d3-4958-a3f1-eeb6e776dfc7", 32 | "version": "1.0.0.0" 33 | } 34 | ] 35 | }, 36 | "paths": { 37 | "zippedPackage": "solution/basic.sppkg" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /samples/basic/config/sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json" 3 | } 4 | -------------------------------------------------------------------------------- /samples/basic/config/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", 3 | "port": 4321, 4 | "https": true, 5 | "initialPage": "https://{tenantDomain}/_layouts/workbench.aspx" 6 | } 7 | -------------------------------------------------------------------------------- /samples/basic/config/write-manifests.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json", 3 | "cdnBasePath": "" 4 | } -------------------------------------------------------------------------------- /samples/basic/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('@microsoft/sp-build-web'); 4 | build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); 5 | 6 | var getTasks = build.rig.getTasks; 7 | build.rig.getTasks = function () { 8 | var result = getTasks.call(build.rig); 9 | 10 | result.set('serve', result.get('serve-deprecated')); 11 | 12 | return result; 13 | }; 14 | 15 | /* fast-serve */ 16 | const { addFastServe } = require("spfx-fast-serve-helpers"); 17 | addFastServe(build); 18 | /* end of fast-serve */ 19 | 20 | build.initialize(require('gulp')); 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic", 3 | "version": "0.0.1", 4 | "private": true, 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "gulp bundle", 8 | "clean": "gulp clean", 9 | "test": "gulp test", 10 | "serve": "fast-serve" 11 | }, 12 | "engines": { 13 | "node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0" 14 | }, 15 | "dependencies": { 16 | "@fluentui/react": "^8.106.4", 17 | "@microsoft/sp-adaptive-card-extension-base": "1.20.0", 18 | "@microsoft/sp-component-base": "1.20.0", 19 | "@microsoft/sp-core-library": "1.20.0", 20 | "@microsoft/sp-lodash-subset": "1.20.0", 21 | "@microsoft/sp-office-ui-fabric-core": "1.20.0", 22 | "@microsoft/sp-property-pane": "1.20.0", 23 | "@microsoft/sp-webpart-base": "1.20.0", 24 | "react": "17.0.1", 25 | "react-dom": "17.0.1", 26 | "tslib": "2.3.1" 27 | }, 28 | "devDependencies": { 29 | "@microsoft/eslint-config-spfx": "1.20.2", 30 | "@microsoft/eslint-plugin-spfx": "1.20.2", 31 | "@microsoft/rush-stack-compiler-4.7": "0.1.0", 32 | "@microsoft/sp-build-web": "1.20.2", 33 | "@microsoft/sp-module-interfaces": "1.20.2", 34 | "@rushstack/eslint-config": "4.0.1", 35 | "@types/react": "17.0.45", 36 | "@types/react-dom": "17.0.17", 37 | "@types/webpack-env": "~1.15.2", 38 | "ajv": "^6.12.5", 39 | "eslint": "8.57.0", 40 | "eslint-plugin-react-hooks": "4.3.0", 41 | "gulp": "4.0.2", 42 | "spfx-fast-serve-helpers": "1.20.2", 43 | "typescript": "4.7.4" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /samples/basic/src/index.ts: -------------------------------------------------------------------------------- 1 | // A file is required to be in the root of the /src directory by the TypeScript compiler 2 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/BasicWebpartWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "1a1105ce-e53a-4cab-b728-27171ac56b6a", 4 | "alias": "BasicWebpartWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart"], 16 | 17 | "preconfiguredEntries": [{ 18 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other 19 | "group": { "default": "Other" }, 20 | "title": { "default": "BasicWebpart" }, 21 | "description": { "default": "BasicWebpart description" }, 22 | "officeFabricIconFontName": "Page", 23 | "properties": { 24 | "description": "BasicWebpart" 25 | } 26 | }] 27 | } 28 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/BasicWebpartWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | IPropertyPaneConfiguration, 6 | PropertyPaneTextField 7 | } from '@microsoft/sp-property-pane'; 8 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 9 | 10 | import * as strings from 'BasicWebpartWebPartStrings'; 11 | import BasicWebpart from './components/BasicWebpart'; 12 | import { IBasicWebpartProps } from './components/IBasicWebpartProps'; 13 | 14 | export interface IBasicWebpartWebPartProps { 15 | description: string; 16 | } 17 | 18 | export default class BasicWebpartWebPart extends BaseClientSideWebPart { 19 | 20 | public render(): void { 21 | const element: React.ReactElement = React.createElement( 22 | BasicWebpart, 23 | { 24 | description: this.properties.description 25 | } 26 | ); 27 | 28 | ReactDom.render(element, this.domElement); 29 | } 30 | 31 | protected onDispose(): void { 32 | ReactDom.unmountComponentAtNode(this.domElement); 33 | } 34 | 35 | protected get dataVersion(): Version { 36 | return Version.parse('1.0'); 37 | } 38 | 39 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 40 | return { 41 | pages: [ 42 | { 43 | header: { 44 | description: strings.PropertyPaneDescription 45 | }, 46 | groups: [ 47 | { 48 | groupName: strings.BasicGroupName, 49 | groupFields: [ 50 | PropertyPaneTextField('description', { 51 | label: strings.DescriptionFieldLabel 52 | }) 53 | ] 54 | } 55 | ] 56 | } 57 | ] 58 | }; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/components/BasicWebpart.module.scss: -------------------------------------------------------------------------------- 1 | @import '~@fluentui/react/dist/sass/References.scss'; 2 | 3 | .basicWebpart { 4 | .container { 5 | max-width: 700px; 6 | margin: 0px auto; 7 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1); 8 | } 9 | 10 | .row { 11 | @include ms-Grid-row; 12 | @include ms-fontColor-white; 13 | background-color: $ms-color-themeDark; 14 | padding: 20px; 15 | } 16 | 17 | .column { 18 | @include ms-Grid-col; 19 | @include ms-lg10; 20 | @include ms-xl8; 21 | @include ms-xlPush2; 22 | @include ms-lgPush1; 23 | } 24 | 25 | .title { 26 | @include ms-font-xl; 27 | @include ms-fontColor-white; 28 | } 29 | 30 | .subTitle { 31 | @include ms-font-l; 32 | @include ms-fontColor-white; 33 | } 34 | 35 | .description { 36 | @include ms-font-l; 37 | @include ms-fontColor-white; 38 | } 39 | 40 | .button { 41 | // Our button 42 | text-decoration: none; 43 | height: 32px; 44 | 45 | // Primary Button 46 | min-width: 80px; 47 | background-color: $ms-color-themePrimary; 48 | border-color: $ms-color-themePrimary; 49 | color: $ms-color-white; 50 | 51 | // Basic Button 52 | outline: transparent; 53 | position: relative; 54 | font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif; 55 | -webkit-font-smoothing: antialiased; 56 | font-size: $ms-font-size-m; 57 | font-weight: $ms-font-weight-regular; 58 | border-width: 0; 59 | text-align: center; 60 | cursor: pointer; 61 | display: inline-block; 62 | padding: 0 16px; 63 | 64 | .label { 65 | font-weight: $ms-font-weight-semibold; 66 | font-size: $ms-font-size-m; 67 | height: 32px; 68 | line-height: 32px; 69 | margin: 0 4px; 70 | vertical-align: top; 71 | display: inline-block; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/components/BasicWebpart.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './BasicWebpart.module.scss'; 3 | import { IBasicWebpartProps } from './IBasicWebpartProps'; 4 | import { escape } from '@microsoft/sp-lodash-subset'; 5 | 6 | export default class BasicWebpart extends React.Component { 7 | public render(): React.ReactElement { 8 | return ( 9 |
10 |
11 |
12 |
13 |

Customize SharePoint experiences using Web Parts.

14 |

{escape(this.props.description)}

15 | 16 | Learn more 17 | 18 |
19 |
20 |
21 |
22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/components/IBasicWebpartProps.ts: -------------------------------------------------------------------------------- 1 | export interface IBasicWebpartProps { 2 | description: string; 3 | } 4 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "Welcome": "Welcome to SharePoint!" 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /samples/basic/src/webparts/basicWebpart/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IBasicWebpartWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | Welcome: string; 6 | } 7 | 8 | declare module 'BasicWebpartWebPartStrings' { 9 | const strings: IBasicWebpartWebPartStrings; 10 | export = strings; 11 | } 12 | -------------------------------------------------------------------------------- /samples/basic/teams/0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_color.png -------------------------------------------------------------------------------- /samples/basic/teams/0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/0a44e5c0-9e3c-4ad9-9e94-e5d86625b6bd_outline.png -------------------------------------------------------------------------------- /samples/basic/teams/1a1105ce-e53a-4cab-b728-27171ac56b6a_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/1a1105ce-e53a-4cab-b728-27171ac56b6a_color.png -------------------------------------------------------------------------------- /samples/basic/teams/1a1105ce-e53a-4cab-b728-27171ac56b6a_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/1a1105ce-e53a-4cab-b728-27171ac56b6a_outline.png -------------------------------------------------------------------------------- /samples/basic/teams/adba4f0a-6084-4cb4-8721-5d4008abbd52_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/adba4f0a-6084-4cb4-8721-5d4008abbd52_color.png -------------------------------------------------------------------------------- /samples/basic/teams/adba4f0a-6084-4cb4-8721-5d4008abbd52_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/basic/teams/adba4f0a-6084-4cb4-8721-5d4008abbd52_outline.png -------------------------------------------------------------------------------- /samples/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "forceConsistentCasingInFileNames": true, 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "jsx": "react", 9 | "declaration": true, 10 | "sourceMap": true, 11 | "experimentalDecorators": true, 12 | "skipLibCheck": true, 13 | "outDir": "lib", 14 | "inlineSources": false, 15 | "strictNullChecks": false, 16 | "noUnusedLocals": false, 17 | "typeRoots": [ 18 | "./node_modules/@types", 19 | "./node_modules/@microsoft" 20 | ], 21 | "types": [ 22 | "webpack-env" 23 | ], 24 | "lib": [ 25 | "es5", 26 | "dom", 27 | "es2015.promise", 28 | "es2015.collection" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /samples/library-components/README.md: -------------------------------------------------------------------------------- 1 | # Library components: regular approach 2 | 3 | ![SPFx](https://img.shields.io/badge/SPFx-1.18.2-green.svg) 4 | 5 | Please refer to the docs [about library components](../../docs/LibraryComponents.md) to read about regular approach of handling library components with `spfx-fast-serve`. 6 | 7 | ## How to run 8 | 9 | 1. Run `npm install` in `./spfx-library` and then inside `./spfx-webparts` 10 | 2. Run `npm run serve` in `./spfx-library` and then inside `./spfx-webparts`. 11 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules 8 | 9 | # Build generated files 10 | dist 11 | lib 12 | release 13 | solution 14 | temp 15 | *.sppkg 16 | .heft 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # OSX 22 | .DS_Store 23 | 24 | # Visual Studio files 25 | .ntvs_analysis.dat 26 | .vs 27 | bin 28 | obj 29 | 30 | # Resx Generated Code 31 | *.resx.ts 32 | 33 | # Styles Generated Code 34 | *.scss.ts 35 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/.npmignore: -------------------------------------------------------------------------------- 1 | !dist 2 | config 3 | 4 | gulpfile.js 5 | 6 | release 7 | src 8 | temp 9 | 10 | tsconfig.json 11 | tslint.json 12 | 13 | *.log 14 | 15 | .yo-rc.json 16 | .vscode 17 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Hosted workbench", 6 | "type": "msedge", 7 | "request": "launch", 8 | "url": "https://{tenantDomain}/_layouts/workbench.aspx", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///.././src/*": "${webRoot}/src/*", 13 | "webpack:///../../../src/*": "${webRoot}/src/*", 14 | "webpack:///../../../../src/*": "${webRoot}/src/*", 15 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 16 | }, 17 | "runtimeArgs": [ 18 | "--remote-debugging-port=9222", 19 | "-incognito" 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-library/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Configure glob patterns for excluding files and folders in the file explorer. 4 | "files.exclude": { 5 | "**/.git": true, 6 | "**/.DS_Store": true, 7 | "**/bower_components": true, 8 | "**/coverage": true, 9 | "**/jest-output": true, 10 | "**/lib-amd": true, 11 | "src/**/*.scss.ts": true 12 | }, 13 | "typescript.tsdk": ".\\node_modules\\typescript\\lib" 14 | } 15 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@microsoft/generator-sharepoint": { 3 | "plusBeta": false, 4 | "isCreatingSolution": true, 5 | "nodeVersion": "18.17.1", 6 | "sdksVersions": { 7 | "@microsoft/microsoft-graph-client": "3.0.2", 8 | "@microsoft/teams-js": "2.12.0" 9 | }, 10 | "version": "1.19.0", 11 | "libraryName": "corporate-library", 12 | "libraryId": "ecba9457-d31a-4d20-a888-be994dd24db4", 13 | "environment": "spo", 14 | "packageManager": "npm", 15 | "solutionName": "corporate-library", 16 | "solutionShortDescription": "corporate-library description", 17 | "skipFeatureDeployment": true, 18 | "isDomainIsolated": false, 19 | "componentType": "library" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/README.md: -------------------------------------------------------------------------------- 1 | # corporate-library 2 | 3 | ## Used SharePoint Framework Version 4 | 5 | ![version](https://img.shields.io/badge/version-1.19.0-green.svg) 6 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", 3 | "version": "2.0", 4 | "bundles": { 5 | "my-api-library": { 6 | "components": [ 7 | { 8 | "entrypoint": "./lib/index.js", 9 | "manifest": "./src/libraries/myApi/MyApiLibrary.manifest.json" 10 | } 11 | ] 12 | } 13 | }, 14 | "externals": {}, 15 | "localizedResources": { 16 | "MyApiLibraryStrings": "lib/libraries/myApi/loc/{locale}.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/deploy-azure-storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", 3 | "workingDir": "./release/assets/", 4 | "account": "", 5 | "container": "corporate-library", 6 | "accessKey": "" 7 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/package-solution.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", 3 | "solution": { 4 | "name": "corporate-library-client-side-solution", 5 | "id": "ecba9457-d31a-4d20-a888-be994dd24db4", 6 | "version": "1.0.0.0", 7 | "includeClientSideAssets": true, 8 | "skipFeatureDeployment": true, 9 | "isDomainIsolated": false, 10 | "developer": { 11 | "name": "", 12 | "websiteUrl": "", 13 | "privacyUrl": "", 14 | "termsOfUseUrl": "", 15 | "mpnId": "Undefined-1.18.2" 16 | }, 17 | "metadata": { 18 | "shortDescription": { 19 | "default": "corporate-library description" 20 | }, 21 | "longDescription": { 22 | "default": "corporate-library description" 23 | }, 24 | "screenshotPaths": [], 25 | "videoUrl": "", 26 | "categories": [] 27 | } 28 | }, 29 | "paths": { 30 | "zippedPackage": "solution/corporate-library.sppkg" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json" 3 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", 3 | "port": 4321, 4 | "https": true 5 | } 6 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/config/write-manifests.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json", 3 | "cdnBasePath": "" 4 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-library/fast-serve/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/master/schema/config.v2.schema.json", 3 | "serve": { 4 | "port": 4320, 5 | "isLibraryComponent": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('@microsoft/sp-build-web'); 4 | 5 | build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); 6 | 7 | var getTasks = build.rig.getTasks; 8 | build.rig.getTasks = function () { 9 | var result = getTasks.call(build.rig); 10 | 11 | result.set('serve', result.get('serve-deprecated')); 12 | 13 | return result; 14 | }; 15 | 16 | /* fast-serve */ 17 | const { addFastServe } = require("spfx-fast-serve-helpers"); 18 | addFastServe(build); 19 | /* end of fast-serve */ 20 | 21 | build.initialize(require('gulp')); 22 | 23 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corporate-library", 3 | "version": "0.0.1", 4 | "private": false, 5 | "engines": { 6 | "node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0" 7 | }, 8 | "main": "lib/index.js", 9 | "scripts": { 10 | "build": "gulp bundle", 11 | "clean": "gulp clean", 12 | "test": "gulp test", 13 | "serve": "fast-serve" 14 | }, 15 | "dependencies": { 16 | "@microsoft/sp-adaptive-card-extension-base": "1.19.0", 17 | "@microsoft/sp-core-library": "1.19.0", 18 | "tslib": "2.3.1" 19 | }, 20 | "devDependencies": { 21 | "@microsoft/eslint-config-spfx": "1.20.1", 22 | "@microsoft/eslint-plugin-spfx": "1.20.1", 23 | "@microsoft/rush-stack-compiler-4.7": "0.1.0", 24 | "@microsoft/sp-build-web": "1.20.1", 25 | "@microsoft/sp-module-interfaces": "1.20.1", 26 | "@rushstack/eslint-config": "2.5.1", 27 | "@types/webpack-env": "~1.15.2", 28 | "ajv": "^6.12.5", 29 | "eslint": "8.7.0", 30 | "gulp": "4.0.2", 31 | "spfx-fast-serve-helpers": "1.19.1", 32 | "typescript": "4.7.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/src/index.ts: -------------------------------------------------------------------------------- 1 | export { MyApiLibrary } from './libraries/myApi/MyApiLibrary'; 2 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/src/libraries/myApi/MyApiLibrary.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2d3bdbe2-941d-4c2e-891c-9c728b7fc281", 3 | "alias": "MyApiLibrary", 4 | "componentType": "Library", 5 | 6 | // The "*" signifies that the version should be taken from the package.json 7 | "version": "*", 8 | "manifestVersion": 2 9 | } 10 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/src/libraries/myApi/MyApiLibrary.ts: -------------------------------------------------------------------------------- 1 | export class MyApiLibrary { 2 | public name(): string { 3 | return 'my name'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/src/libraries/myApi/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field" 6 | } 7 | }); -------------------------------------------------------------------------------- /samples/library-components/spfx-library/src/libraries/myApi/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IMyApiLibraryStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | } 6 | 7 | declare module 'MyApiLibraryStrings' { 8 | const strings: IMyApiLibraryStrings; 9 | export = strings; 10 | } 11 | -------------------------------------------------------------------------------- /samples/library-components/spfx-library/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "forceConsistentCasingInFileNames": true, 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "jsx": "react", 9 | "declaration": true, 10 | "sourceMap": true, 11 | "experimentalDecorators": true, 12 | "skipLibCheck": true, 13 | "outDir": "lib", 14 | "inlineSources": false, 15 | "noImplicitAny": true, 16 | 17 | "typeRoots": [ 18 | "./node_modules/@types", 19 | "./node_modules/@microsoft" 20 | ], 21 | "types": [ 22 | "webpack-env" 23 | ], 24 | "lib": [ 25 | "es5", 26 | "dom", 27 | "es2015.collection", 28 | "es2015.promise" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directories 7 | node_modules 8 | 9 | # Build generated files 10 | dist 11 | lib 12 | release 13 | solution 14 | temp 15 | *.sppkg 16 | .heft 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # OSX 22 | .DS_Store 23 | 24 | # Visual Studio files 25 | .ntvs_analysis.dat 26 | .vs 27 | bin 28 | obj 29 | 30 | # Resx Generated Code 31 | *.resx.ts 32 | 33 | # Styles Generated Code 34 | *.scss.ts 35 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/.npmignore: -------------------------------------------------------------------------------- 1 | !dist 2 | config 3 | 4 | gulpfile.js 5 | 6 | release 7 | src 8 | temp 9 | 10 | tsconfig.json 11 | tslint.json 12 | 13 | *.log 14 | 15 | .yo-rc.json 16 | .vscode 17 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Hosted workbench", 6 | "type": "msedge", 7 | "request": "launch", 8 | "url": "https://{tenantDomain}/_layouts/workbench.aspx", 9 | "webRoot": "${workspaceRoot}", 10 | "sourceMaps": true, 11 | "sourceMapPathOverrides": { 12 | "webpack:///.././src/*": "${webRoot}/src/*", 13 | "webpack:///../../../src/*": "${webRoot}/src/*", 14 | "webpack:///../../../../src/*": "${webRoot}/src/*", 15 | "webpack:///../../../../../src/*": "${webRoot}/src/*" 16 | }, 17 | "runtimeArgs": [ 18 | "--remote-debugging-port=9222", 19 | "-incognito" 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | // Configure glob patterns for excluding files and folders in the file explorer. 4 | "files.exclude": { 5 | "**/.git": true, 6 | "**/.DS_Store": true, 7 | "**/bower_components": true, 8 | "**/coverage": true, 9 | "**/jest-output": true, 10 | "**/lib-amd": true, 11 | "src/**/*.scss.ts": true 12 | }, 13 | "typescript.tsdk": ".\\node_modules\\typescript\\lib" 14 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@microsoft/generator-sharepoint": { 3 | "plusBeta": false, 4 | "isCreatingSolution": true, 5 | "nodeVersion": "18.17.1", 6 | "sdksVersions": { 7 | "@microsoft/microsoft-graph-client": "3.0.2", 8 | "@microsoft/teams-js": "2.12.0" 9 | }, 10 | "version": "1.19.0", 11 | "libraryName": "spfx-webparts", 12 | "libraryId": "a53ade95-227a-47ff-8add-89ef5cf39823", 13 | "environment": "spo", 14 | "packageManager": "npm", 15 | "solutionName": "spfx-webparts", 16 | "solutionShortDescription": "spfx-webparts description", 17 | "skipFeatureDeployment": true, 18 | "isDomainIsolated": false, 19 | "componentType": "webpart" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/README.md: -------------------------------------------------------------------------------- 1 | # spfx-webparts 2 | 3 | ## Used SharePoint Framework Version 4 | 5 | ![version](https://img.shields.io/badge/version-1.19.0-green.svg) 6 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", 3 | "version": "2.0", 4 | "bundles": { 5 | "hello-world-web-part": { 6 | "components": [ 7 | { 8 | "entrypoint": "./lib/webparts/helloWorld/HelloWorldWebPart.js", 9 | "manifest": "./src/webparts/helloWorld/HelloWorldWebPart.manifest.json" 10 | } 11 | ] 12 | } 13 | }, 14 | "externals": {}, 15 | "localizedResources": { 16 | "HelloWorldWebPartStrings": "lib/webparts/helloWorld/loc/{locale}.js" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/deploy-azure-storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json", 3 | "workingDir": "./release/assets/", 4 | "account": "", 5 | "container": "spfx-webparts", 6 | "accessKey": "" 7 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/package-solution.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json", 3 | "solution": { 4 | "name": "spfx-webparts-client-side-solution", 5 | "id": "a53ade95-227a-47ff-8add-89ef5cf39823", 6 | "version": "1.0.0.0", 7 | "includeClientSideAssets": true, 8 | "skipFeatureDeployment": true, 9 | "isDomainIsolated": false, 10 | "developer": { 11 | "name": "", 12 | "websiteUrl": "", 13 | "privacyUrl": "", 14 | "termsOfUseUrl": "", 15 | "mpnId": "Undefined-1.18.2" 16 | }, 17 | "metadata": { 18 | "shortDescription": { 19 | "default": "spfx-webparts description" 20 | }, 21 | "longDescription": { 22 | "default": "spfx-webparts description" 23 | }, 24 | "screenshotPaths": [], 25 | "videoUrl": "", 26 | "categories": [] 27 | }, 28 | "features": [ 29 | { 30 | "title": "spfx-webparts Feature", 31 | "description": "The feature that activates elements of the spfx-webparts solution.", 32 | "id": "9f22e86b-5161-4a3b-949f-84ca2d4e88be", 33 | "version": "1.0.0.0" 34 | } 35 | ] 36 | }, 37 | "paths": { 38 | "zippedPackage": "solution/spfx-webparts.sppkg" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/sass.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json" 3 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", 3 | "port": 4321, 4 | "https": true, 5 | "initialPage": "https://{tenantDomain}/_layouts/workbench.aspx" 6 | } 7 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/config/write-manifests.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json", 3 | "cdnBasePath": "" 4 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const build = require('@microsoft/sp-build-web'); 4 | 5 | build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); 6 | 7 | var getTasks = build.rig.getTasks; 8 | build.rig.getTasks = function () { 9 | var result = getTasks.call(build.rig); 10 | 11 | result.set('serve', result.get('serve-deprecated')); 12 | 13 | return result; 14 | }; 15 | 16 | /* fast-serve */ 17 | const { addFastServe } = require("spfx-fast-serve-helpers"); 18 | addFastServe(build); 19 | /* end of fast-serve */ 20 | 21 | build.initialize(require('gulp')); 22 | 23 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spfx-webparts", 3 | "version": "0.0.1", 4 | "private": true, 5 | "engines": { 6 | "node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0" 7 | }, 8 | "main": "lib/index.js", 9 | "scripts": { 10 | "build": "gulp bundle", 11 | "clean": "gulp clean", 12 | "test": "gulp test", 13 | "serve": "fast-serve" 14 | }, 15 | "dependencies": { 16 | "@fluentui/react": "^8.106.4", 17 | "@microsoft/sp-adaptive-card-extension-base": "1.19.0", 18 | "@microsoft/sp-component-base": "1.19.0", 19 | "@microsoft/sp-core-library": "1.19.0", 20 | "@microsoft/sp-lodash-subset": "1.19.0", 21 | "@microsoft/sp-office-ui-fabric-core": "1.19.0", 22 | "@microsoft/sp-property-pane": "1.19.0", 23 | "@microsoft/sp-webpart-base": "1.19.0", 24 | "corporate-library": "file:../spfx-library", 25 | "react": "17.0.1", 26 | "react-dom": "17.0.1", 27 | "tslib": "2.3.1" 28 | }, 29 | "devDependencies": { 30 | "@microsoft/eslint-config-spfx": "1.20.1", 31 | "@microsoft/eslint-plugin-spfx": "1.20.1", 32 | "@microsoft/rush-stack-compiler-4.7": "0.1.0", 33 | "@microsoft/sp-build-web": "1.20.1", 34 | "@microsoft/sp-module-interfaces": "1.20.1", 35 | "@rushstack/eslint-config": "2.5.1", 36 | "@types/react": "17.0.45", 37 | "@types/react-dom": "17.0.17", 38 | "@types/webpack-env": "~1.15.2", 39 | "ajv": "^6.12.5", 40 | "eslint": "8.7.0", 41 | "eslint-plugin-react-hooks": "4.3.0", 42 | "gulp": "4.0.2", 43 | "spfx-fast-serve-helpers": "1.19.1", 44 | "typescript": "4.7.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/index.ts: -------------------------------------------------------------------------------- 1 | // A file is required to be in the root of the /src directory by the TypeScript compiler 2 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/HelloWorldWebPart.manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json", 3 | "id": "0da4216e-f0ff-4e10-8e67-c6e02680b941", 4 | "alias": "HelloWorldWebPart", 5 | "componentType": "WebPart", 6 | 7 | // The "*" signifies that the version should be taken from the package.json 8 | "version": "*", 9 | "manifestVersion": 2, 10 | 11 | // If true, the component can only be installed on sites where Custom Script is allowed. 12 | // Components that allow authors to embed arbitrary script code should set this to true. 13 | // https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f 14 | "requiresCustomScript": false, 15 | "supportedHosts": ["SharePointWebPart", "TeamsPersonalApp", "TeamsTab", "SharePointFullPage"], 16 | "supportsThemeVariants": true, 17 | 18 | "preconfiguredEntries": [{ 19 | "groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Advanced 20 | "group": { "default": "Advanced" }, 21 | "title": { "default": "HelloWorld" }, 22 | "description": { "default": "HelloWorld description" }, 23 | "officeFabricIconFontName": "Page", 24 | "properties": { 25 | "description": "HelloWorld" 26 | } 27 | }] 28 | } 29 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/HelloWorldWebPart.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDom from 'react-dom'; 3 | import { Version } from '@microsoft/sp-core-library'; 4 | import { 5 | type IPropertyPaneConfiguration, 6 | PropertyPaneTextField 7 | } from '@microsoft/sp-property-pane'; 8 | import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; 9 | import { IReadonlyTheme } from '@microsoft/sp-component-base'; 10 | 11 | import * as strings from 'HelloWorldWebPartStrings'; 12 | import HelloWorld from './components/HelloWorld'; 13 | import { IHelloWorldProps } from './components/IHelloWorldProps'; 14 | 15 | export interface IHelloWorldWebPartProps { 16 | description: string; 17 | } 18 | 19 | export default class HelloWorldWebPart extends BaseClientSideWebPart { 20 | 21 | private _isDarkTheme: boolean = false; 22 | private _environmentMessage: string = ''; 23 | 24 | public render(): void { 25 | const element: React.ReactElement = React.createElement( 26 | HelloWorld, 27 | { 28 | description: this.properties.description, 29 | isDarkTheme: this._isDarkTheme, 30 | environmentMessage: this._environmentMessage, 31 | hasTeamsContext: !!this.context.sdks.microsoftTeams, 32 | userDisplayName: this.context.pageContext.user.displayName 33 | } 34 | ); 35 | 36 | ReactDom.render(element, this.domElement); 37 | } 38 | 39 | protected onInit(): Promise { 40 | return this._getEnvironmentMessage().then(message => { 41 | this._environmentMessage = message; 42 | }); 43 | } 44 | 45 | 46 | 47 | private _getEnvironmentMessage(): Promise { 48 | if (!!this.context.sdks.microsoftTeams) { // running in Teams, office.com or Outlook 49 | return this.context.sdks.microsoftTeams.teamsJs.app.getContext() 50 | .then(context => { 51 | let environmentMessage: string = ''; 52 | switch (context.app.host.name) { 53 | case 'Office': // running in Office 54 | environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment; 55 | break; 56 | case 'Outlook': // running in Outlook 57 | environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment; 58 | break; 59 | case 'Teams': // running in Teams 60 | case 'TeamsModern': 61 | environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment; 62 | break; 63 | default: 64 | environmentMessage = strings.UnknownEnvironment; 65 | } 66 | 67 | return environmentMessage; 68 | }); 69 | } 70 | 71 | return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment); 72 | } 73 | 74 | protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void { 75 | if (!currentTheme) { 76 | return; 77 | } 78 | 79 | this._isDarkTheme = !!currentTheme.isInverted; 80 | const { 81 | semanticColors 82 | } = currentTheme; 83 | 84 | if (semanticColors) { 85 | this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null); 86 | this.domElement.style.setProperty('--link', semanticColors.link || null); 87 | this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null); 88 | } 89 | 90 | } 91 | 92 | protected onDispose(): void { 93 | ReactDom.unmountComponentAtNode(this.domElement); 94 | } 95 | 96 | protected get dataVersion(): Version { 97 | return Version.parse('1.0'); 98 | } 99 | 100 | protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { 101 | return { 102 | pages: [ 103 | { 104 | header: { 105 | description: strings.PropertyPaneDescription 106 | }, 107 | groups: [ 108 | { 109 | groupName: strings.BasicGroupName, 110 | groupFields: [ 111 | PropertyPaneTextField('description', { 112 | label: strings.DescriptionFieldLabel 113 | }) 114 | ] 115 | } 116 | ] 117 | } 118 | ] 119 | }; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/assets/welcome-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/library-components/spfx-webparts/src/webparts/helloWorld/assets/welcome-dark.png -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/assets/welcome-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/library-components/spfx-webparts/src/webparts/helloWorld/assets/welcome-light.png -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/components/HelloWorld.module.scss: -------------------------------------------------------------------------------- 1 | @import '~@fluentui/react/dist/sass/References.scss'; 2 | 3 | .helloWorld { 4 | overflow: hidden; 5 | padding: 1em; 6 | color: "[theme:bodyText, default: #323130]"; 7 | color: var(--bodyText); 8 | &.teams { 9 | font-family: $ms-font-family-fallbacks; 10 | } 11 | } 12 | 13 | .welcome { 14 | text-align: center; 15 | } 16 | 17 | .welcomeImage { 18 | width: 100%; 19 | max-width: 420px; 20 | } 21 | 22 | .links { 23 | a { 24 | text-decoration: none; 25 | color: "[theme:link, default:#03787c]"; 26 | color: var(--link); // note: CSS Custom Properties support is limited to modern browsers only 27 | 28 | &:hover { 29 | text-decoration: underline; 30 | color: "[theme:linkHovered, default: #014446]"; 31 | color: var(--linkHovered); // note: CSS Custom Properties support is limited to modern browsers only 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/components/HelloWorld.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styles from './HelloWorld.module.scss'; 3 | import type { IHelloWorldProps } from './IHelloWorldProps'; 4 | import { escape } from '@microsoft/sp-lodash-subset'; 5 | import { MyApiLibrary } from "corporate-library"; 6 | 7 | export default class HelloWorld extends React.Component { 8 | public render(): React.ReactElement { 9 | const { 10 | description, 11 | isDarkTheme, 12 | environmentMessage, 13 | hasTeamsContext 14 | } = this.props; 15 | 16 | const myApiLibrary = new MyApiLibrary(); 17 | 18 | return ( 19 |
20 |
21 | 22 |

Well done, {myApiLibrary.name()}!

23 |
{environmentMessage}
24 |
Web part property value: {escape(description)}
25 |
26 |
27 |

Welcome to SharePoint Framework!

28 |

29 | The SharePoint Framework (SPFx) is a extensibility model for Microsoft Viva, Microsoft Teams and SharePoint. It's the easiest way to extend Microsoft 365 with automatic Single Sign On, automatic hosting and industry standard tooling. 30 |

31 |

Learn more about SPFx development:

32 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/components/IHelloWorldProps.ts: -------------------------------------------------------------------------------- 1 | export interface IHelloWorldProps { 2 | description: string; 3 | isDarkTheme: boolean; 4 | environmentMessage: string; 5 | hasTeamsContext: boolean; 6 | userDisplayName: string; 7 | } 8 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/loc/en-us.js: -------------------------------------------------------------------------------- 1 | define([], function() { 2 | return { 3 | "PropertyPaneDescription": "Description", 4 | "BasicGroupName": "Group Name", 5 | "DescriptionFieldLabel": "Description Field", 6 | "AppLocalEnvironmentSharePoint": "The app is running on your local environment as SharePoint web part", 7 | "AppLocalEnvironmentTeams": "The app is running on your local environment as Microsoft Teams app", 8 | "AppLocalEnvironmentOffice": "The app is running on your local environment in office.com", 9 | "AppLocalEnvironmentOutlook": "The app is running on your local environment in Outlook", 10 | "AppSharePointEnvironment": "The app is running on SharePoint page", 11 | "AppTeamsTabEnvironment": "The app is running in Microsoft Teams", 12 | "AppOfficeEnvironment": "The app is running in office.com", 13 | "AppOutlookEnvironment": "The app is running in Outlook", 14 | "UnknownEnvironment": "The app is running in an unknown environment" 15 | } 16 | }); -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/src/webparts/helloWorld/loc/mystrings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface IHelloWorldWebPartStrings { 2 | PropertyPaneDescription: string; 3 | BasicGroupName: string; 4 | DescriptionFieldLabel: string; 5 | AppLocalEnvironmentSharePoint: string; 6 | AppLocalEnvironmentTeams: string; 7 | AppLocalEnvironmentOffice: string; 8 | AppLocalEnvironmentOutlook: string; 9 | AppSharePointEnvironment: string; 10 | AppTeamsTabEnvironment: string; 11 | AppOfficeEnvironment: string; 12 | AppOutlookEnvironment: string; 13 | UnknownEnvironment: string; 14 | } 15 | 16 | declare module 'HelloWorldWebPartStrings' { 17 | const strings: IHelloWorldWebPartStrings; 18 | export = strings; 19 | } 20 | -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/teams/0da4216e-f0ff-4e10-8e67-c6e02680b941_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/library-components/spfx-webparts/teams/0da4216e-f0ff-4e10-8e67-c6e02680b941_color.png -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/teams/0da4216e-f0ff-4e10-8e67-c6e02680b941_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/709b45f3dce194bbc4b2c419bf3577b1e2dbaae6/samples/library-components/spfx-webparts/teams/0da4216e-f0ff-4e10-8e67-c6e02680b941_outline.png -------------------------------------------------------------------------------- /samples/library-components/spfx-webparts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json", 3 | "compilerOptions": { 4 | "target": "es5", 5 | "forceConsistentCasingInFileNames": true, 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "jsx": "react", 9 | "declaration": true, 10 | "sourceMap": true, 11 | "experimentalDecorators": true, 12 | "skipLibCheck": true, 13 | "outDir": "lib", 14 | "inlineSources": false, 15 | "noImplicitAny": true, 16 | 17 | "typeRoots": [ 18 | "./node_modules/@types", 19 | "./node_modules/@microsoft" 20 | ], 21 | "types": [ 22 | "webpack-env" 23 | ], 24 | "lib": [ 25 | "es5", 26 | "dom", 27 | "es2015.collection", 28 | "es2015.promise" 29 | ] 30 | }, 31 | "include": [ 32 | "src/**/*.ts", 33 | "src/**/*.tsx" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /schema/config.1.0.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "SPFx Fast Serve configuration (v1.0)", 4 | "description": "Defines cli and serve configuration structure", 5 | "type": "object", 6 | "properties": { 7 | "cli": { 8 | "type": "object", 9 | "description": "Contains spfx-fast-serve CLI related settings. If you change the spfx-fast-serve cli properties in config.json, you should run spfx-fast-serve again (without parameters) so that it updates your project according to a new configuration", 10 | "properties": { 11 | "usePnpm": { 12 | "type": "boolean" 13 | }, 14 | "isLibraryComponent": { 15 | "type": "boolean" 16 | }, 17 | "useRestProxy": { 18 | "type": "boolean" 19 | } 20 | }, 21 | "required": [ 22 | "usePnpm", 23 | "isLibraryComponent", 24 | "useRestProxy" 25 | ] 26 | }, 27 | "serve": { 28 | "type": "object", 29 | "description": "Contains serve specific properties. You can change these settings and see the immediate effect after running 'npm run serve'", 30 | "properties": { 31 | "open": { 32 | "type": "boolean", 33 | "description": "Whether to open a browser after the starting serve. You can configure opening url by using 'openUrl' property" 34 | }, 35 | "openUrl": { 36 | "type": "string", 37 | "description": "The url, which will opened after the serve. If empty, local workbench will opened" 38 | }, 39 | "fullScreenErrors": { 40 | "type": "boolean", 41 | "description": "Whether to show errors with a full-screen overlay on UI or not (only in console)" 42 | }, 43 | "loggingLevel": { 44 | "type": "string", 45 | "enum": [ 46 | "minimal", 47 | "normal", 48 | "detailed" 49 | ] 50 | } 51 | } 52 | } 53 | }, 54 | "required": [ 55 | "cli" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /schema/config.1.1.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "SPFx Fast Serve configuration (v1.1)", 4 | "description": "Defines cli and serve configuration structure", 5 | "type": "object", 6 | "properties": { 7 | "cli": { 8 | "type": "object", 9 | "description": "Contains spfx-fast-serve CLI related settings. If you change the spfx-fast-serve cli properties in config.json, you should run spfx-fast-serve again (without parameters) so that it updates your project according to a new configuration", 10 | "properties": { 11 | "isLibraryComponent": { 12 | "type": "boolean" 13 | }, 14 | "port": { 15 | "type": "integer" 16 | } 17 | }, 18 | "required": [ 19 | "isLibraryComponent" 20 | ] 21 | }, 22 | "serve": { 23 | "type": "object", 24 | "description": "Contains serve specific properties. You can change these settings and see the immediate effect after running 'npm run serve'", 25 | "properties": { 26 | "open": { 27 | "type": "boolean", 28 | "description": "Whether to open a browser after the starting serve. You can configure opening url by using 'openUrl' property" 29 | }, 30 | "openUrl": { 31 | "type": "string", 32 | "description": "The url, which will opened after the serve. If empty, local workbench will opened" 33 | }, 34 | "fullScreenErrors": { 35 | "type": "boolean", 36 | "description": "Whether to show errors with a full-screen overlay on UI or not (only in console)" 37 | }, 38 | "loggingLevel": { 39 | "type": "string", 40 | "enum": [ 41 | "minimal", 42 | "normal", 43 | "detailed" 44 | ] 45 | } 46 | } 47 | } 48 | }, 49 | "required": [ 50 | "cli" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /schema/config.latest.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "SPFx Fast Serve configuration", 4 | "description": "Defines cli and serve configuration structure", 5 | "type": "object", 6 | "properties": { 7 | "cli": { 8 | "type": "object", 9 | "description": "Contains spfx-fast-serve CLI related settings. If you change the spfx-fast-serve cli properties in config.json, you should run spfx-fast-serve again (without parameters) so that it updates your project according to a new configuration", 10 | "properties": { 11 | "isLibraryComponent": { 12 | "type": "boolean" 13 | }, 14 | "port": { 15 | "type": "integer" 16 | } 17 | }, 18 | "required": [ 19 | "isLibraryComponent" 20 | ] 21 | }, 22 | "serve": { 23 | "type": "object", 24 | "description": "Contains serve specific properties. You can change these settings and see the immediate effect after running 'npm run serve'", 25 | "properties": { 26 | "open": { 27 | "type": "boolean", 28 | "description": "[Obsolete in SPFx 1.13+] Whether to open a browser after the starting serve. You can configure opening url by using 'openUrl' property" 29 | }, 30 | "openUrl": { 31 | "type": "string", 32 | "description": "The url, which will be opened after the serve." 33 | }, 34 | "fullScreenErrors": { 35 | "type": "boolean", 36 | "description": "Whether to show errors with a full-screen overlay on UI or not (only in console)" 37 | }, 38 | "loggingLevel": { 39 | "type": "string", 40 | "enum": [ 41 | "minimal", 42 | "normal", 43 | "detailed" 44 | ] 45 | }, 46 | "hotRefresh": { 47 | "type": "boolean", 48 | "description": "[SPFx 1.12+] Enables Hot Module Replacement (HMR). This feature currently is experimental. Read more https://github.com/s-KaiNet/spfx-fast-serve#configuration-options" 49 | }, 50 | "eslint": { 51 | "type": "boolean", 52 | "description": "[SPFx 1.13+] Enables eslint for the project. Read more https://github.com/s-KaiNet/spfx-fast-serve#configuration-options" 53 | }, 54 | "reactProfiling": { 55 | "type": "boolean", 56 | "description": "[SPFx 1.13+] Enables react profiling mode. You can profile SPFx solution using React Chromium extension" 57 | }, 58 | "containers": { 59 | "type": "boolean", 60 | "description": "[SPFx 1.13+] By default fast-serve automatically detects containerized environment (like Docker) and applies needed configuration. But if it doesn't work for you, you can explicitly disable or enable support for containers using this option." 61 | } 62 | } 63 | } 64 | }, 65 | "required": [ 66 | "cli" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /schema/config.v2.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "SPFx Fast Serve configuration", 4 | "description": "Defines serve configuration structure", 5 | "type": "object", 6 | "properties": { 7 | "serve": { 8 | "type": "object", 9 | "description": "Contains serve specific properties. You can change these settings and see the immediate effect after running 'npm run serve'", 10 | "properties": { 11 | "port": { 12 | "type": "integer", 13 | "description": "HTTP port to use to serve the bundles (default: 4321)" 14 | }, 15 | "memory": { 16 | "type": "integer", 17 | "description": "Memory limits for the dev server in MB (default: 8192)" 18 | }, 19 | "isLibraryComponent": { 20 | "type": "boolean", 21 | "description": "Should be true, when running inside library component project type (default:false)" 22 | }, 23 | "locale": { 24 | "type": "string", 25 | "description": "Local code when running in a multi-language scenario" 26 | }, 27 | "config": { 28 | "type": "string", 29 | "description": "Serve configuration to run on a startup" 30 | }, 31 | "openUrl": { 32 | "type": "string", 33 | "description": "Url to open on a startup. If empty, no url will be opened" 34 | }, 35 | "loggingLevel": { 36 | "type": "string", 37 | "enum": [ 38 | "minimal", 39 | "normal", 40 | "detailed" 41 | ], 42 | "description": "Logging level, 'minimal' notifies about errors and new builds only, 'normal' adds bundle information, 'detailed' displays maximum information about each bundle (default: 'normal')" 43 | }, 44 | "fullScreenErrors": { 45 | "type": "boolean", 46 | "description": "Whether to show errors with a full-screen overlay on UI or not (only in console)" 47 | }, 48 | "eslint": { 49 | "type": "boolean", 50 | "description": "ESLint support (default: true)" 51 | }, 52 | "hotRefresh": { 53 | "type": "boolean", 54 | "description": "When true, enables webpack's Hot Module Replacement (HMR) feature, read more here - https://github.com/s-KaiNet/spfx-fast-serve/blob/master/docs/HMR.md" 55 | }, 56 | "reactProfiling": { 57 | "type": "boolean", 58 | "description": "When true, enables react profiling mode through React Chrome extension (default: false)" 59 | }, 60 | "containers": { 61 | "type": "boolean", 62 | "description": "Explicitly enables containerized environment support (default: false)" 63 | }, 64 | "debug": { 65 | "type": "boolean", 66 | "description": "When true, enables debug mode (default: false)" 67 | } 68 | } 69 | } 70 | }, 71 | "required": [ 72 | "serve" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /src/commands/BaseCommand.ts: -------------------------------------------------------------------------------- 1 | export abstract class BaseCommand { 2 | public abstract execute(): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /src/commands/Install.ts: -------------------------------------------------------------------------------- 1 | import { BaseCommand } from './BaseCommand'; 2 | import { prompt } from 'enquirer'; 3 | import { detect, PM } from 'detect-package-manager'; 4 | import { logger } from '../common/Logger'; 5 | import chalk from 'chalk'; 6 | import { spawnProcess } from '../common/utils'; 7 | 8 | export class Install extends BaseCommand { 9 | 10 | private force: boolean; 11 | 12 | constructor() { 13 | super(); 14 | 15 | const args = process.argv.slice(2); 16 | this.force = args.indexOf('--force-install') !== -1; 17 | } 18 | 19 | public async execute(): Promise { 20 | const pm = await detect(); 21 | 22 | const message = `Now install dependencies ('${pm} install') and execute '${pm} run serve' afterwards.`; 23 | const completed = 'spfx-fast-serve configuration is completed!'; 24 | let answer: { install: boolean }; 25 | 26 | if (this.force) { 27 | await this.installDependencies(pm, completed); 28 | return; 29 | } 30 | 31 | logger.info(chalk.bgMagenta.white(message)); 32 | logger.newLine(); 33 | 34 | try { 35 | answer = await prompt<{ install: boolean }>({ 36 | name: 'install', 37 | type: 'confirm', 38 | initial: true, 39 | message: 'Do you want to install the dependencies now? ("Enter" to install, "Esc" to finish without installing)', 40 | }); 41 | } catch (error) { 42 | logger.newLine(); 43 | logger.success(chalk.green(completed)); 44 | logger.newLine(); 45 | return; 46 | } 47 | 48 | if (answer.install) { 49 | await this.installDependencies(pm, completed); 50 | } else { 51 | logger.newLine(); 52 | logger.success(chalk.green(completed)); 53 | logger.newLine(); 54 | } 55 | } 56 | 57 | private async installDependencies(pm: PM, message: string): Promise { 58 | logger.newLine(); 59 | 60 | logger.info(chalk.bgMagenta.white(`Executing '${pm} install'...`)); 61 | logger.newLine(); 62 | 63 | await spawnProcess(pm, ['install']); 64 | 65 | logger.newLine(); 66 | logger.success(chalk.green(message)); 67 | logger.newLine(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/commands/PatchGulpFile.ts: -------------------------------------------------------------------------------- 1 | import { readFile } from 'fs/promises'; 2 | import * as path from 'path'; 3 | import chalk from 'chalk'; 4 | import replace, { ReplaceInFileConfig } from 'replace-in-file'; 5 | 6 | import { logger } from '../common/Logger'; 7 | import { BaseCommand } from './BaseCommand'; 8 | import { getTemplatesPath } from '../common/utils'; 9 | 10 | export class PatchGulpFile extends BaseCommand { 11 | public async execute(): Promise { 12 | const gulpfilePath = path.join(process.cwd(), 'gulpfile.js'); 13 | const currentGulpFile = (await readFile(gulpfilePath)).toString(); 14 | 15 | if (currentGulpFile.indexOf('spfx-fast-serve-helpers') !== -1) { 16 | logger.success(chalk.blueBright('It looks like your gulpfile.js was patched before, skipping.')); 17 | logger.newLine(); 18 | return; 19 | } 20 | 21 | if (currentGulpFile.indexOf('build.configureWebpack.mergeConfig') !== -1) { 22 | logger.warning(chalk.yellowBright('We detected that you use webpack\'s task \'mergeConfig\' feature in your gulpfile.js. Make sure that you applied corresponding changes in webpack.extend.js as well.')); 23 | logger.newLine(); 24 | logger.log(chalk.yellowBright('Please read https://github.com/s-KaiNet/spfx-fast-serve#webpack-extensibility for details.')); 25 | logger.newLine(); 26 | } 27 | 28 | const replaceContent = (await readFile(getTemplatesPath('gulpfile.js'))).toString(); 29 | 30 | const options: ReplaceInFileConfig = { 31 | files: gulpfilePath, 32 | from: /build\.initialize.*;/g, 33 | to: replaceContent, 34 | glob: { 35 | windowsPathsNoEscape: true 36 | } 37 | }; 38 | 39 | await replace(options); 40 | 41 | logger.success(chalk.blueBright('Patched gulpfile.js.')); 42 | logger.newLine(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/commands/PatchPackageJson.ts: -------------------------------------------------------------------------------- 1 | import { readFile, writeFile } from 'fs/promises'; 2 | import chalk from 'chalk'; 3 | import * as path from 'path'; 4 | import detectIndent from 'detect-indent'; 5 | 6 | import { logger } from '../common/Logger'; 7 | import { getSpfxMinorVersion, isBeta } from '../common/utils'; 8 | import { BaseCommand } from './BaseCommand'; 9 | 10 | export class PatchPackageJson extends BaseCommand { 11 | public async execute(): Promise { 12 | const packagePath = path.join(process.cwd(), 'package.json'); 13 | const packageString = (await readFile(packagePath)).toString(); 14 | const indent = detectIndent(packageString).indent || ' '; 15 | const packageJson = JSON.parse(packageString); 16 | const minorVersion = getSpfxMinorVersion(); 17 | 18 | let version = `~1.${minorVersion}.0`; 19 | const dependency = 'spfx-fast-serve-helpers'; 20 | 21 | if (isBeta()) { 22 | version = `${version}-beta.0`; 23 | } 24 | 25 | if (packageJson.devDependencies[dependency] && packageJson.devDependencies[dependency] !== version) { 26 | logger.warning(chalk.yellowBright('Your dependency \'' + dependency + '\' version \'' + packageJson.devDependencies[dependency] + '\' will be replaced with version \'' + version + '\'')); 27 | } 28 | 29 | packageJson.devDependencies[dependency] = version; 30 | 31 | packageJson.scripts = packageJson.scripts || {}; 32 | if (packageJson.scripts['serve']) { 33 | logger.warning(chalk.yellowBright('Your npm \'serve\' command will be replaced.')); 34 | logger.newLine(); 35 | } 36 | 37 | packageJson.scripts['serve'] = 'fast-serve'; 38 | 39 | await writeFile(packagePath, JSON.stringify(packageJson, null, indent)); 40 | 41 | logger.success(chalk.blueBright('Updated package.json.')); 42 | logger.newLine(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/commands/Pipeline.ts: -------------------------------------------------------------------------------- 1 | import { BaseCommand } from './BaseCommand'; 2 | 3 | export class Pipeline { 4 | private commands: BaseCommand[]; 5 | 6 | constructor(...commands: BaseCommand[]) { 7 | this.commands = commands; 8 | } 9 | 10 | public async execute(): Promise { 11 | for (const command of this.commands) { 12 | await command.execute(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Pipeline'; 2 | export * from './PatchGulpFile'; 3 | export * from './PatchPackageJson'; 4 | export * from './BaseCommand'; 5 | export * from './Install'; 6 | -------------------------------------------------------------------------------- /src/common/Logger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import logSymbols from 'log-symbols'; 3 | 4 | class Logger { 5 | private logger: (...args: any[]) => void; 6 | 7 | constructor() { 8 | const args = process.argv.slice(2); 9 | const silent = args.indexOf('--silent') !== -1; 10 | 11 | if (silent) { 12 | this.logger = () => 0; 13 | } else { 14 | this.logger = console.log; 15 | } 16 | } 17 | 18 | public success(message: string) { 19 | this.logger(logSymbols.success, message); 20 | } 21 | 22 | public log(message: string) { 23 | this.logger(message); 24 | } 25 | 26 | public warning(message: string) { 27 | this.logger(logSymbols.warning, message); 28 | } 29 | 30 | public info(message: string) { 31 | this.logger(logSymbols.info, message); 32 | } 33 | 34 | public newLine() { 35 | this.logger(''); 36 | } 37 | } 38 | 39 | export const logger = new Logger(); 40 | -------------------------------------------------------------------------------- /src/common/consts.ts: -------------------------------------------------------------------------------- 1 | export const FastServeFolderName = 'fast-serve'; 2 | export const ConfigFileName = 'config.json'; 3 | export const SchemaUrl = 'https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/master/schema/config.latest.schema.json'; 4 | export const SchemaUrlOld = 'https://raw.githubusercontent.com/s-KaiNet/spfx-fast-serve/master/schema/config.1.1.schema.json'; 5 | export const WebpackExtendFileName = 'webpack.extend.js'; 6 | export const GulpFileName = 'gulpfile.js'; 7 | -------------------------------------------------------------------------------- /src/common/utils.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { spawn } from 'cross-spawn'; 3 | const packagePath = path.join(process.cwd(), 'package.json'); 4 | const dependecyToCheck = '@microsoft/sp-core-library'; 5 | // eslint-disable-next-line @typescript-eslint/no-var-requires 6 | const packageJson = require(packagePath); 7 | 8 | export function getTemplatesPath(fileName: string) { 9 | const basePath = 'templates/'; 10 | 11 | return path.join(__dirname, '..', basePath + fileName); 12 | } 13 | 14 | export function getSpfxMinorVersion() { 15 | let version: string = getSpfxVersionString(); 16 | 17 | if (version.indexOf('~') === 0 || version.indexOf('^') === 0) { 18 | version = version.substr(1); 19 | } 20 | return parseInt(version.split('.')[1]); 21 | } 22 | 23 | export function isBeta() { 24 | const version: string = getSpfxVersionString(); 25 | 26 | return version.indexOf('-beta') !== -1 || version.indexOf('-rc') !== -1; 27 | } 28 | 29 | export async function spawnProcess(program: string, args: string[], env?: typeof process.env): Promise { 30 | return new Promise((resolve, reject) => { 31 | const proc = spawn(program, args, { 32 | stdio: 'inherit', 33 | env: env || process.env 34 | }); 35 | 36 | process.on('SIGTERM', () => proc.kill('SIGTERM')) 37 | process.on('SIGINT', () => proc.kill('SIGINT')) 38 | process.on('SIGBREAK', () => proc.kill('SIGBREAK')) 39 | process.on('SIGHUP', () => proc.kill('SIGHUP')) 40 | 41 | proc.on('exit', (code, signal) => { 42 | let crossEnvExitCode = code 43 | if (crossEnvExitCode === null) { 44 | crossEnvExitCode = signal === 'SIGINT' ? 0 : 1 45 | } 46 | 47 | if (crossEnvExitCode === 1) { 48 | reject(); 49 | } else { 50 | resolve(); 51 | } 52 | }); 53 | }); 54 | } 55 | 56 | function getSpfxVersionString() { 57 | let version: string = packageJson.dependencies[dependecyToCheck]; 58 | 59 | if (!version) { // try version from yo-rc.json 60 | const yoPath = path.join(process.cwd(), '.yo-rc.json'); 61 | // eslint-disable-next-line @typescript-eslint/no-var-requires 62 | const yoJson = require(yoPath); 63 | version = yoJson['@microsoft/generator-sharepoint']?.version; 64 | 65 | if (!version) { 66 | throw new Error('Cannot find SPFx version in package.json or .yo-rc.json'); 67 | } 68 | 69 | return version; 70 | 71 | } else { 72 | return version; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import updateNotifier from 'update-notifier'; 4 | import chalk from 'chalk'; 5 | import { 6 | PatchGulpFile, 7 | PatchPackageJson, 8 | Pipeline, 9 | Install 10 | } from './commands'; 11 | import { getSpfxMinorVersion } from './common/utils'; 12 | import { logger } from './common/Logger'; 13 | 14 | // eslint-disable-next-line @typescript-eslint/no-var-requires 15 | const pkg = require('./../package.json'); 16 | 17 | updateNotifier({ pkg }).notify(); 18 | 19 | const minorVersion = getSpfxMinorVersion(); 20 | 21 | if (minorVersion < 17) { 22 | logger.warning(chalk.yellowBright(`Your SPFx version 1.${minorVersion} is not supported. For SPFx < 1.17 use spfx-fast-serve@3.x. More info https://github.com/s-KaiNet/spfx-fast-serve#which-sharepoint-framework-versions-are-supported`)); 23 | process.exit(1); 24 | } 25 | 26 | const pipeline = new Pipeline( 27 | new PatchGulpFile(), 28 | new PatchPackageJson(), 29 | new Install()); 30 | 31 | (async () => { 32 | await pipeline.execute(); 33 | })(); 34 | -------------------------------------------------------------------------------- /src/templates/gulpfile.js: -------------------------------------------------------------------------------- 1 | /* fast-serve */ 2 | const { addFastServe } = require("spfx-fast-serve-helpers"); 3 | addFastServe(build); 4 | /* end of fast-serve */ 5 | 6 | build.initialize(require('gulp')); 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "allowSyntheticDefaultImports": true, 9 | "esModuleInterop": true, 10 | "noImplicitAny": true, 11 | "removeComments": true, 12 | "skipLibCheck": true, 13 | "outDir": "./lib", 14 | "types": [ 15 | "node", 16 | ] 17 | }, 18 | "include": [ 19 | "src/**/*.ts" 20 | ] 21 | } 22 | --------------------------------------------------------------------------------