├── .azure-devops ├── build.yml ├── cleanup.yaml ├── devcerts.yml ├── edgewebview.yml ├── full-pipeline.yml ├── install.yml ├── lint.yml └── test.yml ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── bug_report.md └── pull_request_template.md ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── LICENSE ├── SECURITY.md ├── generator-office.yml ├── package-lock.json ├── package.json ├── readme.md ├── src ├── app │ ├── config │ │ ├── projectProperties.json │ │ └── projectsJsonData.ts │ ├── defaults.ts │ ├── helpers │ │ └── helperMethods.ts │ ├── index.ts │ └── templates │ │ ├── hosts │ │ ├── excel │ │ │ └── manifest.xml │ │ ├── onenote │ │ │ └── manifest.xml │ │ ├── outlook │ │ │ └── manifest.xml │ │ ├── powerpoint │ │ │ └── manifest.xml │ │ ├── project │ │ │ └── manifest.xml │ │ └── word │ │ │ └── manifest.xml │ │ └── manifest-only │ │ ├── .gitignore │ │ ├── assets │ │ ├── icon-128.png │ │ ├── icon-16.png │ │ ├── icon-32.png │ │ ├── icon-64.png │ │ ├── icon-80.png │ │ └── logo-filled.png │ │ └── package.json ├── docs │ ├── assets │ │ ├── browsersync.gif │ │ ├── designtemplate.gif │ │ ├── gettingstarted-fast.gif │ │ ├── gettingstarted-slow.gif │ │ ├── quickstart.gif │ │ ├── ssl-chrome-bypass.gif │ │ ├── ssl-chrome-devtool.png │ │ ├── ssl-chrome-error.png │ │ ├── ssl-chrome-getcert.png │ │ ├── ssl-chrome-good.png │ │ ├── ssl-edge-error.png │ │ ├── ssl-ie-01.png │ │ ├── ssl-ie-02.png │ │ ├── ssl-ie-03.png │ │ ├── ssl-ie-04.png │ │ ├── ssl-ie-05.png │ │ ├── ssl-ie-06.png │ │ ├── ssl-ie-07.png │ │ ├── ssl-ie-08.png │ │ ├── ssl-ie-09.png │ │ ├── ssl-ie-10.png │ │ ├── ssl-keychain-01.png │ │ ├── ssl-keychain-02.png │ │ ├── ssl-windows-01.png │ │ ├── ssl-windows-02.png │ │ ├── ssl-windows-03.png │ │ ├── ssl-windows-04.png │ │ ├── ssl-windows-05.png │ │ ├── ssl-windows-06.png │ │ ├── ssotemplate-1.gif │ │ ├── ssotemplate.gif │ │ ├── validator.gif │ │ └── yo-office-install-cert-dialog.png │ └── ssl.md └── test │ ├── convert-to-single-host.ts │ ├── manifest-only-project.ts │ └── utils.ts └── tsconfig.json /.azure-devops/build.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Build' 4 | inputs: 5 | command: custom 6 | customCommand: 'run build' -------------------------------------------------------------------------------- /.azure-devops/cleanup.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3 3 | displayName: "Cleanup" -------------------------------------------------------------------------------- /.azure-devops/devcerts.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | echo Install Office-AddinDev-Certs at machine level 4 | call npm install office-addin-dev-certs 5 | call npx office-addin-dev-certs install --machine 6 | 7 | displayName: 'Install add-in dev cert' -------------------------------------------------------------------------------- /.azure-devops/edgewebview.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | echo Enable EdgeWebView Loopback 4 | call npx office-addin-dev-settings appcontainer EdgeWebView --loopback --yes 5 | 6 | echo Set Edge WebView Registry Settings 7 | 8 | set PATH1="HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646" 9 | 10 | reg add %PATH1% /f /v DisplayName /t REG_SZ /d "@{Microsoft.Win32WebViewHost_10.0.19041.423_neutral_neutral_cw5n1h2txyewy?ms-resource://Windows.Win32WebViewHost/resources/DisplayName}" 11 | reg add %PATH1% /f /v Moniker /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy" 12 | 13 | set PATH2="HKEY_CURRENT_USER\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646\Children\S-1-15-2-1310292540-1029022339-4008023048-2190398717-53961996-4257829345-603366646-3829197285-1050560373-949424154-522343454" 14 | 15 | reg add %PATH2% /f /v DisplayName /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy/123" 16 | reg add %PATH2% /f /v Moniker /t REG_SZ /d "123" 17 | reg add %PATH2% /f /v ParentMoniker /t REG_SZ /d "microsoft.win32webviewhost_cw5n1h2txyewy" 18 | 19 | displayName: 'Enable Edge WebView' -------------------------------------------------------------------------------- /.azure-devops/full-pipeline.yml: -------------------------------------------------------------------------------- 1 | resources: 2 | repositories: 3 | - repository: OfficePipelineTemplates 4 | type: git 5 | name: 1ESPipelineTemplates/OfficePipelineTemplates 6 | ref: refs/tags/release 7 | extends: 8 | template: /v1/Office.Official.PipelineTemplate.yml@OfficePipelineTemplates 9 | parameters: 10 | pool: 11 | name: OE-OfficeClientApps 12 | sdl: 13 | eslint: 14 | configuration: required 15 | parser: '@typescript-eslint/parser' 16 | parserOptions: sourceType:module 17 | stages: 18 | - stage: stage 19 | jobs: 20 | - job: Windows_10_Latest 21 | steps: 22 | - template: /.azure-devops/install.yml@self 23 | # - template: ./lint.yml 24 | - template: /.azure-devops/build.yml@self 25 | - template: /.azure-devops/devcerts.yml@self 26 | - template: /.azure-devops/edgewebview.yml@self 27 | - template: /.azure-devops/test.yml@self 28 | - job: Linux 29 | pool: 30 | name: Azure-Pipelines-1ESPT-ExDShared 31 | image: ubuntu-latest 32 | os: linux 33 | steps: 34 | - template: /.azure-devops/install.yml@self 35 | - template: /.azure-devops/build.yml@self 36 | - template: /.azure-devops/test.yml@self 37 | -------------------------------------------------------------------------------- /.azure-devops/install.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Install' -------------------------------------------------------------------------------- /.azure-devops/lint.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: Npm@1 3 | displayName: 'Lint' 4 | inputs: 5 | command: custom 6 | customCommand: 'run lint' -------------------------------------------------------------------------------- /.azure-devops/test.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: CmdLine@2 3 | inputs: 4 | script: | 5 | echo Running Tests 6 | 7 | npm install office-addin-manifest 8 | npm run test 9 | 10 | echo Done running tests 11 | displayName: 'Run Tests' -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "jsx": true, 5 | "useJSXTextNode": true 6 | }, 7 | "extends": [ 8 | "plugin:@typescript-eslint/recommended", 9 | "prettier" 10 | ], 11 | "plugins": ["@typescript-eslint"] 12 | } 13 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @OfficeDev/office-platform-devx -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Prerequisites 11 | 12 | Please answer the following questions before submitting an issue. 13 | **YOU MAY DELETE THE PREREQUISITES SECTION.** 14 | - [ ] I am running the latest version of Node and the tools 15 | - [ ] I checked the documentation and found no answer 16 | - [ ] I checked to make sure that this issue has not already been filed 17 | 18 | 19 | # Expected behavior 20 | 21 | Please describe the behavior you were expecting 22 | 23 | 24 | # Current behavior 25 | 26 | Please provide information about the failure. What is the current behavior? If it is not a bug, please add it to [UserVoice](https://officespdev.uservoice.com/forums/224641-feature-requests-and-feedback/category/164751-office-developer-tools), so that it gets added to our feature roadmap. 27 | 28 | 29 | ## Steps to Reproduce 30 | 31 | Please provide detailed steps for reproducing the issue. 32 | 33 | 1. step 1 34 | 2. step 2 35 | 3. you get it... 36 | 37 | 38 | ## Context 39 | 40 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 41 | 42 | * Operating System: 43 | * Node version: 44 | * Office version: 45 | * Tool version: 46 | 47 | ## Failure Logs 48 | 49 | Please include any relevant log snippets, screenshots or code samples here. 50 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thank you for your pull request. Please provide the following information. 2 | 3 | --- 4 | 5 | 1. **Do these changes impact *User Experience*?** (e.g., how the user interacts with Yo Office and/or the files and folders the user sees in the project that Yo Office creates) 6 | > 7 | > * [ ] Yes 8 | > * [ ] No 9 | 10 | If Yes, briefly describe what is impacted. 11 | 12 | 13 | 2. **Do these changes impact *documentation*?** (e.g., a tutorial on https://learn.microsoft.com/en-us/office/dev/add-ins/overview/office-add-ins) 14 | > 15 | > * [ ] Yes 16 | > * [ ] No 17 | 18 | If Yes, briefly describe what is impacted. 19 | 20 | 21 | 3. **Validation/testing performed**: 22 | 23 | Describe manual testing done. 24 | 25 | 4. **Platforms tested**: 26 | 27 | > * [ ] Windows 28 | > * [ ] Mac 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 2 | # See LICENSE in the project root for license information. 3 | 4 | # Created by https://www.gitignore.io/api/osx,windows 5 | 6 | ### OSX ### 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | 34 | ### Windows ### 35 | # Windows image file caches 36 | Thumbs.db 37 | ehthumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | 42 | # Recycle Bin used on file shares 43 | $RECYCLE.BIN/ 44 | 45 | # Windows Installer files 46 | *.cab 47 | *.msi 48 | *.msm 49 | *.msp 50 | 51 | # Windows shortcuts 52 | *.lnk 53 | 54 | # Ignore the following folders 55 | .vs 56 | .idea 57 | *.log 58 | obj 59 | bin 60 | *.user 61 | 62 | node_modules/** 63 | packages/** 64 | typings/** 65 | generators/** 66 | .vscode/** -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 2 | # See LICENSE in the project root for license information. 3 | 4 | !generators/** -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to Microsoft Office Project Generator 2 | 3 | Thank you for your interest in this library! Your contributions and improvements will help the developer community. 4 | * [Ways to contribute](https://github.com/OfficeDev/generator-office/blob/master/CONTRIBUTING.md#ways-to-contribute) 5 | * [Before we can accept your pull request](https://github.com/OfficeDev/generator-office/blob/master/CONTRIBUTING.md#before-we-can-accept-your-pull-request) 6 | * [Use GitHub, Git, and this repository](https://github.com/OfficeDev/generator-office/blob/master/CONTRIBUTING.md#use-github-git-and-this-repository) 7 | * [More resources](https://github.com/OfficeDev/generator-office/blob/master/CONTRIBUTING.md#more-resources) 8 | 9 | ## Ways to contribute 10 | You can contribute to Office Project Generator in these ways: 11 | * Provide feedback 12 | * Report bugs and suggest enhancements via [GitHub Issues](https://github.com/OfficeDev/generator-office/issues) 13 | * Do it yourself 14 | * Fix [Issues](https://github.com/OfficeDev/generator-office/issues) yourself and submit the changes as a [Pull Request](https://github.com/OfficeDev/generator-office/pulls) for review. 15 | * You can submit [code comment contributions](https://github.com/OfficeDev/generator-office/blob/master/CONTRIBUTING.md#provide-better-code-comments) where you want a better explanation of the code. 16 | 17 | ## Before we can accept your pull request 18 | If you are in one of the following groups, you need to send us a signed Contribution License Agreement (CLA) before we can accept your pull request: 19 | * Members of the Microsoft Open Technologies group 20 | * Contributors who don't work for Microsoft 21 | 22 | As a community member, you must sign the Contribution License Agreement (CLA) before you can contribute large submissions to this project, but you need to complete and submit the documentation only once. The Office 365 organization on GitHub will send a link to the CLA that we want you to sign via email. By signing the CLA, you acknowledge the rights of the GitHub community to use any code that you submit. The intellectual property represented by the code contribution is licensed for use by Microsoft open source projects. Please carefully review the document, as you may also need to have your employer sign the document. 23 | 24 | Signing the Contribution License Agreement (CLA) does not grant you rights to commit to the main repository, but it does mean that the Office Developer and Office Developer Content Publishing teams will be able to review and consider your contributions and you will get credit if we do. 25 | 26 | Once we receive and process your CLA, we'll do our best to review your pull requests within 10 business days. 27 | 28 | ## Use GitHub, Git, and this repository 29 | **Note**: Most of the information in this section can be found in [GitHub Help](https://help.github.com/) articles. If you're familiar with Git and GitHub, skip to the Contribute code section for the specifics of the code contributions for this repository. 30 | 31 | ### To set up your fork of the repository 32 | 1. Set up a GitHub account so you can contribute to this project. If you haven't done this, go to [GitHub](https://github.com/join) and do it now. 33 | 1. Install Git on your computer. Follow the steps in the Setting up Git Tutorial. 34 | 1. Create your own fork of this repository. To do this, at the top of the page, choose the Fork button. 35 | 1. Copy your fork to your computer. To do this, open Git Bash. At the command prompt enter: 36 | 37 | ``` 38 | git clone https://github.com//.git 39 | ``` 40 | Next, create a reference to the root repository by entering these commands: 41 | ``` 42 | cd 43 | git remote add upstream https://github.com/OfficeDev/.git 44 | git fetch upstream 45 | ``` 46 | Congratulations! You've now set up your repository. You won't need to repeat these steps again. 47 | 48 | ### Provide better code comments 49 | Code comments make code samples even better by helping developers learn to use the code correctly in their own applications. If you spot a class, method, or section of code that you think could use better descriptions, then create a pull request with your code comments. 50 | In general we want our code comments to follow these guidelines: 51 | 52 | * Any code that has associated documentation displayed in an IDE (such as IntelliSense, or JavaDocs) has code comments. 53 | * Classes, methods, parameters, and return values have clear descriptions. 54 | * Exceptions and errors are documented. 55 | * Remarks exist for anything special or notable about the code. 56 | * Sections of code that have complex algorithms have appropriate comments describing what they do. 57 | * Code added from Stack Overflow, or any other source, is clearly attributed. 58 | 59 | ### Contribute code 60 | To make the contribution process as seamless as possible for you, follow this procedure. 61 | 62 | 1. Create a new branch. 63 | 1. Add new content or edit existing content. 64 | 1. Submit a pull request to the main repository. 65 | 1. Delete the branch. 66 | 67 | Limit each branch to a single module to streamline the workflow and reduce the chance of merge conflicts. The following types of contribution are appropriate for a new branch: 68 | 69 | * A correction to the slide deck 70 | * Instruction step fixes or additional clarification in hands on labs 71 | * Code fixes in sample starter or completed projects 72 | * Spelling and grammar edits on a hands on lab 73 | 74 | #### Create a new branch 75 | 1. Open GitBash. 76 | 1. Type `git pull upstream master:` at the prompt. This creates a new branch locally that's copied from the latest OfficeDev master branch. **Note**: For internal contributors, replace `master` in the command with the branch for the publishing date you're targeting. 77 | 1. Type `git push origin ` at the prompt. This will alert GitHub to the new branch. You should now see the new branch in your fork of the repository on GitHub. 78 | 1. Type `git checkout ` to switch to your new branch. 79 | 80 | #### Add new content or edit existing content 81 | Navigate to the repository on your computer. On a Windows PC, the repository files are in `C:\Users\\`. 82 | Use the IDE of your choice to modify and build the library. Once you have completed your change, commented your code, and test, check the code into the remote branch on GitHub. 83 | 84 | Be sure to satisfy all of the requirements in the following list before submitting a pull request: 85 | * Follow the code style found in the cloned repository code. 86 | * Code must be tested. 87 | * Test the library UI thoroughly to be sure nothing has been broken by your change. 88 | Keep the size of your code change reasonable. If the repository owner cannot review your code change in 4 hours or less, your pull request may not be reviewed and approved quickly. 89 | * Avoid unnecessary changes to cloned or forked code. The reviewer will use a tool to find the differences between your code and the original code. Whitespace changes are called out along with your code. Be sure your changes will help improve the content. 90 | 91 | #### Push your code to the remote GitHub branch 92 | The files in `C:\Users\\` are a working copy of the new branch that you created in your local repository. Changing anything in this folder doesn't affect the local repository until you commit a change. To commit a change to the local repository, type the following commands in GitBash: 93 | ``` 94 | git add . 95 | git commit -v -a -m "" 96 | ``` 97 | The `add` command adds your changes to a staging area in preparation for committing them to the repository. The period after the `add` command specifies that you want to stage all of the files that you added or modified, checking subfolders recursively. (If you don't want to commit all of the changes, you can add specific files. You can also undo a commit. For help, type `git add -help` or `git status`.) 98 | 99 | The `commit` command applies the staged changes to the repository. The switch `-m` means you are providing the commit comment in the command line. The `-v` and `-a` switches can be omitted. The `-v` switch is for verbose output from the command, and `-a` does what you already did with the add command. 100 | 101 | You can commit multiple times while you are doing your work, or you can commit once when you're done. 102 | 103 | #### Submit a pull request to the main repository 104 | 105 | When you're finished with your work and are ready to have it merged into the central repository, follow these steps. 106 | 107 | 1. In GitBash, type `git push origin ` at the command prompt. In your local repository, origin refers to your GitHub repository that you cloned the local repository from. This command pushes the current state of your new branch, including all commits made in the previous steps, to your GitHub fork. 108 | 1. On the GitHub site, navigate in your fork to the new branch. 109 | 1. Click the **Pull Request** button at the top of the page. 110 | 1. Ensure that the Base branch is `OfficeDev/@master` and the Head branch is `/@`. 111 | 1. Click the **Update Commit Range** button. 112 | 1. Give your pull request a Title, and describe all the changes you're making. If your bug fixes a UserVoice item or GitHub issue, be sure to reference that issue in the description. 113 | 1. Submit the pull request. 114 | 115 | One of the site administrators will now process your pull request. Your pull request will surface on the `OfficeDev/` site under Issues. When the pull request is accepted, the issue will be resolved. 116 | 117 | #### Create a new branch after merging 118 | After a branch is successfully merged (i.e., your pull request is accepted), don't continue working in the local branch that was successfully merged upstream. This can lead to merge conflicts if you submit another pull request. Instead, if you want to do another update, create a new local branch from the successfully merged upstream branch. 119 | 120 | For example, suppose your local branch X was successfully merged into the OfficeDev/generator-office master branch and you want to make additional updates to the content that was merged. Create a new local branch, X2, from the OfficeDev/generator-office master branch. To do this, open GitBash and execute the following commands: 121 | ``` 122 | cd 123 | git pull upstream master:X2 124 | git push origin X2 125 | ``` 126 | You now have local copies (in a new local branch) of the work that you submitted in branch X. The X2 branch also contains all the work other developers have merged, so if your work depends on others' work (for example, a base class), it is available in the new branch. You can verify that your previous work (and others' work) is in the branch by checking out the new branch... 127 | ``` 128 | git checkout X2 129 | ``` 130 | ...and verifying the code. (The `checkout` command updates the files in `C:\Users\\generator-office` to the current state of the X2 branch.) Once you check out the new branch, you can make updates to the code and commit them as usual. However, to avoid working in the merged branch (X) by mistake, it's best to delete it (see the following **Delete a branch** section). 131 | 132 | #### Delete a branch 133 | Once your changes are successfully merged into the central repository, you can delete the branch you used because you no longer need it. Any additional work requires a new branch. 134 | 135 | To delete your branch follow these steps: 136 | 137 | 1. In GitBash type `git checkout master` at the command prompt. This ensures that you aren't in the branch to be deleted (which isn't allowed). 138 | 1. Next, type `git branch -d ` at the command prompt. This deletes the branch on your local machine only if it has been successfully merged to the upstream repository. (You can override this behavior with the `–D` flag, but first be sure you want to do this.) 139 | 1. Finally, type `git push origin :` at the command prompt (a space before the colon and no space after it). This will delete the branch on your github fork. 140 | 141 | Congratulations, you have successfully contributed to the project. 142 | 143 | ## More resources 144 | * To learn more about Markdown, see [Daring Fireball](http://daringfireball.net/). 145 | * To learn more about using Git and GitHub, check out the [GitHub Help section](http://help.github.com/). 146 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 Microsoft Corporation. 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /generator-office.yml: -------------------------------------------------------------------------------- 1 | ### YamlMime:Sample 2 | page_type: sample 3 | products: 4 | - office-365 5 | languages: 6 | - html 7 | - typescript 8 | extensions: 9 | contentType: tools 10 | technologies: 11 | - Add-ins 12 | createdDate: 8/21/2015 1:12:15 PM 13 | title: Yo Office 14 | description: Yeoman generator for building Microsoft Office related projects. 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-office", 3 | "version": "3.0.0", 4 | "description": "Yeoman generator for creating Microsoft Office projects using any text editor.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/OfficeDev/generator-office/" 8 | }, 9 | "scripts": { 10 | "copy": "copyfiles \"src/app/templates/**/*.*\" -u 2 generators/app && copyfiles \"src/app/config/**/projectProperties.json*\" -u 3 generators/app/templates && copyfiles \"src/app/config/**/projectProperties.json*\" -u 3 generators/test", 11 | "lint": "eslint --ext .ts src", 12 | "tsc": "tsc -p tsconfig.json", 13 | "tsc-watch": "tsc -p tsconfig.json -w", 14 | "start": "rimraf generators && concurrently \"npm run tsc-watch\" \"npm run copy-watch\"", 15 | "build": "rimraf generators && concurrently \"npm run tsc\" \"npm run copy\"", 16 | "test": "mocha --timeout 0 -r ts-node/register generators/test/**.js" 17 | }, 18 | "engines": { 19 | "node": ">=14" 20 | }, 21 | "homepage": "https://github.com/officedev/generator-office", 22 | "license": "MIT", 23 | "author": "OfficeDev", 24 | "files": [ 25 | "generators" 26 | ], 27 | "type": "module", 28 | "main": "generators/index.js", 29 | "keywords": [ 30 | "office", 31 | "yeoman-generator", 32 | "office-add-in", 33 | "add-in", 34 | "outlook", 35 | "excel", 36 | "word", 37 | "powerpoint", 38 | "onenote", 39 | "project" 40 | ], 41 | "dependencies": { 42 | "adm-zip": "0.5.16", 43 | "axios": "^1.7.7", 44 | "chalk": "^5.4.1", 45 | "lodash": "^4.17.21", 46 | "office-addin-usage-data": "^2.0.3", 47 | "open": "^8.4.0", 48 | "uuid": "^8.3.2", 49 | "yeoman-generator": "^7.0.0", 50 | "yosay": "^2.0.2" 51 | }, 52 | "devDependencies": { 53 | "@types/adm-zip": "^0.5.7", 54 | "@types/lodash": "^4.17.14", 55 | "@types/mocha": "^10.0.10", 56 | "@types/node": "^22.10.6", 57 | "@types/request": "^2.48.8", 58 | "@typescript-eslint/eslint-plugin": "^8.0.0", 59 | "@typescript-eslint/parser": "^8.0.0", 60 | "concurrently": "^9.1.2", 61 | "copyfiles": "^2.4.1", 62 | "eslint": "^9.0.0", 63 | "eslint-config-prettier": "9.1.0", 64 | "mocha": "^11.0.0", 65 | "office-addin-manifest": "^2.0.3", 66 | "prettier": "^3.4.2", 67 | "rimraf": "^6.0.1", 68 | "ts-node": "^10.7.0", 69 | "typescript": "^5.2.2", 70 | "yeoman-assert": "^3.1.1", 71 | "yeoman-environment": "^4.4.3", 72 | "yeoman-test": "^10.0.1" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Yeoman generator for Office Add-ins - YO OFFICE! 2 | 3 | [![npm version](https://badge.fury.io/js/generator-office.svg)](http://badge.fury.io/js/generator-office) 4 | [![Downloads](http://img.shields.io/npm/dm/generator-office.svg)](https://npmjs.org/package/generator-office) 5 | 6 | This [Yeoman](http://yeoman.io) generator creates a Node.js [Office Add-in](https://learn.microsoft.com/office/dev/add-ins) project that can be managed with Visual Studio Code or any other editor. You can use it to create Office Add-ins for: 7 | 8 | - Excel 9 | - OneNote 10 | - Outlook 11 | - PowerPoint 12 | - Project 13 | - Word 14 | 15 | In addition you can use it to create add-ins that use single sign-on or Excel custom functions. 16 | 17 | Like other Yeoman generators, this generator simply creates the scaffolding of files for your Office Add-in project. You can choose to create Office Add-in projects using plain HTML, CSS & JavaScript, or using React, and can choose between JavaScript and Typescript as well. 18 | 19 | > **Note:** While you can also use [Visual Studio](https://visualstudio.microsoft.com/vs) to create Office Add-in projects, the Yeoman generator provides more options in terms of the types of projects you can create. For example: 20 | > 21 | > - The Yeoman generator can create Office Add-in projects that use plain HTML, CSS & JavaScript, or React; whereas Visual Studio can only create Office Add-in projects that use plain HTML, CSS & JavaScript. 22 | > - The Yeoman generator can create Office Add-ins projects that use either JavaScript or TypeScript; whereas Visual Studio only creates JavaScript projects which you would need to convert manually if you want to use TypeScript. 23 | > - The Yeoman generator can create add-ins for Excel, OneNote, Outlook, PowerPoint, Project, and Word; whereas Visual Studio can only create add-ins for Excel, Outlook, PowerPoint, and Word. 24 | 25 | For detailed information about installing and using the Yeoman generator to create Office Add-ins, see [Create Office Add-in projects using the Yeoman Generator](https://learn.microsoft.com/office/dev/add-ins/develop/yeoman-generator-overview). 26 | 27 | The generator is normally used in interactive mode, but you can use command line arguments and options as described in the sections below. 28 | 29 | ## Usage 30 | 31 | ```bash 32 | yo office [arguments] [options] 33 | ``` 34 | 35 | ### Command Line Arguments 36 | 37 | The following command line arguments are supported. If using the command line arguments, you must use them in the order cited below, or the generator will prompt you for the values. 38 | 39 | #### Example 40 | 41 | ```bash 42 | yo office --projectType react --name "New Web AddIn" --host word --ts true 43 | ``` 44 | 45 | ```output 46 | _-----_ ╭──────────────────────────╮ 47 | | | │ Welcome to the Office │ 48 | |--(o)--| │ Add-in generator, by │ 49 | `---------´ │ @OfficeDev! Let's create │ 50 | ( _´U`_ ) │ a project together! │ 51 | /___A___\ /╰──────────────────────────╯ 52 | | ~ | 53 | __'.___.'__ 54 | ´ ` |° ´ Y ` 55 | ``` 56 | 57 | #### `--projectType` 58 | 59 | Specifies the project type to create. 60 | 61 | Project Type | Description 62 | ----------- | ------------------------ 63 | taskpane | Task Pane add-in using HTML 64 | react | Task Pane add-in using the React framework 65 | excel-functions | Task Pane add-in with Excel Custom Functions 66 | single-sign-on | Taskpane add-in supporting single-sign-on 67 | nested-app-auth | Taskpane add-in supporting Nested App Auth single sign-on (preview) 68 | manifest | Manifest and related files for an Office Add-in 69 | 70 | - Type: String 71 | - Optional 72 | 73 | #### `--name` 74 | 75 | Title of the project - this is the display name that is written the manifest.xml file. 76 | 77 | - Type: String 78 | - Optional 79 | 80 | > **Note:** The Windows command prompt requires this argument to be in quotes (e.g. "My Office Add-in") 81 | 82 | #### `--host` 83 | 84 | The Microsoft Office client application that can host the add-in. The supported arguments include Excel (`excel`), OneNote (`onenote`), Outlook (`outlook`), PowerPoint (`powerpoint`), Project (`project`), and Word (`word`). 85 | 86 | - Type: String 87 | - Optional 88 | 89 | ### Command Line Options 90 | 91 | The following command line options are supported. If these are not specified, the generator will prompt you for the values before scaffolding the project. The options should be specified after the projectType, name and host arguments. 92 | 93 | Specifying `--output` tells the generator to create the project in a specific location. If the output parameter is not specified, the project will be created in the current directory. If the output option specifies a non-empty folder, 94 | the generator will inform you so you don't accidentally overwrite existing files. 95 | 96 | - Type: String 97 | - Optional 98 | 99 | Specifying `--js` tells the generator to use JavaScript. 100 | 101 | - Type: Boolean 102 | - Default: False 103 | - Optional 104 | 105 | Specifying `--ts` tells the generator to use TypeScript. 106 | 107 | - Type: Boolean 108 | - Default: False 109 | - Optional 110 | 111 | Specifying `--details` tells the generator to provide detailed help, including all the accepted values for each project type and host, 112 | 113 | - Type: Boolean 114 | - Default: False 115 | - Optional 116 | 117 | #### `--skip-install` 118 | 119 | After scaffolding the project, the generator (and all sub generators) run all package management install commands such as `npm install` & `typings install`. Specifying `--skip-install` tells the generator to skip this step. 120 | 121 | - Type: Boolean 122 | - Default: False 123 | - Optional 124 | 125 | ## Running the Generated Site 126 | 127 | Launch the local HTTPS site on `https://localhost:3000` by simply typing the following command in your console: 128 | 129 | ```bash 130 | npm start 131 | ``` 132 | 133 | > **Note**: Office Add-ins should use HTTPS, not HTTP, even when you are developing. If you are prompted to install a certificate after you run `npm start`, accept the prompt to install the certificate that the Yeoman generator provides. 134 | 135 | Next, sideload the add-in in an Office application. See [Sideload an Office Add-in for testing](https://learn.microsoft.com/office/dev/add-ins/testing/test-debug-office-add-ins#sideload-an-office-add-in-for-testing). 136 | 137 | ## Validate manifest.xml 138 | 139 | As you modify your `manifest.xml` file, use the included [Office Toolbox](https://github.com/OfficeDev/office-toolbox) to ensure that your XML file is correct and complete. It will also give you information on against what platforms to test your add-ins before submitting to the store. 140 | 141 | To run Office Add-in Validator, use the following command in your project directory: 142 | 143 | ```bash 144 | npm run validate 145 | ``` 146 | 147 | ![](src/docs/assets/validator.gif) 148 | 149 | For more information on manifest validation, refer to our [add-in manifests documentation](https://learn.microsoft.com/office/dev/add-ins/develop/add-in-manifests). 150 | 151 | ## Contributing 152 | 153 | ### [Contributing Guidelines](CONTRIBUTING.md) 154 | 155 | If you are interested in contributing, please start by reading the [Contributing Guidelines](CONTRIBUTING.md). 156 | 157 | ### Development 158 | 159 | #### Prerequisites 160 | 161 | Ensure you have [Node.js](https://nodejs.org/en/) (version 8.0.0 or later) installed. 162 | 163 | Install [Yeoman](http://yeoman.io/). 164 | 165 | ```bash 166 | npm install -g yo 167 | ``` 168 | 169 | #### Initialize the repo 170 | 171 | ```bash 172 | git clone https://github.com/OfficeDev/generator-office.git 173 | cd generator-office 174 | npm install 175 | ``` 176 | 177 | #### Make your desired changes 178 | 179 | - Project templates can be found under [src/app/templates](src/app/templates/) 180 | - Generator script can be found at [src/app/index.ts](src/app/index.ts) 181 | 182 | #### Build and link your changes 183 | 184 | ```bash 185 | npm run build 186 | npm link 187 | cd .. 188 | yo office 189 | ``` 190 | 191 | At this point, `yo office` will be running with your custom built `office-generator` changes. 192 | 193 | ## Data usage 194 | 195 | Yo Office collects anonymized usage data and sends it to Microsoft. This allows us to understand how Yo Office is used and how to improve it. 196 | 197 | For more details on what we collect and how to turn it off, see our [Data usage notice](https://aka.ms/OfficeAddInCLIPrivacy) 198 | 199 | --- 200 | 201 | Copyright (c) 2017 Microsoft Corporation. All rights reserved. 202 | 203 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 204 | -------------------------------------------------------------------------------- /src/app/config/projectProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectTypes": { 3 | "taskpane": { 4 | "displayname": "Office Add-in Task Pane project", 5 | "templates": { 6 | "javascript": { 7 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-JS", 8 | "branch": "yo-office" 9 | }, 10 | "typescript": { 11 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane", 12 | "branch": "yo-office" 13 | } 14 | }, 15 | "supportedHosts": { 16 | "Excel": { 17 | "supportedManifestTypes": [ 18 | "xml" 19 | ] 20 | }, 21 | "Onenote": { 22 | "supportedManifestTypes": [ 23 | "xml" 24 | ] 25 | }, 26 | "Outlook": { 27 | "supportedManifestTypes": [ 28 | "xml", 29 | "json" 30 | ] 31 | }, 32 | "Powerpoint": { 33 | "supportedManifestTypes": [ 34 | "xml" 35 | ] 36 | }, 37 | "Project": { 38 | "supportedManifestTypes": [ 39 | "xml" 40 | ] 41 | }, 42 | "Word": { 43 | "supportedManifestTypes": [ 44 | "xml" 45 | ] 46 | } 47 | } 48 | }, 49 | "taskpane-preview": { 50 | "displayname": "Excel, PowerPoint, and/or Word Task Pane with unified manifest for Microsoft 365 (preview)", 51 | "templates": { 52 | "typescript": { 53 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane", 54 | "prerelease": "json-wxpo-preview", 55 | "branch": "json-wxpo-preview" 56 | } 57 | }, 58 | "supportedHosts": { 59 | "All": { 60 | "supportedManifestTypes": [ 61 | "json" 62 | ] 63 | }, 64 | "Excel": { 65 | "supportedManifestTypes": [ 66 | "json" 67 | ] 68 | }, 69 | "Powerpoint": { 70 | "supportedManifestTypes": [ 71 | "json" 72 | ] 73 | }, 74 | "Word": { 75 | "supportedManifestTypes": [ 76 | "json" 77 | ] 78 | } 79 | } 80 | }, 81 | "react": { 82 | "displayname": "Office Add-in Task Pane project using React framework", 83 | "templates": { 84 | "javascript": { 85 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-React-JS", 86 | "branch": "yo-office" 87 | }, 88 | "typescript": { 89 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-React", 90 | "branch": "yo-office" 91 | } 92 | }, 93 | "supportedHosts": { 94 | "Excel": { 95 | "supportedManifestTypes": [ 96 | "xml" 97 | ] 98 | }, 99 | "Onenote": { 100 | "supportedManifestTypes": [ 101 | "xml" 102 | ] 103 | }, 104 | "Outlook": { 105 | "supportedManifestTypes": [ 106 | "xml", 107 | "json" 108 | ] 109 | }, 110 | "Powerpoint": { 111 | "supportedManifestTypes": [ 112 | "xml" 113 | ] 114 | }, 115 | "Project": { 116 | "supportedManifestTypes": [ 117 | "xml" 118 | ] 119 | }, 120 | "Word": { 121 | "supportedManifestTypes": [ 122 | "xml" 123 | ] 124 | } 125 | } 126 | }, 127 | "excel-functions-shared": { 128 | "displayname": "Excel Custom Functions using a Shared Runtime", 129 | "templates": { 130 | "javascript": { 131 | "repository": "https://github.com/OfficeDev/Excel-Custom-Functions-Shared-JS", 132 | "branch": "yo-office", 133 | "prerelease": "main" 134 | }, 135 | "typescript": { 136 | "repository": "https://github.com/OfficeDev/Excel-Custom-Functions-Shared", 137 | "branch": "yo-office", 138 | "prerelease": "main" 139 | } 140 | }, 141 | "supportedHosts": { 142 | "Excel": { 143 | "supportedManifestTypes": [ 144 | "xml" 145 | ] 146 | } 147 | } 148 | }, 149 | "excel-functions": { 150 | "displayname": "Excel Custom Functions using a JavaScript-only Runtime", 151 | "templates": { 152 | "javascript": { 153 | "repository": "https://github.com/OfficeDev/Excel-Custom-Functions-JS", 154 | "branch": "yo-office" 155 | }, 156 | "typescript": { 157 | "repository": "https://github.com/OfficeDev/Excel-Custom-Functions", 158 | "branch": "yo-office" 159 | } 160 | }, 161 | "supportedHosts": { 162 | "Excel": { 163 | "supportedManifestTypes": [ 164 | "xml" 165 | ] 166 | } 167 | } 168 | }, 169 | "single-sign-on": { 170 | "displayname": "Office Add-in Task Pane project supporting single sign-on", 171 | "templates": { 172 | "javascript": { 173 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-SSO-JS", 174 | "branch": "yo-office" 175 | }, 176 | "typescript": { 177 | "repository": "https://github.com/OfficeDev/Office-Addin-Taskpane-SSO", 178 | "branch": "yo-office" 179 | } 180 | }, 181 | "supportedHosts": { 182 | "Excel": { 183 | "supportedManifestTypes": [ 184 | "xml" 185 | ] 186 | }, 187 | "Outlook": { 188 | "supportedManifestTypes": [ 189 | "xml", 190 | "json" 191 | ] 192 | }, 193 | "Powerpoint": { 194 | "supportedManifestTypes": [ 195 | "xml" 196 | ] 197 | }, 198 | "Word": { 199 | "supportedManifestTypes": [ 200 | "xml" 201 | ] 202 | } 203 | } 204 | }, 205 | "nested-app-auth": { 206 | "displayname": "Office Add-in Task Pane project supporting Nested App Auth single sign-on (preview)", 207 | "templates": { 208 | "typescript": { 209 | "repository": "https://github.com/OfficeDev/Office-Addin-TaskPane-SSO-NAA", 210 | "branch": "yo-office" 211 | } 212 | }, 213 | "supportedHosts": { 214 | "Excel": { 215 | "supportedManifestTypes": [ 216 | "xml" 217 | ] 218 | }, 219 | "Powerpoint": { 220 | "supportedManifestTypes": [ 221 | "xml" 222 | ] 223 | }, 224 | "Word": { 225 | "supportedManifestTypes": [ 226 | "xml" 227 | ] 228 | } 229 | } 230 | }, 231 | "manifest": { 232 | "displayname": "Office Add-in project containing the manifest only", 233 | "templates": { 234 | "manifestonly": { 235 | "repository": "" 236 | } 237 | }, 238 | "supportedHosts": { 239 | "Excel": { 240 | "supportedManifestTypes": [ 241 | "xml" 242 | ] 243 | }, 244 | "Onenote": { 245 | "supportedManifestTypes": [ 246 | "xml" 247 | ] 248 | }, 249 | "Outlook": { 250 | "supportedManifestTypes": [ 251 | "xml" 252 | ] 253 | }, 254 | "Powerpoint": { 255 | "supportedManifestTypes": [ 256 | "xml" 257 | ] 258 | }, 259 | "Project": { 260 | "supportedManifestTypes": [ 261 | "xml" 262 | ] 263 | }, 264 | "Word": { 265 | "supportedManifestTypes": [ 266 | "xml" 267 | ] 268 | } 269 | } 270 | } 271 | }, 272 | "hostTypes": { 273 | "wxpo": { 274 | "displayname": "All" 275 | }, 276 | "excel": { 277 | "displayname": "Excel" 278 | }, 279 | "onenote": { 280 | "displayname": "OneNote" 281 | }, 282 | "outlook": { 283 | "displayname": "Outlook" 284 | }, 285 | "powerpoint": { 286 | "displayname": "PowerPoint" 287 | }, 288 | "project": { 289 | "displayname": "Project" 290 | }, 291 | "word": { 292 | "displayname": "Word" 293 | } 294 | }, 295 | "manifestTypes": { 296 | "xml": { 297 | "displayname": "Add-in only manifest" 298 | }, 299 | "json": { 300 | "displayname": "Unified manifest for Microsoft 365" 301 | } 302 | } 303 | } -------------------------------------------------------------------------------- /src/app/config/projectsJsonData.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import _ from 'lodash'; 3 | 4 | export default class projectsJsonData { 5 | m_projectJsonDataFile = '/projectProperties.json'; 6 | m_projectJsonData; 7 | 8 | constructor(templatePath: string) { 9 | const jsonData = fs.readFileSync(templatePath + this.m_projectJsonDataFile); 10 | this.m_projectJsonData = JSON.parse(jsonData.toString()); 11 | } 12 | 13 | isValidProjectType(input: string): boolean { 14 | for (const key in this.m_projectJsonData.projectTypes) { 15 | if (_.toLower(input) == key) { 16 | return true; 17 | } 18 | } 19 | return false; 20 | } 21 | 22 | isValidHost(input: string): boolean { 23 | for (const key in this.m_projectJsonData.hostTypes) { 24 | if (_.toLower(input) == key) { 25 | return true; 26 | } 27 | } 28 | return false; 29 | } 30 | 31 | isValidManifestType(input: string): boolean { 32 | for (const key in this.m_projectJsonData.manifestTypes) { 33 | if (_.toLower(input) == key) { 34 | return true; 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | getProjectDisplayName(projectType: string): string { 41 | return this.m_projectJsonData.projectTypes[_.toLower(projectType)].displayname; 42 | } 43 | 44 | getParsedProjectJsonData() { 45 | return this.m_projectJsonData; 46 | } 47 | 48 | getProjectTemplateNames(): string[] { 49 | const projectTemplates: string[] = []; 50 | for (const key in this.m_projectJsonData.projectTypes) { 51 | projectTemplates.push(key); 52 | } 53 | return projectTemplates; 54 | } 55 | 56 | projectBothScriptTypes(projectType: string): boolean { 57 | return this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.javascript != undefined && this.m_projectJsonData.projectTypes[_.toLower(projectType)].templates.typescript != undefined; 58 | } 59 | 60 | getManifestOptions(projectType: string, host: string): string[] { 61 | const selectedHost = this.m_projectJsonData.projectTypes[projectType]?.supportedHosts[host]; 62 | return selectedHost ? selectedHost.supportedManifestTypes : []; 63 | } 64 | 65 | getHostOptions(projectType: string): string[] { 66 | const selectedProjectType = this.m_projectJsonData.projectTypes[projectType]; 67 | return selectedProjectType ? Object.keys(selectedProjectType.supportedHosts) : []; 68 | } 69 | 70 | getScriptTypeOptions(projectType: string): string[] { 71 | const scriptTypes: string[] = []; 72 | for (const template in this.m_projectJsonData.projectTypes[projectType].templates) { 73 | let scriptType: string = "unknown"; 74 | if (template === "javascript") { 75 | scriptType = "JavaScript"; 76 | } else if (template === "typescript") { 77 | scriptType = "TypeScript"; 78 | } 79 | 80 | scriptTypes.push(scriptType); 81 | } 82 | return scriptTypes; 83 | } 84 | 85 | getHostDisplayName(hostKey: string): string { 86 | for (const key in this.m_projectJsonData.hostTypes) { 87 | if (_.toLower(hostKey) == key) { 88 | return this.m_projectJsonData.hostTypes[key].displayname; 89 | } 90 | } 91 | return ""; 92 | } 93 | 94 | getManifestDisplayName(hostKey: string): string { 95 | return this.m_projectJsonData.manifestTypes[hostKey]?.displayname; 96 | } 97 | 98 | getProjectTemplateRepository(projectTypeKey: string, scriptType: string): string | undefined { 99 | for (const key in this.m_projectJsonData.projectTypes) { 100 | if (_.toLower(projectTypeKey) == key) { 101 | if (projectTypeKey == 'manifest') { 102 | return this.m_projectJsonData.projectTypes[key].templates.manifestonly.repository; 103 | } 104 | else { 105 | return this.m_projectJsonData.projectTypes[key].templates[scriptType].repository; 106 | } 107 | } 108 | } 109 | return undefined; 110 | } 111 | 112 | getProjectTemplateBranchName(projectTypeKey: string, scriptType: string, prerelease: boolean): string | undefined { 113 | for (const key in this.m_projectJsonData.projectTypes) { 114 | if (_.toLower(projectTypeKey) == key) { 115 | if (projectTypeKey == 'manifest') { 116 | return this.m_projectJsonData.projectTypes.manifest.templates.branch; 117 | } 118 | else { 119 | if (prerelease) { 120 | if (this.m_projectJsonData.projectTypes[key].templates[scriptType].prerelease) { 121 | return this.m_projectJsonData.projectTypes[key].templates[scriptType].prerelease 122 | } 123 | else { 124 | return "master"; 125 | } 126 | } else { 127 | return this.m_projectJsonData.projectTypes[key].templates[scriptType].branch; 128 | } 129 | } 130 | } 131 | } 132 | return undefined; 133 | } 134 | 135 | getProjectRepoAndBranch(projectTypeKey: string, scriptType: string, prerelease: boolean) { 136 | scriptType = scriptType === 'ts' ? 'typescript' : 'javascript'; 137 | const repoBranchInfo = { repo: undefined, branch: undefined }; 138 | 139 | repoBranchInfo.repo = this.getProjectTemplateRepository(projectTypeKey, scriptType); 140 | repoBranchInfo.branch = (repoBranchInfo.repo) ? this.getProjectTemplateBranchName(projectTypeKey, scriptType, prerelease) : undefined; 141 | 142 | return repoBranchInfo; 143 | } 144 | } -------------------------------------------------------------------------------- /src/app/defaults.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export const configurationErrorEventName = "configuration-error-generator-office"; 4 | export const copyFilesErrorEventName = "copy-files-error-generator-office"; 5 | export const installDependenciesErrorEventName = "install-dependencies-error-generator-office"; 6 | export const networkShareSideloadingSteps = "https://learn.microsoft.com/office/dev/add-ins/testing/create-a-network-shared-folder-catalog-for-task-pane-and-content-add-ins"; 7 | export const outlookSideloadingSteps = "https://learn.microsoft.com/office/dev/add-ins/outlook/sideload-outlook-add-ins-for-testing"; 8 | export const postInstallHintsErrorEventName = "post-install-hints-error-generator-office"; 9 | export const promptSelectionstEventName = "prompt-selections-generator-office"; 10 | export const promptSelectionsErrorEventName = "prompt-selections-error-generator-office"; 11 | export const usageDataProjectName = "generator-office"; 12 | export const usageDataPromptMessage = `Office Add-in CLI tools collect anonymized usage data which is sent to Microsoft to help improve our product. Please read our privacy notice at ${chalk.blue('https://aka.ms/OfficeAddInCLIPrivacy')}. ​To disable data collection, choose Exit and run ${chalk.green('“npx office-addin-usage-data off”')}.\n\n`; 13 | -------------------------------------------------------------------------------- /src/app/helpers/helperMethods.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | import * as fs from "fs"; 3 | import * as path from "path"; 4 | import AdmZip from "adm-zip"; 5 | 6 | const zipFile = 'project.zip'; 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-namespace 9 | export namespace helperMethods { 10 | export function deleteFolderRecursively(projectFolder: string) { 11 | try { 12 | if (fs.existsSync(projectFolder)) { 13 | fs.readdirSync(projectFolder).forEach(function (file) { 14 | const curPath = `${projectFolder}/${file}`; 15 | 16 | if (fs.lstatSync(curPath).isDirectory()) { 17 | deleteFolderRecursively(curPath); 18 | } 19 | else { 20 | fs.unlinkSync(curPath); 21 | } 22 | }); 23 | fs.rmdirSync(projectFolder); 24 | } 25 | } catch (err) { 26 | throw new Error(`Unable to delete folder "${projectFolder}".\n${err}`); 27 | } 28 | } 29 | 30 | export function doesProjectFolderExist(projectFolder: string) { 31 | if (fs.existsSync(projectFolder)) { 32 | return fs.readdirSync(projectFolder).length > 0; 33 | } 34 | return false; 35 | }; 36 | 37 | export async function downloadProjectTemplateZipFile(projectFolder: string, projectRepo: string, projectBranch: string): Promise { 38 | const projectTemplateZipFile = `${projectRepo}/archive/${projectBranch}.zip`; 39 | return axios({ 40 | method: 'get', 41 | url: projectTemplateZipFile, 42 | responseType: 'stream', 43 | }).then(response => { 44 | return new Promise((resolve, reject) => { 45 | response.data.pipe(fs.createWriteStream(zipFile)) 46 | .on('error', function (err) { 47 | reject(`Unable to download project zip file for "${projectTemplateZipFile}".\n${err}`); 48 | }) 49 | .on('close', async () => { 50 | resolve(path.resolve(`${projectFolder}/project.zip`)); 51 | }); 52 | }); 53 | }).catch(err => { 54 | const error: string = `Unable to download project zip file for "${projectTemplateZipFile}".\n${err}`; 55 | console.log(error) 56 | return Promise.reject(error); 57 | }); 58 | } 59 | 60 | export async function unzipProjectTemplate(projectFolder: string): Promise { 61 | return new Promise(async (resolve, reject) => { 62 | const zipFile = 'project.zip'; 63 | const zip = new AdmZip(`${projectFolder}/${zipFile}`); 64 | try { 65 | zip.extractAllTo(/*target path*/projectFolder, /*overwrite*/true); 66 | // get path to unzipped folder 67 | const unzippedFolder = fs.readdirSync(projectFolder).filter(function (file) { 68 | return fs.statSync(`${projectFolder}/${file}`).isDirectory(); 69 | }); 70 | resolve(unzippedFolder[0]); 71 | } catch (err) { 72 | reject(`Unable to unzip project zip file for "${projectFolder}".\n${err}`); 73 | } 74 | }); 75 | } 76 | } -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | import _ from 'lodash'; 6 | import chalk from 'chalk'; 7 | import * as defaults from "./defaults.js"; 8 | import { helperMethods } from './helpers/helperMethods.js'; 9 | import projectsJsonData from './config/projectsJsonData.js'; 10 | import * as usageData from "office-addin-usage-data"; 11 | import { v4 as uuidv4 } from 'uuid'; 12 | import yosay from 'yosay'; 13 | import Generator, { PromptQuestion } from 'yeoman-generator'; 14 | import * as fs from "fs"; 15 | import * as path from "path"; 16 | 17 | const excelCustomFunctions = `excel-functions`; 18 | let isSsoProject = false; 19 | const javascript = `JavaScript`; 20 | let language: string; 21 | const manifest = 'manifest'; 22 | const sso = 'single-sign-on'; 23 | const typescript = `TypeScript`; 24 | let jsonData: projectsJsonData; 25 | 26 | let usageDataObject: usageData.OfficeAddinUsageData; 27 | const usageDataOptions: usageData.IUsageDataOptions = { 28 | groupName: usageData.groupName, 29 | projectName: defaults.usageDataProjectName, 30 | raisePrompt: false, 31 | instrumentationKey: usageData.instrumentationKeyForOfficeAddinCLITools, 32 | promptQuestion: defaults.usageDataPromptMessage, 33 | usageDataLevel: usageData.UsageDataLevel.off, 34 | method: usageData.UsageDataReportingMethod.applicationInsights, 35 | isForTesting: false 36 | } 37 | 38 | export default class extends Generator { 39 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 40 | project: any; 41 | 42 | /* Setup the generator */ 43 | constructor(args, opts) { 44 | super(args, opts); 45 | 46 | if (parseInt(process.version.slice(1, process.version.indexOf('.'))) % 2 == 1) { 47 | console.log(yosay('generator-office does not support your version of Node. Please switch to the latest LTS version of Node.')); 48 | this._exitProcess(); 49 | } 50 | 51 | this.argument('projectType', { type: String, required: false }); 52 | this.argument('name', { type: String, required: false }); 53 | this.argument('host', { type: String, required: false }); 54 | this.argument('manifestType', { type: String, required: false }); 55 | 56 | this.option('skip-install', { 57 | type: Boolean, 58 | description: 'Skip running `npm install` post scaffolding.' 59 | }); 60 | 61 | this.option('js', { 62 | type: Boolean, 63 | description: 'Project uses JavaScript instead of TypeScript.' 64 | }); 65 | 66 | this.option('ts', { 67 | type: Boolean, 68 | description: 'Project uses TypeScript instead of JavaScript.' 69 | }); 70 | 71 | this.option('output', { 72 | alias: 'o', 73 | type: String, 74 | description: 'Project folder name if different from project name.' 75 | }); 76 | 77 | this.option('prerelease', { 78 | type: String, 79 | description: 'Use the prerelease version of the project template.' 80 | }); 81 | 82 | this.option('test', { 83 | type: String, 84 | description: 'Project is created in the context of unit tests.' 85 | }); 86 | 87 | this.option('details', { 88 | alias: 'd', 89 | type: Boolean, 90 | description: 'Get more details on Yo Office arguments.' 91 | }); 92 | } 93 | 94 | /* Generator initalization */ 95 | async initializing(): Promise { 96 | if (this.options["details"]) { 97 | await this._detailedHelp(); 98 | } 99 | if (this.options['test']) { 100 | usageDataOptions.isForTesting = true; 101 | } 102 | if (this.options['skip-install']) { 103 | this.options.skipInstall = true; 104 | } 105 | const message = `Welcome to the ${chalk.bold.green('Office Add-in')} generator, by ${chalk.bold.green('@OfficeDev')}! Let\'s create a project together!`; 106 | await this.log(yosay(message)); 107 | jsonData = new projectsJsonData(this.templatePath()); 108 | } 109 | 110 | /* Prompt user for project options */ 111 | async prompting(): Promise { 112 | try { 113 | if (usageData.needToPromptForUsageData(usageDataOptions.groupName||"")) { 114 | const promptForUsageData: PromptQuestion[] = [ 115 | { 116 | name: 'usageDataPromptAnswer', 117 | message: usageDataOptions.promptQuestion || defaults.usageDataPromptMessage, 118 | type: 'list', 119 | default: 'Continue', 120 | choices: ['Continue', 'Exit'], 121 | } 122 | ]; 123 | const answerForUsageDataPrompt = await this.prompt(promptForUsageData); 124 | if (answerForUsageDataPrompt?.usageDataPromptAnswer === 'Continue') { 125 | usageDataOptions.usageDataLevel = usageData.UsageDataLevel.on; 126 | } else { 127 | process.exit(); 128 | } 129 | } else { 130 | usageDataOptions.usageDataLevel = usageData.readUsageDataLevel(usageDataOptions.groupName||""); 131 | } 132 | 133 | let isManifestProject = false; 134 | let isExcelFunctionsProject = false; 135 | 136 | // Normalize host name if passed as a command line argument 137 | if (this.options["host"] != null) { 138 | this.options["host"] = jsonData.getHostDisplayName(this.options["host"]); 139 | } 140 | 141 | /* askForProjectType will only be triggered if no project type was specified via command line projectType argument, 142 | * and the projectType argument input was indeed valid */ 143 | const startForProjectType = (new Date()).getTime(); 144 | const askForProjectType: PromptQuestion[] = [ 145 | { 146 | name: 'projectType', 147 | message: 'Choose a project type:', 148 | type: 'list', 149 | default: 'React', 150 | choices: jsonData.getProjectTemplateNames().map(template => ({ name: jsonData.getProjectDisplayName(template), value: template })), 151 | when: this.options["projectType"] == null || !jsonData.isValidProjectType(this.options["projectType"]) 152 | } 153 | ]; 154 | const answerForProjectType = await this.prompt(askForProjectType); 155 | const endForProjectType = (new Date()).getTime(); 156 | const durationForProjectType = (endForProjectType - startForProjectType) / 1000; 157 | 158 | const projectType = _.toLower(this.options["projectType"]) || _.toLower(answerForProjectType.projectType); 159 | 160 | /* Set isManifestProject to true if Manifest project type selected from prompt or Manifest was specified via the command prompt */ 161 | if ((answerForProjectType.projectType != null && _.toLower(answerForProjectType.projectType) === manifest) 162 | || (this.options["projectType"] != null && _.toLower(this.options["projectType"])) === manifest) { 163 | isManifestProject = true; 164 | } 165 | 166 | /* Set isExcelFunctionsProject to true if ExcelFunctions project type selected from prompt or Excel Functions was specified via the command prompt */ 167 | if ((answerForProjectType.projectType != null && answerForProjectType.projectType) === excelCustomFunctions 168 | || (this.options["projectType"] != null && _.toLower(this.options["projectType"]) === excelCustomFunctions)) { 169 | isExcelFunctionsProject = true; 170 | } 171 | 172 | /* Set isSsoProject to true if SSO project type selected from prompt or Single Sign-On was specified via the command prompt */ 173 | if ((answerForProjectType.projectType != null && answerForProjectType.projectType) === sso 174 | || (this.options["projectType"] != null && _.toLower(this.options["projectType"]) === sso)) { 175 | isSsoProject = true; 176 | } 177 | 178 | const getSupportedScriptTypes = jsonData.getScriptTypeOptions(projectType); 179 | const askForScriptType: PromptQuestion[] = [ 180 | { 181 | name: 'scriptType', 182 | type: 'list', 183 | message: 'Choose a script type:', 184 | choices: getSupportedScriptTypes, 185 | default: getSupportedScriptTypes[0], 186 | when: !this.options["js"] && !this.options["ts"] && !isManifestProject && getSupportedScriptTypes.length > 1 187 | } 188 | ]; 189 | const answerForScriptType = await this.prompt(askForScriptType); 190 | 191 | /* askforName will be triggered if no project name was specified via command line Name argument */ 192 | const askForName: PromptQuestion[] = [{ 193 | name: 'name', 194 | type: 'input', 195 | message: 'What do you want to name your add-in?', 196 | default: 'My Office Add-in', 197 | when: this.options["name"] == null 198 | }]; 199 | const answerForName = await this.prompt(askForName); 200 | 201 | /* askForHost will be triggered if no project name was specified via the command line Host argument, and the Host argument 202 | * input was in fact valid, and the project type is not Excel-Functions */ 203 | const startForHost = (new Date()).getTime(); 204 | const supportedHosts = jsonData.getHostOptions(projectType); 205 | const askForHost: PromptQuestion[] = [{ 206 | name: 'host', 207 | message: 'Which Office client application would you like to support?', 208 | type: 'list', 209 | default: supportedHosts[0], 210 | choices: supportedHosts.map(host => ({ name: host, value: host })), 211 | when: (this.options["host"] == null || this.options["host"] != null && !jsonData.isValidHost(this.options["host"])) 212 | && supportedHosts.length > 1 213 | }]; 214 | const answerForHost = await this.prompt(askForHost); 215 | const endForHost = (new Date()).getTime(); 216 | const durationForHost = (endForHost - startForHost) / 1000; 217 | 218 | const selectedHost = this.options["host"] || answerForHost.host || supportedHosts[0]; 219 | 220 | usageDataObject = new usageData.OfficeAddinUsageData(usageDataOptions); 221 | 222 | /* aksForManifestType will be triggered if no type was specified via the command line manifestType argument */ 223 | const startForManifestType = (new Date()).getTime(); 224 | const manifestOptions = jsonData.getManifestOptions(projectType, selectedHost); 225 | const askForManifestType: PromptQuestion[] = [{ 226 | name: 'manifestType', 227 | message: 'Which manifest type would you like to use?', 228 | type: 'list', 229 | default: manifestOptions[0], 230 | choices: manifestOptions.map(manifestType => ({ name: jsonData.getManifestDisplayName(manifestType), value: manifestType })), 231 | when: (this.options["manifestType"] == null || this.options["manifestType"] != null && !jsonData.isValidManifestType(this.options["manifestType"])) 232 | && jsonData.getManifestOptions(projectType, selectedHost).length > 1 233 | }]; 234 | const answerForManifestType = await this.prompt(askForManifestType); 235 | const endForManifestType = (new Date()).getTime(); 236 | const durationForManifestType = (endForManifestType - startForManifestType) / 1000; 237 | 238 | usageDataObject = new usageData.OfficeAddinUsageData(usageDataOptions); 239 | 240 | /* Configure project properties based on user input or answers to prompts */ 241 | this._configureProject(answerForProjectType, answerForManifestType, answerForScriptType, answerForHost, answerForName, isManifestProject, isExcelFunctionsProject); 242 | const projectInfo = { 243 | Host: [this.project.host, durationForHost], 244 | ScriptType: [this.project.scriptType], 245 | IsManifestOnly: [this.project.isManifestOnly.toString()], 246 | ProjectType: [this.project.projectType, durationForProjectType], 247 | ManifestType: [this.project.manifestType, durationForManifestType], 248 | isForTesting: [usageDataOptions.isForTesting] 249 | }; 250 | // Send usage data for project created 251 | usageDataObject.reportEvent(defaults.promptSelectionstEventName, projectInfo); 252 | } catch (err) { 253 | usageDataObject = new usageData.OfficeAddinUsageData(usageDataOptions); 254 | usageDataObject.reportError(defaults.promptSelectionsErrorEventName, new Error('Prompting Error: ' + err)); 255 | } 256 | } 257 | 258 | async writing(): Promise { 259 | await this._copyProjectFiles() 260 | .catch((err) => { 261 | usageDataObject.reportError(defaults.copyFilesErrorEventName, new Error('Installation Error: ' + err)); 262 | process.exitCode = 1; 263 | }); 264 | } 265 | 266 | async install(): Promise { 267 | // Call 'convert-to-single-host' npm script in generated project, passing in host parameter 268 | // Need to call this here after package.json was written to disk, but before npm install is called 269 | await this.spawn("npm", ["run", "convert-to-single-host", "--if-present", "--", _.toLower(this.project.hostInternalName), this.project.manifestType, this.project.name]); 270 | } 271 | 272 | async end(): Promise { 273 | if (!this.options['test']) { 274 | try { 275 | await this._postInstallHints(); 276 | } catch (err) { 277 | usageDataObject.reportError(defaults.postInstallHintsErrorEventName, new Error('Exit Error: ' + err)); 278 | } 279 | } 280 | } 281 | 282 | _configureProject(answerForProjectType, answerForManifestType, answerForScriptType, answerForHost, answerForName, isManifestProject, isExcelFunctionsProject): void { 283 | try { 284 | const projType = _.toLower(this.options["projectType"]) || _.toLower(answerForProjectType.projectType); 285 | const supportedHosts = jsonData.getHostOptions(projType); 286 | const selectedHost = this.options["host"] || answerForHost.host || supportedHosts[0]; 287 | 288 | this.project = { 289 | folder: this.options["output"] || answerForName.name || this.options["name"], 290 | host: answerForHost.host 291 | ? answerForHost.host 292 | : this.options["host"] 293 | ? this.options["host"] 294 | : jsonData?.getHostOptions(projType)[0], 295 | manifestType: answerForManifestType.manifestType 296 | ? answerForManifestType.manifestType 297 | : this.options["manifestType"] 298 | ? this.options["manifestType"] 299 | : jsonData?.getManifestOptions(projType, selectedHost)[0], 300 | name: this.options["name"] || answerForName.name, 301 | projectType: projType, 302 | scriptType: answerForScriptType.scriptType 303 | ? answerForScriptType.scriptType 304 | : this.options["ts"] 305 | ? typescript 306 | : this.options["js"] 307 | ? javascript 308 | : jsonData?.getScriptTypeOptions(projType)[0], 309 | isManifestOnly: isManifestProject, 310 | isExcelFunctionsProject: isExcelFunctionsProject, 311 | }; 312 | 313 | /* Set folder if to output param if specified */ 314 | if (this.options["output"] != null) { 315 | this.project.folder = this.options["output"]; 316 | } 317 | 318 | /* Set language variable */ 319 | language = this.project.scriptType === typescript ? 'ts' : 'js'; 320 | 321 | this.project.projectInternalName = _.kebabCase(this.project.name); 322 | this.project.projectDisplayName = this.project.name; 323 | this.project.projectId = uuidv4(); 324 | this.project.hostInternalName = this.project.host == "All" ? "wxpo" : this.project.host; 325 | 326 | this.destinationRoot(this.project.folder); 327 | process.chdir(this.destinationRoot()); 328 | this.env.cwd = this.destinationRoot(); 329 | 330 | /* Check to to see if destination folder already exists. If so, we will exit and prompt the user to provide 331 | a different project name or output folder */ 332 | this._exitYoOfficeIfProjectFolderExists(); 333 | } 334 | catch (err) { 335 | usageDataObject.reportError(defaults.configurationErrorEventName, new Error('Configuration Error: ' + err)); 336 | 337 | } 338 | } 339 | 340 | async _copyProjectFiles(): Promise { 341 | return new Promise(async (resolve, reject) => { 342 | try { 343 | const projectRepoBranchInfo = jsonData.getProjectRepoAndBranch(this.project.projectType, language, this.options["prerelease"]); 344 | 345 | this._projectCreationMessage(); 346 | 347 | // Copy project template files from project repository (currently only custom functions has its own separate repo) 348 | if (projectRepoBranchInfo.repo && projectRepoBranchInfo.branch) { 349 | const projectFolder: string = this.destinationPath(); 350 | const zipFile: string = await helperMethods.downloadProjectTemplateZipFile(projectFolder, projectRepoBranchInfo.repo, projectRepoBranchInfo.branch); 351 | const unzippedFolder: string = await helperMethods.unzipProjectTemplate(projectFolder); 352 | const moveFromFolder: string = this.destinationPath(unzippedFolder); 353 | 354 | // delete original zip file 355 | if (fs.existsSync(zipFile)) { 356 | fs.unlinkSync(zipFile); 357 | } 358 | 359 | // loop through all the files and folders in the unzipped folder and move them to project root 360 | fs.readdirSync(moveFromFolder) 361 | .filter((file) => !file.includes(".gitignore") && !file.includes("package.json")) 362 | .forEach(function (file) { 363 | const fromPath = path.join(moveFromFolder, file); 364 | const toPath = path.join(projectFolder, file); 365 | fs.renameSync(fromPath, toPath); 366 | }); 367 | 368 | // copy package.json file to new project directory and trigger npm install 369 | this.fs.copyTpl(path.join(moveFromFolder, "package.json"), path.join(projectFolder, "package.json")); 370 | 371 | // delete project zipped folder 372 | helperMethods.deleteFolderRecursively(this.destinationPath(unzippedFolder)); 373 | 374 | } 375 | else { 376 | // Manifest-only project 377 | const templateFills = Object.assign({}, this.project); 378 | this.fs.copyTpl(this.templatePath(`hosts/${_.toLower(this.project.hostInternalName)}/manifest.xml`), this.destinationPath('manifest.xml'), templateFills); 379 | this.fs.copyTpl(this.templatePath(`manifest-only/**`), this.destinationPath(), templateFills); 380 | } 381 | 382 | return resolve() 383 | } 384 | catch (err) { 385 | usageDataObject.reportError(defaults.copyFilesErrorEventName, new Error("File Copy Error: " + err)); 386 | return reject(err); 387 | } 388 | }); 389 | } 390 | 391 | async _postInstallHints(): Promise { 392 | const projFolder: string = /\s/.test(this.destinationRoot()) ? "\"" + this.destinationRoot() + "\"" : this.destinationRoot(); 393 | let stepNumber = 1; 394 | 395 | /* Next steps and npm commands */ 396 | await this.log('----------------------------------------------------------------------------------------------------------\n'); 397 | await this.log(` ${chalk.green('Congratulations!')} Your add-in has been created! Your next steps:\n`); 398 | await this.log(` ${stepNumber++}. Go the directory where your project was created:\n`); 399 | await this.log(` ${chalk.bold('cd ' + projFolder)}\n`); 400 | 401 | if (isSsoProject) { 402 | await this.log(` ${stepNumber++}. Configure your SSO taskpane add-in:\n`); 403 | await this.log(` ${chalk.bold('npm run configure-sso')}\n`); 404 | } else if (this.project.isExcelFunctionsProject) { 405 | await this.log(` ${stepNumber++}. Build your Excel Custom Functions taskpane add-in:\n`); 406 | await this.log(` ${chalk.bold('npm run build')}\n`); 407 | } 408 | 409 | if (!this.project.isManifestOnly) { 410 | if (this.project.host === "Excel" || this.project.host === "Word" || this.project.host === "Powerpoint" || this.project.host === "Outlook" || this.project.host === "All") { 411 | await this.log(` ${stepNumber++}. Start the local web server and sideload the add-in:\n`); 412 | await this.log(` ${chalk.bold('npm start')}\n`); 413 | } else { 414 | await this.log(` ${stepNumber++}. Start the local web server:\n`); 415 | await this.log(` ${chalk.bold('npm run dev-server')}\n`); 416 | await this.log(` ${stepNumber++}. Sideload the the add-in:\n`); 417 | await this.log(` ${chalk.bold('Follow these instructions:')}`); 418 | await this.log(` ${defaults.networkShareSideloadingSteps}\n`); 419 | } 420 | } 421 | 422 | await this.log(` ${stepNumber++}. Open the project in VS Code:\n`); 423 | await this.log(` ${chalk.bold('code .')}\n`); 424 | await this.log(` For more information, visit http://code.visualstudio.com.\n`); 425 | await this.log(` Please visit https://learn.microsoft.com/office/dev/add-ins for more information about Office Add-ins.\n`); 426 | if(this.project.host === "Outlook") { 427 | await this.log(` Please visit ${defaults.outlookSideloadingSteps} for more information about Outlook sideloading.\n`); 428 | } 429 | await this.log('----------------------------------------------------------------------------------------------------------\n'); 430 | this._exitProcess(); 431 | } 432 | 433 | async _projectCreationMessage(): Promise { 434 | /* Log to console the type of project being created */ 435 | if (this.project.isManifestOnly) { 436 | await this.log('----------------------------------------------------------------------------------\n'); 437 | await this.log(` Creating manifest for ${chalk.bold.green(this.project.projectDisplayName)} at ${chalk.bold.magenta(this.destinationRoot())}\n`); 438 | await this.log('----------------------------------------------------------------------------------'); 439 | } 440 | else { 441 | await this.log('\n----------------------------------------------------------------------------------\n'); 442 | await this.log(` Creating ${chalk.bold.green(this.project.projectDisplayName)} add-in for ${chalk.bold.magenta(_.capitalize(this.project.host))}`); 443 | await this.log(` using ${chalk.bold.yellow(this.project.scriptType)} and ${chalk.bold.magenta(jsonData.getProjectDisplayName(this.project.projectType))} and ${chalk.bold.yellow(jsonData.getManifestDisplayName(this.project.manifestType))}`); 444 | await this.log(` at ${chalk.bold.magenta(this.destinationRoot())}\n`); 445 | await this.log('----------------------------------------------------------------------------------'); 446 | } 447 | } 448 | 449 | async _detailedHelp(): Promise { 450 | await this.log(`\nYo Office ${chalk.bgGreen('Arguments')} and ${chalk.bgMagenta('Options.')}\n`); 451 | await this.log(`NOTE: ${chalk.bgGreen('Arguments')} must be specified in the order below, and ${chalk.bgMagenta('Options')} must follow ${chalk.bgGreen('Arguments')}.\n`); 452 | await this.log(` ${chalk.bgGreen('projectType')}:Specifies the type of project to create. Valid project types include:`); 453 | await this.log(` ${chalk.yellow('taskpane:')} Creates an 'Office Add-in Task Pane project' project.`); 454 | await this.log(` ${chalk.yellow('react:')} Creates an 'Office add-in using React framework' project.`); 455 | await this.log(` ${chalk.yellow('excel-functions-shared:')} Creates an 'Office add-in for Excel custom functions using a Shared Runtime' project.`); 456 | await this.log(` ${chalk.yellow('excel-functions:')} Creates an 'Office add-in for Excel custom functions using a JavaScript-only Runtime' project.`); 457 | await this.log(` ${chalk.yellow('single-sign-on:')} Creates an 'Office Add-in Task Pane project supporting single sign-on' project.`); 458 | await this.log(` ${chalk.yellow('nested-app-auth:')} Creates an 'Office Add-in Task Pane project supporting Nested App Auth single sign-on (preview)' project.`); 459 | await this.log(` ${chalk.yellow('manifest:')} Creates an only the manifest file for an Office add-in project.\n`); 460 | await this.log(` ${chalk.bgGreen('name')}:Specifies the name for the project that will be created.\n`); 461 | await this.log(` ${chalk.bgGreen('host')}:Specifies the host app in the add-in manifest. Valid hosts include:`); 462 | await this.log(` ${chalk.yellow('excel:')} Creates an Office add-in for Excel.`); 463 | await this.log(` ${chalk.yellow('onenote:')} Creates an Office add-in for OneNote.`); 464 | await this.log(` ${chalk.yellow('outlook:')} Creates an Office add-in for Outlook.`); 465 | await this.log(` ${chalk.yellow('powerpoint:')} Creates an Office add-in for PowerPoint.`); 466 | await this.log(` ${chalk.yellow('project:')} Creates an Office add-in for Project.`); 467 | await this.log(` ${chalk.yellow('word:')} Creates an Office add-in for Word.\n`); 468 | await this.log(` ${chalk.bgGreen('manifestType')}:Specifies the manifest type to use for the add-in. Valid types include:`); 469 | await this.log(` ${chalk.yellow('xml:')} Creates a Add-in only manifest`); 470 | await this.log(` ${chalk.yellow('json:')} Creates a unified manifest for Microsoft 365.\n`); 471 | await this.log(` ${chalk.bgMagenta('--output')}:Specifies the location in the file system where the project will be created.`); 472 | await this.log(` ${chalk.yellow('If the option is not specified, the project will be created in the current folder')}\n`); 473 | await this.log(` ${chalk.bgMagenta('--js')}:Specifies that the project will use JavaScript instead of TypeScript.`); 474 | await this.log(` ${chalk.yellow('If the option is not specified, Yo Office will prompt for TypeScript or JavaScript')}\n`); 475 | await this.log(` ${chalk.bgMagenta('--ts')}:Specifies that the project will use TypeScript instead of JavaScript.`); 476 | await this.log(` ${chalk.yellow('If the option is not specified, Yo Office will prompt for TypeScript or JavaScript')}\n`); 477 | this._exitProcess(); 478 | } 479 | 480 | async _exitYoOfficeIfProjectFolderExists(): Promise { 481 | if (helperMethods.doesProjectFolderExist(this.destinationRoot())) { 482 | await this.log(`${chalk.bold.red(`\nFolder already exists at ${chalk.bold.green(this.destinationRoot())} and is not empty. To avoid accidentally overwriting any files, please start over and choose a different project name or destination folder via the ${chalk.bold.magenta(`--output`)} parameter`)}\n`); 483 | this._exitProcess(); 484 | } 485 | return false; 486 | } 487 | 488 | _exitProcess(): void { 489 | process.exit(); 490 | } 491 | }; 492 | -------------------------------------------------------------------------------- /src/app/templates/hosts/excel/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | <%= projectId %> 13 | 14 | 15 | 1.0.0.0 16 | [Provider name] 17 | en-US 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | AppDomain1 32 | AppDomain2 33 | AppDomain3 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ReadWriteDocument 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | <!-- Description of the Getting Started callout. resid points to a LongString resource --> 65 | <Description resid="GetStarted.Description"/> 66 | 67 | <!-- Point to a url resource which details how the add-in should be used. --> 68 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 69 | </GetStarted> 70 | <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. 71 | Think of the FunctionFile as the code behind ExecuteFunction. --> 72 | <FunctionFile resid="Commands.Url" /> 73 | 74 | <!-- PrimaryCommandSurface is the main Office Ribbon. --> 75 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 76 | <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> 77 | <OfficeTab id="TabHome"> 78 | <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> 79 | <Group id="CommandsGroup"> 80 | <!-- Label for your group. resid must point to a ShortString resource. --> 81 | <Label resid="CommandsGroup.Label" /> 82 | <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> 83 | <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> 84 | <Icon> 85 | <bt:Image size="16" resid="Icon.16x16" /> 86 | <bt:Image size="32" resid="Icon.32x32" /> 87 | <bt:Image size="80" resid="Icon.80x80" /> 88 | </Icon> 89 | 90 | <!-- Control. It can be of type "Button" or "Menu". --> 91 | <Control xsi:type="Button" id="TaskpaneButton"> 92 | <Label resid="TaskpaneButton.Label" /> 93 | <Supertip> 94 | <!-- ToolTip title. resid must point to a ShortString resource. --> 95 | <Title resid="TaskpaneButton.Label" /> 96 | <!-- ToolTip description. resid must point to a LongString resource. --> 97 | <Description resid="TaskpaneButton.Tooltip" /> 98 | </Supertip> 99 | <Icon> 100 | <bt:Image size="16" resid="Icon.16x16" /> 101 | <bt:Image size="32" resid="Icon.32x32" /> 102 | <bt:Image size="80" resid="Icon.80x80" /> 103 | </Icon> 104 | 105 | <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> 106 | <Action xsi:type="ShowTaskpane"> 107 | <TaskpaneId>ButtonId1</TaskpaneId> 108 | <!-- Provide a url resource id for the location that will be displayed on the task pane. --> 109 | <SourceLocation resid="Taskpane.Url" /> 110 | </Action> 111 | </Control> 112 | </Group> 113 | </OfficeTab> 114 | </ExtensionPoint> 115 | </DesktopFormFactor> 116 | </Host> 117 | </Hosts> 118 | 119 | <!-- You can use resources across hosts and form factors. --> 120 | <Resources> 121 | <bt:Images> 122 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 123 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 124 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 125 | </bt:Images> 126 | <bt:Urls> 127 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 128 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 129 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 130 | </bt:Urls> 131 | <!-- ShortStrings max characters==125. --> 132 | <bt:ShortStrings> 133 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 134 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 135 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 136 | </bt:ShortStrings> 137 | <!-- LongStrings max characters==250. --> 138 | <bt:LongStrings> 139 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> 140 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 141 | </bt:LongStrings> 142 | </Resources> 143 | </VersionOverrides> 144 | <!-- End Add-in Commands Mode integration. --> 145 | 146 | </OfficeApp> 147 | -------------------------------------------------------------------------------- /src/app/templates/hosts/onenote/manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <OfficeApp 3 | xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 6 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" 7 | xsi:type="TaskPaneApp"> 8 | 9 | <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> 10 | 11 | <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> 12 | <Id><%= projectId %></Id> 13 | 14 | <!--Version. Updates from the store only get triggered if there is a version change. --> 15 | <Version>1.0.0.0</Version> 16 | <ProviderName>[Provider name]</ProviderName> 17 | <DefaultLocale>en-US</DefaultLocale> 18 | <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> 19 | <DisplayName DefaultValue="<%= projectDisplayName %>" /> 20 | <Description DefaultValue="[Notebook Add-in description]"/> 21 | 22 | <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> 23 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png" /> 24 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-80.png"/> 25 | 26 | <!--If you plan to submit this add-in to the Office Store, uncomment the SupportUrl element below--> 27 | <!--<SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />--> 28 | 29 | <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> 30 | <AppDomains> 31 | <AppDomain>AppDomain1</AppDomain> 32 | <AppDomain>AppDomain2</AppDomain> 33 | <AppDomain>AppDomain3</AppDomain> 34 | </AppDomains> 35 | <!--End Basic Settings. --> 36 | 37 | <!--Begin TaskPane Mode integration. This section is used if there are no VersionOverrides or if the Office client version does not support add-in commands. --> 38 | <Hosts> 39 | <Host Name="Notebook" /> 40 | </Hosts> 41 | <DefaultSettings> 42 | <SourceLocation DefaultValue="https://localhost:3000/index.html" /> 43 | </DefaultSettings> 44 | <!-- End TaskPane Mode integration. --> 45 | 46 | <Permissions>ReadWriteDocument</Permissions> 47 | 48 | <!-- Begin Add-in Commands Mode integration. --> 49 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 50 | 51 | <!-- The Hosts node is required. --> 52 | <Hosts> 53 | <!-- Each host can have a different set of commands. --> 54 | <!-- Excel host is Workbook, Word host is Document, and PowerPoint host is Presentation. --> 55 | <!-- Make sure the hosts you override match the hosts declared in the top section of the manifest. --> 56 | <Host xsi:type="Notebook"> 57 | <!-- Form factor. Currently only DesktopFormFactor is supported. --> 58 | <DesktopFormFactor> 59 | <!--"This code enables a customizable message to be displayed when the add-in is loaded successfully upon individual install."--> 60 | <GetStarted> 61 | <!-- Title of the Getting Started callout. resid points to a ShortString resource --> 62 | <Title resid="GetStarted.Title"/> 63 | 64 | <!-- Description of the Getting Started callout. resid points to a LongString resource --> 65 | <Description resid="GetStarted.Description"/> 66 | 67 | <!-- Point to a url resource which details how the add-in should be used. --> 68 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 69 | </GetStarted> 70 | <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. 71 | Think of the FunctionFile as the code behind ExecuteFunction. --> 72 | <FunctionFile resid="Commands.Url" /> 73 | 74 | <!-- PrimaryCommandSurface is the main Office Ribbon. --> 75 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 76 | <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> 77 | <OfficeTab id="TabHome"> 78 | <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> 79 | <Group id="CommandsGroup"> 80 | <!-- Label for your group. resid must point to a ShortString resource. --> 81 | <Label resid="CommandsGroup.Label" /> 82 | <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> 83 | <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> 84 | <Icon> 85 | <bt:Image size="16" resid="Icon.16x16" /> 86 | <bt:Image size="32" resid="Icon.32x32" /> 87 | <bt:Image size="80" resid="Icon.80x80" /> 88 | </Icon> 89 | 90 | <!-- Control. It can be of type "Button" or "Menu". --> 91 | <Control xsi:type="Button" id="TaskpaneButton"> 92 | <Label resid="TaskpaneButton.Label" /> 93 | <Supertip> 94 | <!-- ToolTip title. resid must point to a ShortString resource. --> 95 | <Title resid="TaskpaneButton.Label" /> 96 | <!-- ToolTip description. resid must point to a LongString resource. --> 97 | <Description resid="TaskpaneButton.Tooltip" /> 98 | </Supertip> 99 | <Icon> 100 | <bt:Image size="16" resid="Icon.16x16" /> 101 | <bt:Image size="32" resid="Icon.32x32" /> 102 | <bt:Image size="80" resid="Icon.80x80" /> 103 | </Icon> 104 | 105 | <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> 106 | <Action xsi:type="ShowTaskpane"> 107 | <TaskpaneId>ButtonId1</TaskpaneId> 108 | <!-- Provide a url resource id for the location that will be displayed on the task pane. --> 109 | <SourceLocation resid="Taskpane.Url" /> 110 | </Action> 111 | </Control> 112 | </Group> 113 | </OfficeTab> 114 | </ExtensionPoint> 115 | </DesktopFormFactor> 116 | </Host> 117 | </Hosts> 118 | 119 | <!-- You can use resources across hosts and form factors. --> 120 | <Resources> 121 | <bt:Images> 122 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 123 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 124 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 125 | </bt:Images> 126 | <bt:Urls> 127 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 128 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 129 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 130 | </bt:Urls> 131 | <!-- ShortStrings max characters==125. --> 132 | <bt:ShortStrings> 133 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 134 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 135 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 136 | </bt:ShortStrings> 137 | <!-- LongStrings max characters==250. --> 138 | <bt:LongStrings> 139 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> 140 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 141 | </bt:LongStrings> 142 | </Resources> 143 | </VersionOverrides> 144 | <!-- End Add-in Commands Mode integration. --> 145 | 146 | </OfficeApp> 147 | -------------------------------------------------------------------------------- /src/app/templates/hosts/outlook/manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <OfficeApp 3 | xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 6 | xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" 7 | xsi:type="MailApp"> 8 | 9 | <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> 10 | 11 | <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> 12 | <Id><%= projectId %></Id> 13 | 14 | <!--Version. Updates from the store only get triggered if there is a version change. --> 15 | <Version>1.0.0.0</Version> 16 | <ProviderName>[Provider name]</ProviderName> 17 | <DefaultLocale>en-US</DefaultLocale> 18 | <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> 19 | <DisplayName DefaultValue="<%= projectDisplayName %>" /> 20 | <Description DefaultValue="[Outlook Add-in description]"/> 21 | 22 | <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> 23 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-64.png" /> 24 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-128.png"/> 25 | 26 | <!--If you plan to submit this add-in to the Office Store, uncomment the SupportUrl element below--> 27 | <SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" /> 28 | 29 | <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> 30 | <AppDomains> 31 | <AppDomain>AppDomain1</AppDomain> 32 | <AppDomain>AppDomain2</AppDomain> 33 | <AppDomain>AppDomain3</AppDomain> 34 | </AppDomains> 35 | <!--End Basic Settings. --> 36 | 37 | <Hosts> 38 | <Host Name="Mailbox" /> 39 | </Hosts> 40 | <Requirements> 41 | <Sets> 42 | <Set Name="Mailbox" MinVersion="1.1" /> 43 | </Sets> 44 | </Requirements> 45 | <FormSettings> 46 | <Form xsi:type="ItemRead"> 47 | <DesktopSettings> 48 | <SourceLocation DefaultValue="https://localhost:3000/index.html"/> 49 | <RequestedHeight>250</RequestedHeight> 50 | </DesktopSettings> 51 | </Form> 52 | </FormSettings> 53 | 54 | <Permissions>ReadWriteItem</Permissions> 55 | <Rule xsi:type="RuleCollection" Mode="Or"> 56 | <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" /> 57 | </Rule> 58 | <DisableEntityHighlighting>false</DisableEntityHighlighting> 59 | 60 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0"> 61 | <Requirements> 62 | <bt:Sets DefaultMinVersion="1.3"> 63 | <bt:Set Name="Mailbox" /> 64 | </bt:Sets> 65 | </Requirements> 66 | <Hosts> 67 | <Host xsi:type="MailHost"> 68 | 69 | <DesktopFormFactor> 70 | <!-- Location of the Functions that UI-less buttons can trigger (ExecuteFunction Actions). --> 71 | <FunctionFile resid="Commands.Url" /> 72 | 73 | <!-- Message Read --> 74 | <ExtensionPoint xsi:type="MessageReadCommandSurface"> 75 | <!-- Use the default tab of the ExtensionPoint or create your own with <CustomTab id="myTab"> --> 76 | <OfficeTab id="TabDefault"> 77 | <!-- Up to 6 Groups added per Tab --> 78 | <Group id="msgReadGroup"> 79 | <Label resid="GroupLabel" /> 80 | <!-- Launch the add-in : task pane button --> 81 | <Control xsi:type="Button" id="msgReadOpenPaneButton"> 82 | <Label resid="TaskpaneButton.Label" /> 83 | <Supertip> 84 | <Title resid="TaskpaneButton.Label" /> 85 | <Description resid="TaskpaneButton.Tooltip" /> 86 | </Supertip> 87 | <Icon> 88 | <bt:Image size="16" resid="Icon.16x16" /> 89 | <bt:Image size="32" resid="Icon.32x32" /> 90 | <bt:Image size="80" resid="Icon.80x80" /> 91 | </Icon> 92 | <Action xsi:type="ShowTaskpane"> 93 | <SourceLocation resid="Taskpane.Url" /> 94 | </Action> 95 | </Control> 96 | <!-- Go to http://aka.ms/ButtonCommands to learn how to add more Controls: ExecuteFunction and Menu --> 97 | </Group> 98 | </OfficeTab> 99 | </ExtensionPoint> 100 | <!-- Go to http://aka.ms/ExtensionPointsCommands to learn how to add more Extension Points: MessageRead, AppointmentOrganizer, AppointmentAttendee --> 101 | </DesktopFormFactor> 102 | </Host> 103 | </Hosts> 104 | 105 | <Resources> 106 | <bt:Images> 107 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 108 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 109 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 110 | </bt:Images> 111 | <bt:Urls> 112 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 113 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 114 | </bt:Urls> 115 | <bt:ShortStrings> 116 | <bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/> 117 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane"/> 118 | </bt:ShortStrings> 119 | <bt:LongStrings> 120 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens a pane displaying all available properties."/> 121 | </bt:LongStrings> 122 | </Resources> 123 | </VersionOverrides> 124 | </OfficeApp> 125 | -------------------------------------------------------------------------------- /src/app/templates/hosts/powerpoint/manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <OfficeApp 3 | xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 6 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" 7 | xsi:type="TaskPaneApp"> 8 | 9 | <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> 10 | 11 | <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> 12 | <Id><%= projectId %></Id> 13 | 14 | <!--Version. Updates from the store only get triggered if there is a version change. --> 15 | <Version>1.0.0.0</Version> 16 | <ProviderName>[Provider name]</ProviderName> 17 | <DefaultLocale>en-US</DefaultLocale> 18 | <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> 19 | <DisplayName DefaultValue="<%= projectDisplayName %>" /> 20 | <Description DefaultValue="[Presentation Add-in description]"/> 21 | 22 | <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> 23 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png" /> 24 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-80.png.png"/> 25 | 26 | <!--If you plan to submit this add-in to the Office Store, uncomment the SupportUrl element below--> 27 | <!--<SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />--> 28 | 29 | <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> 30 | <AppDomains> 31 | <AppDomain>AppDomain1</AppDomain> 32 | <AppDomain>AppDomain2</AppDomain> 33 | <AppDomain>AppDomain3</AppDomain> 34 | </AppDomains> 35 | <!--End Basic Settings. --> 36 | 37 | <!--Begin TaskPane Mode integration. This section is used if there are no VersionOverrides or if the Office client version does not support add-in commands. --> 38 | <Hosts> 39 | <Host Name="Presentation" /> 40 | </Hosts> 41 | <DefaultSettings> 42 | <SourceLocation DefaultValue="https://localhost:3000/index.html" /> 43 | </DefaultSettings> 44 | <!-- End TaskPane Mode integration. --> 45 | 46 | <Permissions>ReadWriteDocument</Permissions> 47 | 48 | <!-- Begin Add-in Commands Mode integration. --> 49 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 50 | 51 | <!-- The Hosts node is required. --> 52 | <Hosts> 53 | <!-- Each host can have a different set of commands. --> 54 | <!-- Excel host is Workbook, Word host is Document, and PowerPoint host is Presentation. --> 55 | <!-- Make sure the hosts you override match the hosts declared in the top section of the manifest. --> 56 | <Host xsi:type="Presentation"> 57 | <!-- Form factor. Currently only DesktopFormFactor is supported. --> 58 | <DesktopFormFactor> 59 | <!--"This code enables a customizable message to be displayed when the add-in is loaded successfully upon individual install."--> 60 | <GetStarted> 61 | <!-- Title of the Getting Started callout. resid points to a ShortString resource --> 62 | <Title resid="GetStarted.Title"/> 63 | 64 | <!-- Description of the Getting Started callout. resid points to a LongString resource --> 65 | <Description resid="GetStarted.Description"/> 66 | 67 | <!-- Point to a url resource which details how the add-in should be used. --> 68 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 69 | </GetStarted> 70 | <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. 71 | Think of the FunctionFile as the code behind ExecuteFunction. --> 72 | <FunctionFile resid="Commands.Url" /> 73 | 74 | <!-- PrimaryCommandSurface is the main Office Ribbon. --> 75 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 76 | <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> 77 | <OfficeTab id="TabHome"> 78 | <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> 79 | <Group id="CommandsGroup"> 80 | <!-- Label for your group. resid must point to a ShortString resource. --> 81 | <Label resid="CommandsGroup.Label" /> 82 | <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> 83 | <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> 84 | <Icon> 85 | <bt:Image size="16" resid="Icon.16x16" /> 86 | <bt:Image size="32" resid="Icon.32x32" /> 87 | <bt:Image size="80" resid="Icon.80x80" /> 88 | </Icon> 89 | 90 | <!-- Control. It can be of type "Button" or "Menu". --> 91 | <Control xsi:type="Button" id="TaskpaneButton"> 92 | <Label resid="TaskpaneButton.Label" /> 93 | <Supertip> 94 | <!-- ToolTip title. resid must point to a ShortString resource. --> 95 | <Title resid="TaskpaneButton.Label" /> 96 | <!-- ToolTip description. resid must point to a LongString resource. --> 97 | <Description resid="TaskpaneButton.Tooltip" /> 98 | </Supertip> 99 | <Icon> 100 | <bt:Image size="16" resid="Icon.16x16" /> 101 | <bt:Image size="32" resid="Icon.32x32" /> 102 | <bt:Image size="80" resid="Icon.80x80" /> 103 | </Icon> 104 | 105 | <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> 106 | <Action xsi:type="ShowTaskpane"> 107 | <TaskpaneId>ButtonId1</TaskpaneId> 108 | <!-- Provide a url resource id for the location that will be displayed on the task pane. --> 109 | <SourceLocation resid="Taskpane.Url" /> 110 | </Action> 111 | </Control> 112 | </Group> 113 | </OfficeTab> 114 | </ExtensionPoint> 115 | </DesktopFormFactor> 116 | </Host> 117 | </Hosts> 118 | 119 | <!-- You can use resources across hosts and form factors. --> 120 | <Resources> 121 | <bt:Images> 122 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 123 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 124 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 125 | </bt:Images> 126 | <bt:Urls> 127 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 128 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 129 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 130 | </bt:Urls> 131 | <!-- ShortStrings max characters==125. --> 132 | <bt:ShortStrings> 133 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 134 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 135 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 136 | </bt:ShortStrings> 137 | <!-- LongStrings max characters==250. --> 138 | <bt:LongStrings> 139 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> 140 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 141 | </bt:LongStrings> 142 | </Resources> 143 | </VersionOverrides> 144 | <!-- End Add-in Commands Mode integration. --> 145 | 146 | </OfficeApp> 147 | -------------------------------------------------------------------------------- /src/app/templates/hosts/project/manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <OfficeApp 3 | xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 6 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" 7 | xsi:type="TaskPaneApp"> 8 | 9 | <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> 10 | 11 | <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> 12 | <Id><%= projectId %></Id> 13 | 14 | <!--Version. Updates from the store only get triggered if there is a version change. --> 15 | <Version>1.0.0.0</Version> 16 | <ProviderName>[Provider name]</ProviderName> 17 | <DefaultLocale>en-US</DefaultLocale> 18 | <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> 19 | <DisplayName DefaultValue="<%= projectDisplayName %>" /> 20 | <Description DefaultValue="[Project Add-in description]"/> 21 | 22 | <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> 23 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png" /> 24 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-80.png.png"/> 25 | 26 | <!--If you plan to submit this add-in to the Office Store, uncomment the SupportUrl element below--> 27 | <!--<SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />--> 28 | 29 | <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> 30 | <AppDomains> 31 | <AppDomain>AppDomain1</AppDomain> 32 | <AppDomain>AppDomain2</AppDomain> 33 | <AppDomain>AppDomain3</AppDomain> 34 | </AppDomains> 35 | <!--End Basic Settings. --> 36 | 37 | <!--Begin TaskPane Mode integration. This section is used if there are no VersionOverrides or if the Office client version does not support add-in commands. --> 38 | <Hosts> 39 | <Host Name="Project" /> 40 | </Hosts> 41 | <DefaultSettings> 42 | <SourceLocation DefaultValue="https://localhost:3000/index.html" /> 43 | </DefaultSettings> 44 | <!-- End TaskPane Mode integration. --> 45 | 46 | <Permissions>ReadWriteDocument</Permissions> 47 | 48 | <!-- Begin Add-in Commands Mode integration. --> 49 | 50 | <!-- End Add-in Commands Mode integration. --> 51 | 52 | </OfficeApp> 53 | -------------------------------------------------------------------------------- /src/app/templates/hosts/word/manifest.xml: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="UTF-8"?> 2 | <OfficeApp 3 | xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 4 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 | xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 6 | xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" 7 | xsi:type="TaskPaneApp"> 8 | 9 | <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> 10 | 11 | <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> 12 | <Id><%= projectId %></Id> 13 | 14 | <!--Version. Updates from the store only get triggered if there is a version change. --> 15 | <Version>1.0.0.0</Version> 16 | <ProviderName>[Provider name]</ProviderName> 17 | <DefaultLocale>en-US</DefaultLocale> 18 | <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> 19 | <DisplayName DefaultValue="<%= projectDisplayName %>" /> 20 | <Description DefaultValue="[Document Add-in description]"/> 21 | 22 | <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> 23 | <IconUrl DefaultValue="https://localhost:3000/assets/icon-32.png" /> 24 | <HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-80.png"/> 25 | 26 | <!--If you plan to submit this add-in to the Office Store, uncomment the SupportUrl element below--> 27 | <!--<SupportUrl DefaultValue="[Insert the URL of a page that provides support information for the app]" />--> 28 | 29 | <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> 30 | <AppDomains> 31 | <AppDomain>AppDomain1</AppDomain> 32 | <AppDomain>AppDomain2</AppDomain> 33 | <AppDomain>AppDomain3</AppDomain> 34 | </AppDomains> 35 | <!--End Basic Settings. --> 36 | 37 | <!--Begin TaskPane Mode integration. This section is used if there are no VersionOverrides or if the Office client version does not support add-in commands. --> 38 | <Hosts> 39 | <Host Name="Document" /> 40 | </Hosts> 41 | <DefaultSettings> 42 | <SourceLocation DefaultValue="https://localhost:3000/index.html" /> 43 | </DefaultSettings> 44 | <!-- End TaskPane Mode integration. --> 45 | 46 | <Permissions>ReadWriteDocument</Permissions> 47 | 48 | <!-- Begin Add-in Commands Mode integration. --> 49 | <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> 50 | 51 | <!-- The Hosts node is required. --> 52 | <Hosts> 53 | <!-- Each host can have a different set of commands. --> 54 | <!-- Excel host is Workbook, Word host is Document, and PowerPoint host is Presentation. --> 55 | <!-- Make sure the hosts you override match the hosts declared in the top section of the manifest. --> 56 | <Host xsi:type="Document"> 57 | <!-- Form factor. Currently only DesktopFormFactor is supported. --> 58 | <DesktopFormFactor> 59 | <!--"This code enables a customizable message to be displayed when the add-in is loaded successfully upon individual install."--> 60 | <GetStarted> 61 | <!-- Title of the Getting Started callout. resid points to a ShortString resource --> 62 | <Title resid="GetStarted.Title"/> 63 | 64 | <!-- Description of the Getting Started callout. resid points to a LongString resource --> 65 | <Description resid="GetStarted.Description"/> 66 | 67 | <!-- Point to a url resource which details how the add-in should be used. --> 68 | <LearnMoreUrl resid="GetStarted.LearnMoreUrl"/> 69 | </GetStarted> 70 | <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. 71 | Think of the FunctionFile as the code behind ExecuteFunction. --> 72 | <FunctionFile resid="Commands.Url" /> 73 | 74 | <!-- PrimaryCommandSurface is the main Office Ribbon. --> 75 | <ExtensionPoint xsi:type="PrimaryCommandSurface"> 76 | <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> 77 | <OfficeTab id="TabHome"> 78 | <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> 79 | <Group id="CommandsGroup"> 80 | <!-- Label for your group. resid must point to a ShortString resource. --> 81 | <Label resid="CommandsGroup.Label" /> 82 | <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> 83 | <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> 84 | <Icon> 85 | <bt:Image size="16" resid="Icon.16x16" /> 86 | <bt:Image size="32" resid="Icon.32x32" /> 87 | <bt:Image size="80" resid="Icon.80x80" /> 88 | </Icon> 89 | 90 | <!-- Control. It can be of type "Button" or "Menu". --> 91 | <Control xsi:type="Button" id="TaskpaneButton"> 92 | <Label resid="TaskpaneButton.Label" /> 93 | <Supertip> 94 | <!-- ToolTip title. resid must point to a ShortString resource. --> 95 | <Title resid="TaskpaneButton.Label" /> 96 | <!-- ToolTip description. resid must point to a LongString resource. --> 97 | <Description resid="TaskpaneButton.Tooltip" /> 98 | </Supertip> 99 | <Icon> 100 | <bt:Image size="16" resid="Icon.16x16" /> 101 | <bt:Image size="32" resid="Icon.32x32" /> 102 | <bt:Image size="80" resid="Icon.80x80" /> 103 | </Icon> 104 | 105 | <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> 106 | <Action xsi:type="ShowTaskpane"> 107 | <TaskpaneId>ButtonId1</TaskpaneId> 108 | <!-- Provide a url resource id for the location that will be displayed on the task pane. --> 109 | <SourceLocation resid="Taskpane.Url" /> 110 | </Action> 111 | </Control> 112 | </Group> 113 | </OfficeTab> 114 | </ExtensionPoint> 115 | </DesktopFormFactor> 116 | </Host> 117 | </Hosts> 118 | 119 | <!-- You can use resources across hosts and form factors. --> 120 | <Resources> 121 | <bt:Images> 122 | <bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16.png"/> 123 | <bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32.png"/> 124 | <bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80.png"/> 125 | </bt:Images> 126 | <bt:Urls> 127 | <bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> 128 | <bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" /> 129 | <bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" /> 130 | </bt:Urls> 131 | <!-- ShortStrings max characters==125. --> 132 | <bt:ShortStrings> 133 | <bt:String id="GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> 134 | <bt:String id="CommandsGroup.Label" DefaultValue="Commands Group" /> 135 | <bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane" /> 136 | </bt:ShortStrings> 137 | <!-- LongStrings max characters==250. --> 138 | <bt:LongStrings> 139 | <bt:String id="GetStarted.Description" DefaultValue="Your sample add-in loaded successfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> 140 | <bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> 141 | </bt:LongStrings> 142 | </Resources> 143 | </VersionOverrides> 144 | <!-- End Add-in Commands Mode integration. --> 145 | 146 | </OfficeApp> 147 | -------------------------------------------------------------------------------- /src/app/templates/manifest-only/.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 2 | # See LICENSE in the project root for license information. 3 | 4 | # Created by https://www.gitignore.io/api/osx,windows 5 | 6 | ### OSX ### 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | 34 | ### Windows ### 35 | # Windows image file caches 36 | Thumbs.db 37 | ehthumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | 42 | # Recycle Bin used on file shares 43 | $RECYCLE.BIN/ 44 | 45 | # Windows Installer files 46 | *.cab 47 | *.msi 48 | *.msm 49 | *.msp 50 | 51 | # Windows shortcuts 52 | *.lnk 53 | 54 | # Ignore the following folders 55 | .vs 56 | .idea 57 | *.log 58 | obj 59 | bin 60 | *.user 61 | 62 | node_modules/** 63 | typings/** 64 | .vscode/** 65 | -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/icon-128.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/icon-16.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/icon-32.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/icon-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/icon-64.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/icon-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/icon-80.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/assets/logo-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/app/templates/manifest-only/assets/logo-filled.png -------------------------------------------------------------------------------- /src/app/templates/manifest-only/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= projectInternalName %>", 3 | "description": "", 4 | "author": "", 5 | "version": "0.1.0", 6 | "scripts": { 7 | "validate": "office-toolbox validate -m manifest.xml" 8 | }, 9 | "dependencies": { 10 | "office-toolbox": "^0.1.1" 11 | }, 12 | "devDependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /src/docs/assets/browsersync.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/browsersync.gif -------------------------------------------------------------------------------- /src/docs/assets/designtemplate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/designtemplate.gif -------------------------------------------------------------------------------- /src/docs/assets/gettingstarted-fast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/gettingstarted-fast.gif -------------------------------------------------------------------------------- /src/docs/assets/gettingstarted-slow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/gettingstarted-slow.gif -------------------------------------------------------------------------------- /src/docs/assets/quickstart.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/quickstart.gif -------------------------------------------------------------------------------- /src/docs/assets/ssl-chrome-bypass.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-chrome-bypass.gif -------------------------------------------------------------------------------- /src/docs/assets/ssl-chrome-devtool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-chrome-devtool.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-chrome-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-chrome-error.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-chrome-getcert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-chrome-getcert.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-chrome-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-chrome-good.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-edge-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-edge-error.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-01.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-02.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-03.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-04.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-05.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-06.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-07.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-08.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-09.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-ie-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-ie-10.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-keychain-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-keychain-01.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-keychain-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-keychain-02.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-01.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-02.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-03.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-04.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-05.png -------------------------------------------------------------------------------- /src/docs/assets/ssl-windows-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssl-windows-06.png -------------------------------------------------------------------------------- /src/docs/assets/ssotemplate-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssotemplate-1.gif -------------------------------------------------------------------------------- /src/docs/assets/ssotemplate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/ssotemplate.gif -------------------------------------------------------------------------------- /src/docs/assets/validator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/validator.gif -------------------------------------------------------------------------------- /src/docs/assets/yo-office-install-cert-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OfficeDev/generator-office/f73923579aba8779f1dd8b487e4ac200b5d9cca4/src/docs/assets/yo-office-install-cert-dialog.png -------------------------------------------------------------------------------- /src/docs/ssl.md: -------------------------------------------------------------------------------- 1 | # Installing the self-signed certificate 2 | 3 | Office Add-ins should use HTTPS (not HTTP) even when you are developing, because Office clients require add-ins and webpages to come from a trusted and secure location. If your add-in fails to load within an Office client, it may be because you haven't installed (trusted) the certificate that the Yeoman generator for Office Add-ins provides. 4 | 5 | When you browse to a site that has an untrusted certificate, the browser will display an error with the certificate, as shown in the following screenshots. 6 | 7 | **Certificate error in the Chrome browser:** 8 | 9 | ![Screenshot of certificate error in Chrome browser](assets/ssl-chrome-error.png) 10 | 11 | **Certificate error in the Edge browser:** 12 | 13 | ![Screenshot of certificate error in Edge browser](assets/ssl-edge-error.png) 14 | 15 | This article describes two methods for installing (trusting) the certificate on your machine. Which method you use will depend upon how recent a version of the [Yeoman generator for Office Add-ins](https://github.com/OfficeDev/generator-office) you used to create your add-in project. 16 | 17 | ## Method #1: automatically install the certificate when prompted 18 | 19 | If you used a sufficiently recent version of the [Yeoman generator for Office Add-ins](https://github.com/OfficeDev/generator-office) to create your add-in project, you'll be prompted to install a certificate when you start the local web server, if a valid certificate doesn't already exist on your machine. 20 | 21 | ![Screenshot of dialog box prompting to install the certificate](assets/yo-office-install-cert-dialog.png) 22 | 23 | Accept this prompt to install the certificate that the Yeoman generator provides, and the certificate will be added to the current user's **Trusted Root Certification Authorities** certificate store. You now have a valid self-signed certificate installed on your machine. 24 | 25 | ## Method #2: manually install the certificate 26 | 27 | If you used an older version of the [Yeoman generator for Office Add-ins](https://github.com/OfficeDev/generator-office) to create your add-in project, you'll need to manually configure your machine to trust the self-signed certificate. The steps for this differ depending on your developer environment (macOS / Windows): 28 | 29 | * [macOS](#macOS) 30 | * [Windows](#windows) 31 | 32 | ### macOS 33 | 34 | 1. In **Finder**, open the **certs** folder in the root folder of your project. 35 | 2. Double-click the **ca.crt** file. 36 | 3. In the **Add Certificates** dialog box, choose **Add**. 37 | 4. You'll be prompted for your credentials. Enter your credentials and choose **Modify Keychain**. 38 | 5. Open the **Keychain Access** utility. 39 | 6. Select the **Certificates** category, and double-click the **localhost-ca** certificate. 40 | 7. In the **Trust** section, set the following value 41 | 42 | **When using this certificate**: **Always Trust** 43 | 44 | 8. Close the dialog. 45 | 9. You'll be prompted for your credentials and will need to enter them to enable the certificate 46 | 47 | At this point everything has been configured. Quit all browsers, then reopen and try to navigate to the local HTTPS site. The browser should report it as a valid certificate: 48 | 49 | ![Screenshot of Chrome browser when certificate is valid](assets/ssl-chrome-good.png) 50 | 51 | ### [Windows](https://technet.microsoft.com/en-us/library/cc754841.aspx) 52 | 53 | Take the following steps to setup the certificate authority cert for localhost: 54 | 55 | 1. Go to {project root}\certs. 56 | 2. Double-click ca.crt, and select **Install Certificate**. 57 | ![](assets/ssl-ie-04.png) 58 | 59 | 3. Select **Local Machine** and select **Next** to continue. 60 | ![](assets/ssl-ie-05.png) 61 | 62 | 4. Select **Place all certificates in the following store** and then select **Browse**. 63 | 5. Select **Trusted Root Certification Authorities** and then select **OK**. 64 | 6. Select **Next** and then **Finish**. 65 | 66 | You now have a self-signed certificate installed on your machine. 67 | 68 | Copyright (c) 2017 Microsoft Corporation. All rights reserved. 69 | -------------------------------------------------------------------------------- /src/test/convert-to-single-host.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | import assert from 'yeoman-assert'; 6 | import * as fs from "fs"; 7 | import helpers from 'yeoman-test'; 8 | import { OfficeAddinManifest, ManifestInfo } from "office-addin-manifest"; 9 | import * as path from 'path'; 10 | import { promisify } from "util"; 11 | import { __dirname } from './utils.js'; 12 | 13 | const hosts = ["Excel", "Onenote", "Outlook", "Powerpoint", "Project", "Word"]; 14 | const manifestXmlFile = "manifest.xml"; 15 | const manifestJsonFile = "manifest.json"; 16 | const packageJsonFile = "package.json"; 17 | const readFileAsync = promisify(fs.readFile); 18 | const unexpectedManifestFiles = [ 19 | 'manifest.excel.xml', 20 | 'manifest.onenote.xml', 21 | 'manifest.outlook.xml', 22 | 'manifest.powerpoint.xml', 23 | 'manifest.project.xml', 24 | 'manifest.word.xml', 25 | ] 26 | 27 | // Test to verify converting a project to a single host 28 | // for Office-Addin-Taskpane Typescript project using Excel host 29 | describe('Office-Addin-Taskpane-Ts projects', () => { 30 | const testProjectName = "TaskpaneProject" 31 | const expectedFiles = [ 32 | packageJsonFile, 33 | manifestXmlFile, 34 | 'src/taskpane/taskpane.ts', 35 | ] 36 | const unexpectedFiles = [ 37 | 'src/taskpane/excel.ts', 38 | 'src/taskpane/onenote.ts', 39 | 'src/taskpane/outlook.ts', 40 | 'src/taskpane/powerpoint.ts', 41 | 'src/taskpane/project.ts', 42 | 'src/taskpane/word.ts' 43 | ] 44 | const answers = { 45 | projectType: "taskpane", 46 | scriptType: "TypeScript", 47 | name: testProjectName, 48 | host: hosts[0] 49 | }; 50 | 51 | before((done) => { 52 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 53 | }); 54 | 55 | it('creates expected files', (done) => { 56 | assert.file(expectedFiles); 57 | assert.noFile(unexpectedFiles); 58 | assert.noFile(unexpectedManifestFiles); 59 | done(); 60 | }); 61 | 62 | it('Package.json is updated properly', async () => { 63 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 64 | const content = JSON.parse(data); 65 | assert.equal(content.config["app_to_debug"], hosts[0].toLowerCase()); 66 | 67 | // Verify host-specific sideload and unload sripts have been removed 68 | let unexexpectedScriptsFound = false; 69 | Object.keys(content.scripts).forEach(function (key) { 70 | if (key.includes("sideload:") || key.includes("unload:")) { 71 | unexexpectedScriptsFound = true; 72 | } 73 | }); 74 | assert.equal(unexexpectedScriptsFound, false); 75 | }); 76 | 77 | it('Manifest.xml is updated appropriately', async () => { 78 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 79 | assert.equal(manifestInfo.hosts?.[0], "Workbook"); 80 | assert.equal(manifestInfo.displayName, testProjectName); 81 | }); 82 | }); 83 | 84 | // Test to verify converting a project to a single host 85 | // for Office-Addin-Taskpane Typescript project using Excel host and prerelease flag 86 | describe('Office-Addin-Taskpane-Ts prerelease projects', () => { 87 | const testProjectName = "Taskpane Project" 88 | const expectedFiles = [ 89 | packageJsonFile, 90 | manifestXmlFile, 91 | 'src/taskpane/taskpane.ts', 92 | ] 93 | const unexpectedFiles = [ 94 | 'src/taskpane/excel.ts', 95 | 'src/taskpane/onenote.ts', 96 | 'src/taskpane/outlook.ts', 97 | 'src/taskpane/powerpoint.ts', 98 | 'src/taskpane/project.ts', 99 | 'src/taskpane/word.ts' 100 | ] 101 | const answers = { 102 | projectType: "taskpane", 103 | scriptType: "TypeScript", 104 | name: testProjectName, 105 | host: hosts[0] 106 | }; 107 | 108 | before((done) => { 109 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true, 'prerelease': true } as any).withPrompts(answers).on('end', done); 110 | }); 111 | 112 | it('creates expected files', (done) => { 113 | assert.file(expectedFiles); 114 | assert.noFile(unexpectedFiles); 115 | assert.noFile(unexpectedManifestFiles); 116 | done(); 117 | }); 118 | 119 | it('Package.json is updated properly', async () => { 120 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 121 | const content = JSON.parse(data); 122 | assert.equal(content.config["app_to_debug"], hosts[0].toLowerCase()); 123 | 124 | // Verify host-specific sideload and unload sripts have been removed 125 | let unexexpectedScriptsFound = false; 126 | Object.keys(content.scripts).forEach(function (key) { 127 | if (key.includes("sideload:") || key.includes("unload:")) { 128 | unexexpectedScriptsFound = true; 129 | } 130 | }); 131 | assert.equal(unexexpectedScriptsFound, false); 132 | }); 133 | it('Manifest.xml is updated appropriately', async () => { 134 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 135 | assert.equal(manifestInfo.hosts?.[0], "Workbook"); 136 | assert.equal(manifestInfo.displayName, testProjectName); // TODO: update when new convert script is in yo-office template branches 137 | }); 138 | }); 139 | 140 | // Test to verify converting a project to a single host 141 | // for Office-Addin-Taskpane Typescript project using Outlook host and a json manifest 142 | describe('Office-Addin-Taskpane-Ts Outlook json project', () => { 143 | const testProjectName = "TaskpaneProject" 144 | const expectedFiles = [ 145 | packageJsonFile, 146 | manifestJsonFile, 147 | 'src/taskpane/taskpane.ts', 148 | ] 149 | const unexpectedFiles = [ 150 | 'src/taskpane/excel.ts', 151 | 'src/taskpane/onenote.ts', 152 | 'src/taskpane/outlook.ts', 153 | 'src/taskpane/powerpoint.ts', 154 | 'src/taskpane/project.ts', 155 | 'src/taskpane/word.ts' 156 | ] 157 | const answers = { 158 | projectType: "taskpane", 159 | scriptType: "TypeScript", 160 | name: testProjectName, 161 | host: hosts[2], 162 | manifestType: "json" 163 | }; 164 | 165 | before((done) => { 166 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true, 'prerelease': true } as any).withPrompts(answers).on('end', done); 167 | }); 168 | 169 | it('creates expected files', (done) => { 170 | assert.file(expectedFiles); 171 | assert.noFile(unexpectedFiles); 172 | assert.noFile(unexpectedManifestFiles); 173 | done(); 174 | }); 175 | 176 | it('Package.json is updated properly', async () => { 177 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 178 | const content = JSON.parse(data); 179 | assert.equal(content.config["app_to_debug"], hosts[2].toLowerCase()); 180 | 181 | // Verify host-specific sideload and unload sripts have been removed 182 | let unexexpectedScriptsFound = false; 183 | Object.keys(content.scripts).forEach(function (key) { 184 | if (key.includes("sideload:") || key.includes("unload:")) { 185 | unexexpectedScriptsFound = true; 186 | } 187 | }); 188 | assert.equal(unexexpectedScriptsFound, false); 189 | }); 190 | 191 | it('Manifest.json is updated appropriately', async () => { 192 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestJsonFile); 193 | assert.equal(manifestInfo.hosts?.[0], "mail"); 194 | assert.equal(manifestInfo.displayName, testProjectName); // TODO: update when new convert script is in yo-office template branches 195 | }); 196 | }); 197 | 198 | // Test to verify converting a project to a single host 199 | // for Office-Addin-Taskpane Typescript project using Outlook host and a xml manifest 200 | describe('Office-Addin-Taskpane-Ts Outlook xml project', () => { 201 | const testProjectName = "TaskpaneProject" 202 | const expectedFiles = [ 203 | packageJsonFile, 204 | manifestXmlFile, 205 | 'src/taskpane/taskpane.ts', 206 | ] 207 | const unexpectedFiles = [ 208 | 'src/taskpane/excel.ts', 209 | 'src/taskpane/onenote.ts', 210 | 'src/taskpane/outlook.ts', 211 | 'src/taskpane/powerpoint.ts', 212 | 'src/taskpane/project.ts', 213 | 'src/taskpane/word.ts' 214 | ] 215 | const answers = { 216 | projectType: "taskpane", 217 | scriptType: "TypeScript", 218 | name: testProjectName, 219 | host: hosts[2], 220 | manifestType: "xml" 221 | }; 222 | 223 | before((done) => { 224 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true, 'prerelease': true } as any).withPrompts(answers).on('end', done); 225 | }); 226 | 227 | it('creates expected files', (done) => { 228 | assert.file(expectedFiles); 229 | assert.noFile(unexpectedFiles); 230 | assert.noFile(unexpectedManifestFiles); 231 | done(); 232 | }); 233 | 234 | it('Package.json is updated properly', async () => { 235 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 236 | const content = JSON.parse(data); 237 | assert.equal(content.config["app_to_debug"], hosts[2].toLowerCase()); 238 | 239 | // Verify host-specific sideload and unload sripts have been removed 240 | let unexexpectedScriptsFound = false; 241 | Object.keys(content.scripts).forEach(function (key) { 242 | if (key.includes("sideload:") || key.includes("unload:")) { 243 | unexexpectedScriptsFound = true; 244 | } 245 | }); 246 | assert.equal(unexexpectedScriptsFound, false); 247 | }); 248 | 249 | it('Manifest.xml is updated appropriately', async () => { 250 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 251 | assert.equal(manifestInfo.hosts?.[0], "Mailbox"); 252 | assert.equal(manifestInfo.displayName, testProjectName); // TODO: update when new convert script is in yo-office template branches 253 | }); 254 | }); 255 | 256 | // Test to verify converting a project to a single host 257 | // for React Typescript project using PowerPoint host 258 | describe('Office-Addin-Taskpane-React-Ts project', () => { 259 | const expectedFiles = [ 260 | packageJsonFile, 261 | manifestXmlFile, 262 | 'src/taskpane/components/App.tsx', , 263 | ] 264 | const unexpectedFiles = [ 265 | 'src/taskpane/components/Excel.App.tsx', 266 | 'src/taskpane/components/Onenote.App.tsx', 267 | 'src/taskpane/components/Outlook.App.tsx', 268 | 'src/taskpane/components/PowerPoint.App.tsx', 269 | 'src/taskpane/components/Project.App.tsx', 270 | 'src/taskpane/components/Word.App.tsx', 271 | ] 272 | const answers = { 273 | projectType: "react", 274 | scriptType: "TypeScript", 275 | name: "ReactProject", 276 | host: hosts[3] 277 | }; 278 | 279 | before((done) => { 280 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 281 | }); 282 | 283 | it('creates expected files', (done) => { 284 | assert.file(expectedFiles); 285 | assert.noFile(unexpectedFiles); 286 | assert.noFile(unexpectedManifestFiles); 287 | done(); 288 | }); 289 | 290 | it('Package.json is updated properly', async () => { 291 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 292 | const content = JSON.parse(data); 293 | assert.equal(content.config["app_to_debug"], hosts[3].toLowerCase()); 294 | 295 | // Verify host-specific sideload and unload sripts have been removed 296 | let unexexpectedScriptsFound = false; 297 | Object.keys(content.scripts).forEach(function (key) { 298 | if (key.includes("sideload:") || key.includes("unload:")) { 299 | unexexpectedScriptsFound = true; 300 | } 301 | }); 302 | assert.equal(unexexpectedScriptsFound, false); 303 | }); 304 | 305 | it('Manifest.xml is updated appropriately', async () => { 306 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 307 | assert.equal(manifestInfo.hosts?.[0], "Presentation"); 308 | }); 309 | }); 310 | 311 | // Test to verify converting a project to a single host using the cli 312 | // for Office-Addin-Taskpane Typescript project using Excel host 313 | describe('Office-Addin-Taskpane-Ts projects via cli', () => { 314 | const testProjectName = "TaskpaneProject" 315 | const expectedFiles = [ 316 | packageJsonFile, 317 | manifestXmlFile, 318 | 'src/taskpane/taskpane.ts', 319 | ] 320 | const unexpectedFiles = [ 321 | 'src/taskpane/excel.ts', 322 | 'src/taskpane/onenote.ts', 323 | 'src/taskpane/outlook.ts', 324 | 'src/taskpane/powerpoint.ts', 325 | 'src/taskpane/project.ts', 326 | 'src/taskpane/word.ts' 327 | ] 328 | const options: any = { 329 | projectType: "taskpane", 330 | name: testProjectName, 331 | host: hosts[0], 332 | ts: true, 333 | test: true 334 | }; 335 | const answers = {}; 336 | 337 | before((done) => { 338 | helpers.run(path.join(__dirname, '../app')).withOptions(options).withPrompts(answers).on('end', done); 339 | }); 340 | 341 | it('creates expected files', (done) => { 342 | assert.file(expectedFiles); 343 | assert.noFile(unexpectedFiles); 344 | assert.noFile(unexpectedManifestFiles); 345 | done(); 346 | }); 347 | 348 | it('Package.json is updated properly', async () => { 349 | const data: string = await readFileAsync(packageJsonFile, 'utf8'); 350 | const content = JSON.parse(data); 351 | assert.equal(content.config["app_to_debug"], hosts[0].toLowerCase()); 352 | 353 | // Verify host-specific sideload and unload sripts have been removed 354 | let unexexpectedScriptsFound = false; 355 | Object.keys(content.scripts).forEach(function (key) { 356 | if (key.includes("sideload:") || key.includes("unload:")) { 357 | unexexpectedScriptsFound = true; 358 | } 359 | }); 360 | assert.equal(unexexpectedScriptsFound, false); 361 | }); 362 | 363 | it('Manifest.xml is updated appropriately', async () => { 364 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 365 | assert.equal(manifestInfo.hosts?.[0], "Workbook"); 366 | assert.equal(manifestInfo.displayName, testProjectName); 367 | }); 368 | }); 369 | 370 | // Test to verify converting a project to a single host 371 | // for SSO Typescript project using Excel host 372 | describe('Office-Addin-Taskpane-SSO-TS project', () => { 373 | const expectedFiles = [ 374 | packageJsonFile, 375 | manifestXmlFile, 376 | '.ENV', 377 | 'src/taskpane/taskpane.ts', 378 | 'src/taskpane/taskpane.html', 379 | 'src/taskpane/taskpane.css', 380 | 'src/helpers/fallbackauthdialog.html', 381 | 'src/helpers/fallbackauthdialog.ts', 382 | 'src/helpers/message-helper.ts', 383 | 'src/helpers/middle-tier-calls.ts', 384 | 'src/helpers/sso-helper.ts', 385 | 'src/middle-tier/app.ts', 386 | 'src/middle-tier/msgraph-helper.ts', 387 | 'src/middle-tier/ssoauth-helper.ts' 388 | ] 389 | const unexpectedFiles = [ 390 | 'src/taskpane/excel.ts', 391 | 'src/taskpane/word.ts', 392 | 'src/taskpane/powerpoint.ts', 393 | 'manifest.excel.xml', 394 | 'manifest.word.xml', 395 | 'manifest.powerpoint.xml' 396 | ] 397 | const answers = { 398 | projectType: "single-sign-on", 399 | scriptType: "TypeScript", 400 | name: "SSOTypeScriptProject", 401 | host: hosts[0] 402 | }; 403 | 404 | before((done) => { 405 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 406 | }); 407 | 408 | it('creates expected files', (done) => { 409 | assert.file(expectedFiles); 410 | assert.noFile(unexpectedFiles); 411 | assert.noFile(unexpectedManifestFiles); 412 | done(); 413 | }); 414 | }); 415 | 416 | // Test to verify converting a project to a single host 417 | // for SSO JavaScript project using PowerPoint host 418 | describe('Office-Addin-Taskpane-SSO-JS project', () => { 419 | const expectedFiles = [ 420 | packageJsonFile, 421 | manifestXmlFile, 422 | '.ENV', 423 | 'src/taskpane/taskpane.js', 424 | 'src/taskpane/taskpane.html', 425 | 'src/taskpane/taskpane.css', 426 | 'src/helpers/documenthelper.js', 427 | 'src/helpers/fallbackauthdialog.html', 428 | 'src/helpers/fallbackauthdialog.js', 429 | 'src/helpers/message-helper.js', 430 | 'src/helpers/middle-tier-calls.js', 431 | 'src/helpers/sso-helper.js', 432 | 'src/middle-tier/app.js', 433 | 'src/middle-tier/msgraph-helper.js', 434 | 'src/middle-tier/ssoauth-helper.js' 435 | ] 436 | const unexpectedFiles = [ 437 | 'src/taskpane/excel.js', 438 | 'src/taskpane/word.js', 439 | 'src/taskpane/powerpoint.js', 440 | 'manifest.excel.xml', 441 | 'manifest.word.xml', 442 | 'manifest.powerpoint.xml' 443 | ] 444 | const answers = { 445 | projectType: "single-sign-on", 446 | scriptType: "JavaScript", 447 | name: "SSOJavaScriptProject", 448 | host: hosts[3] 449 | }; 450 | 451 | before((done) => { 452 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 453 | }); 454 | 455 | it('creates expected files', (done) => { 456 | assert.file(expectedFiles); 457 | assert.noFile(unexpectedFiles); 458 | assert.noFile(unexpectedManifestFiles); 459 | done(); 460 | }); 461 | }); 462 | 463 | // Test to verify converting a project to a single host 464 | // for custom function Typescript project 465 | describe('Custom-Functions-Shared-TS project', () => { 466 | const testProjectName = "CFTypeScriptProject" 467 | const expectedFiles = [ 468 | packageJsonFile, 469 | manifestXmlFile, 470 | 'src/functions/functions.ts', 471 | ] 472 | const unexpectedFiles = [ 473 | 'manifest.json', 474 | ] 475 | const answers = { 476 | projectType: "excel-functions-shared", 477 | scriptType: "TypeScript", 478 | name: testProjectName 479 | }; 480 | 481 | before((done) => { 482 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 483 | }); 484 | 485 | it('creates expected files', (done) => { 486 | assert.file(expectedFiles); 487 | assert.noFile(unexpectedFiles); 488 | assert.noFile(unexpectedManifestFiles); 489 | done(); 490 | }); 491 | it('Manifest.xml is updated appropriately', async () => { 492 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 493 | assert.equal(manifestInfo.hosts?.[0], "Workbook"); 494 | assert.equal(manifestInfo.displayName, testProjectName); 495 | }); 496 | }); 497 | 498 | // Test to verify converting a project to a single host 499 | // for custom functions JavaScript project 500 | describe('Custom-Functions-Shared-JS project', () => { 501 | const testProjectName = "CFJavaScriptProject" 502 | const expectedFiles = [ 503 | packageJsonFile, 504 | manifestXmlFile, 505 | 'src/functions/functions.js', 506 | ] 507 | const unexpectedFiles = [ 508 | 'manifest.json', 509 | ] 510 | const answers = { 511 | projectType: "excel-functions-shared", 512 | scriptType: "JavaScript", 513 | name: testProjectName 514 | }; 515 | 516 | before((done) => { 517 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 518 | }); 519 | 520 | it('creates expected files', (done) => { 521 | assert.file(expectedFiles); 522 | assert.noFile(unexpectedFiles); 523 | assert.noFile(unexpectedManifestFiles); 524 | done(); 525 | }); 526 | it('Manifest.xml is updated appropriately', async () => { 527 | const manifestInfo : ManifestInfo = await OfficeAddinManifest.readManifestFile(manifestXmlFile); 528 | assert.equal(manifestInfo.hosts?.[0], "Workbook"); 529 | assert.equal(manifestInfo.displayName, testProjectName); 530 | }); 531 | }); 532 | -------------------------------------------------------------------------------- /src/test/manifest-only-project.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | 6 | import helpers from 'yeoman-test'; 7 | import assert from 'yeoman-assert'; 8 | import * as path from 'path'; 9 | import { __dirname } from './utils.js'; 10 | 11 | const manifestProject = 'manifest'; 12 | const projectDisplayName = 'My Office Add-in'; 13 | const projectEscapedName = 'my-office-add-in'; 14 | 15 | const expectedFiles = [ 16 | 'package.json', 17 | 'manifest.xml', 18 | 'assets/icon-16.png', 19 | 'assets/icon-32.png', 20 | 'assets/icon-80.png', 21 | 'assets/logo-filled.png' 22 | ] 23 | 24 | const unexpectedFiles = [ 25 | 'function-file/function-file.html', 26 | 'function-file/function-file.js', 27 | 'function-file/function-file.html', 28 | 'function-file/function-file.ts', 29 | 'config/webpack.common.js', 30 | 'config/webpack.dev.js', 31 | 'config/webpack.prod.js' 32 | ] 33 | 34 | /** 35 | * Test addin when user passes in answers 36 | * manifest project, default folder, default host. 37 | */ 38 | describe('manifest project - answers', () => { 39 | const answers = { 40 | projectType: manifestProject, 41 | name: projectDisplayName, 42 | host: 'Excel', 43 | }; 44 | 45 | before((done) => { 46 | helpers.run(path.join(__dirname, '../app')).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 47 | }); 48 | 49 | it('creates expected files', (done) => { 50 | assert.file(expectedFiles); 51 | assert.noFile(unexpectedFiles); 52 | done(); 53 | }); 54 | }); 55 | 56 | /** 57 | * Test addin when user passes in answers and arguments 58 | * manifest project, default folder, default host. 59 | */ 60 | describe('manifest project - answers & args', () => { 61 | const answers = { 62 | name: projectDisplayName, 63 | host: 'Excel', 64 | }; 65 | const argument: string[] = []; 66 | 67 | before((done) => { 68 | argument[0] = manifestProject; 69 | helpers.run(path.join(__dirname, '../app')).withArguments(argument).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 70 | }); 71 | 72 | it('creates expected files', (done) => { 73 | assert.file(expectedFiles); 74 | assert.noFile(unexpectedFiles); 75 | done(); 76 | }); 77 | }); 78 | 79 | /** 80 | * Test addin when user passes in answers and arguments 81 | * manifest project, default folder, default host. 82 | */ 83 | describe('manifest project - answers & args', () => { 84 | const answers = { 85 | host: 'Excel', 86 | }; 87 | const argument: string[] = []; 88 | 89 | before((done) => { 90 | argument[0] = manifestProject; 91 | argument[1] = projectEscapedName; 92 | helpers.run(path.join(__dirname, '../app')).withArguments(argument).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 93 | }); 94 | 95 | it('creates expected files', (done) => { 96 | assert.file(expectedFiles); 97 | assert.noFile(unexpectedFiles); 98 | done(); 99 | }); 100 | }); 101 | 102 | /** 103 | * Test addin when user passes in arguments 104 | * manifest project, default folder, default host. 105 | */ 106 | describe('manifest project - answers & args', () => { 107 | const answers = {}; 108 | const argument: string[] = []; 109 | 110 | before((done) => { 111 | argument[0] = manifestProject; 112 | argument[1] = projectEscapedName; 113 | argument[2] = 'Excel'; 114 | helpers.run(path.join(__dirname, '../app')).withArguments(argument).withOptions({ 'test': true } as any).withPrompts(answers).on('end', done); 115 | }); 116 | 117 | it('creates expected files', (done) => { 118 | assert.file(expectedFiles); 119 | assert.noFile(unexpectedFiles); 120 | done(); 121 | }); 122 | }); 123 | -------------------------------------------------------------------------------- /src/test/utils.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'url'; 2 | import { dirname } from 'path'; 3 | 4 | export const __dirname = dirname(fileURLToPath(import.meta.url)); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "emitDecoratorMetadata": true, 5 | "esModuleInterop": true, 6 | "experimentalDecorators": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "module": "Node16", 9 | "moduleResolution": "nodenext", 10 | "noEmitOnError": true, 11 | "noImplicitAny": false, 12 | "outDir": "generators", 13 | "removeComments": false, 14 | "rootDir": "src", 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es2022", 18 | "lib": [ 19 | "dom", 20 | "es2022" 21 | ] 22 | }, 23 | "exclude": [ 24 | "generators/**", 25 | "src/app/templates/**/*", 26 | "node_modules" 27 | ] 28 | } --------------------------------------------------------------------------------