├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── analyze.yaml │ ├── build.yaml │ ├── first-interaction.yaml │ └── stale.yaml ├── .gitignore ├── .markdownlint.json ├── .playps.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── GitVersion.yml ├── LICENSE ├── PSDocs.sln ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── ThirdPartyNotices.txt ├── build.ps1 ├── docs ├── commands │ ├── .markdownlint.json │ └── PSDocs │ │ └── en-US │ │ ├── Get-PSDocument.md │ │ ├── Get-PSDocumentHeader.md │ │ ├── Invoke-PSDocument.md │ │ ├── New-PSDocumentOption.md │ │ └── PSDocs.md ├── concepts │ └── PSDocs │ │ └── en-US │ │ ├── about_PSDocs_Configuration.md │ │ ├── about_PSDocs_Conventions.md │ │ ├── about_PSDocs_Options.md │ │ ├── about_PSDocs_Selectors.md │ │ └── about_PSDocs_Variables.md ├── examples │ ├── Get-child-item-output.md │ ├── SharePoint-config-output.md │ └── Yaml-header-output.md ├── install-instructions.md ├── keywords │ └── PSDocs │ │ └── en-US │ │ └── about_PSDocs_Keywords.md ├── scenarios │ ├── arm-template │ │ ├── arm-template.Doc.ps1 │ │ ├── arm-template.md │ │ ├── metadata.json │ │ ├── output.md │ │ └── template.json │ ├── benchmark │ │ └── results-v0.7.0.md │ ├── directory │ │ └── directory.md │ ├── docfx │ │ └── integration-with-docfx.md │ └── dsc-mof │ │ └── dsc-mof.md └── upgrade-notes.md ├── modules.json ├── pipeline.build.ps1 ├── ps-docs.yaml ├── ps-project.yaml ├── ps-rule.yaml ├── schemas ├── PSDocs-language.schema.json └── PSDocs-options.schema.json ├── scripts ├── dependencies.psm1 └── pipeline-deps.ps1 ├── src ├── PSDocs.Benchmark │ ├── Benchmark.Doc.ps1 │ ├── PSDocs.Benchmark.csproj │ ├── PSDocs.cs │ └── Program.cs └── PSDocs │ ├── Annotations │ └── CommentMetadata.cs │ ├── Commands │ ├── BlockQuoteCommand.cs │ ├── CodeCommand.cs │ ├── DefinitionCommand.cs │ ├── ExportConventionCommand.cs │ ├── IncludeCommand.cs │ ├── InvokeConventionCommand.cs │ ├── InvokeDocumentCommand.cs │ ├── Keyword.cs │ ├── ListCommand.cs │ ├── MetadataCommand.cs │ ├── NoteCommand.cs │ ├── SectionCommand.cs │ ├── TableCommand.cs │ ├── TitleCommand.cs │ └── WarningCommand.cs │ ├── Common │ ├── DictionaryExtensions.cs │ ├── EnvironmentHelper.cs │ ├── ExpressionHelpers.cs │ ├── HashtableExtensions.cs │ ├── IBindingContext.cs │ ├── JsonConverters.cs │ ├── KeyMapDictionary.cs │ ├── NameToken.cs │ ├── ObjectHelper.cs │ ├── PSObjectExtensions.cs │ ├── StringExtensions.cs │ └── YamlConverters.cs │ ├── Configuration │ ├── ColumnPadding.cs │ ├── ConfigurationOption.cs │ ├── DocumentOption.cs │ ├── EdgePipeOption.cs │ ├── ExecutionOption.cs │ ├── FieldMap.cs │ ├── InputFormat.cs │ ├── InputOption.cs │ ├── LanguageMode.cs │ ├── MarkdownEncoding.cs │ ├── MarkdownOption.cs │ ├── OutputOption.cs │ └── PSDocumentOption.cs │ ├── Data │ ├── CodeContent.cs │ ├── IDocumentBuilder.cs │ ├── ITargetInfo.cs │ ├── InputFileInfo.cs │ └── Internal │ │ └── ScriptDocumentBuilder.cs │ ├── Definitions │ ├── Conventions │ │ ├── BaseDocumentConvention.cs │ │ ├── DefaultDocumentConvention.cs │ │ └── ScriptBlockDocumentConvention.cs │ ├── IAnnotated.cs │ ├── IDocumentConvention.cs │ ├── IDocumentDefinition.cs │ ├── Resource.cs │ ├── Selectors │ │ ├── Selector.cs │ │ ├── SelectorContext.cs │ │ ├── SelectorExpressions.cs │ │ └── SelectorVisitor.cs │ ├── Spec.cs │ └── SpecFactory.cs │ ├── Models │ ├── Alignment.cs │ ├── BlockQuote.cs │ ├── Code.cs │ ├── Document.cs │ ├── DocumentFilter.cs │ ├── DocumentNode.cs │ ├── DocumentNodeType.cs │ ├── Include.cs │ ├── ModelHelper.cs │ ├── PSDocsContext.cs │ ├── PSDocsHelper.cs │ ├── Section.cs │ ├── SectionNode.cs │ ├── Table.cs │ ├── TableBuilder.cs │ ├── TableColumnHeader.cs │ └── Text.cs │ ├── PSDocs.Format.ps1xml │ ├── PSDocs.csproj │ ├── PSDocs.psd1 │ ├── PSDocs.psm1 │ ├── Pipeline │ ├── Execeptions.cs │ ├── GetPipeline.cs │ ├── HostContext.cs │ ├── InputPathBuilder.cs │ ├── InstanceNameBinder.cs │ ├── InvokePipeline.cs │ ├── OptionContext.cs │ ├── Output │ │ └── HostPipelineWriter.cs │ ├── PathBuilder.cs │ ├── PipelineBuilder.cs │ ├── PipelineContext.cs │ ├── PipelineReceiver.cs │ ├── PipelineStream.cs │ ├── PipelineWriter.cs │ ├── SourcePipeline.cs │ ├── StreamPipeline.cs │ └── TargetObject.cs │ ├── Processor │ ├── IDocumentProcessor.cs │ ├── IDocumentResult.cs │ └── Markdown │ │ ├── MarkdownProcessor.cs │ │ └── MarkdownProcessorContext.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Resources │ ├── PSDocsResources.Designer.cs │ ├── PSDocsResources.resx │ ├── ViewStrings.Designer.cs │ └── ViewStrings.resx │ ├── Runtime │ ├── Configuration.cs │ ├── DocumentContext.cs │ ├── Host.cs │ ├── HostHelper.cs │ ├── HostState.cs │ ├── ILanguageBlock.cs │ ├── LanguageAst.cs │ ├── LanguageScriptBlock.cs │ ├── LocalizedData.cs │ ├── PSDocs.cs │ ├── ResourceExtent.cs │ ├── RunspaceContext.cs │ ├── ScopedItem.cs │ ├── ScriptDocumentBlock.cs │ ├── SourceScope.cs │ ├── StringContent.cs │ └── StringStream.cs │ ├── en-AU │ └── PSDocs.Resources.psd1 │ ├── en-GB │ └── PSDocs.Resources.psd1 │ └── en-US │ └── PSDocs.Resources.psd1 └── tests └── PSDocs.Tests ├── FromFile.Cmdlets.Doc.ps1 ├── FromFile.Conventions.Doc.ps1 ├── FromFile.Doc.ps1 ├── FromFile.Keyword.Doc.ps1 ├── FromFile.Selector.Doc.ps1 ├── FromFile.TestObjects.yaml ├── FromFile.Variables.Doc.ps1 ├── IncludeFile.md ├── IncludeFile2.md ├── KeywordTests.cs ├── LanguageVisitorTests.cs ├── MarkdownProcessorTests.cs ├── Models └── TestModel.cs ├── PSDocs.BlockQuote.Tests.ps1 ├── PSDocs.Code.Tests.ps1 ├── PSDocs.Common.Tests.ps1 ├── PSDocs.Conventions.Tests.ps1 ├── PSDocs.Include.Tests.ps1 ├── PSDocs.Metadata.Tests.ps1 ├── PSDocs.Note.Tests.ps1 ├── PSDocs.Options.Tests.ps1 ├── PSDocs.Section.Tests.ps1 ├── PSDocs.Selector.Tests.ps1 ├── PSDocs.Table.Tests.ps1 ├── PSDocs.Tests.csproj ├── PSDocs.Tests.yml ├── PSDocs.Title.Tests.ps1 ├── PSDocs.Variables.Tests.ps1 ├── PSDocs.Warning.Tests.ps1 ├── PipelineTests.cs ├── SelectorTests.cs ├── Selectors.Doc.yaml ├── StringContentTests.cs ├── TestCommandRuntime.cs ├── TestModule ├── TestModule.psd1 ├── docs │ ├── Selector.Doc.yaml │ └── Test.Doc.ps1 ├── en-AU │ └── PSDocs-strings.psd1 ├── en-US │ └── PSDocs-strings.psd1 ├── en-ZZ │ └── PSDocs-strings.psd1 └── en │ └── PSDocs-strings.psd1 ├── Usings.cs ├── WithError.Doc.ps1 ├── en-AU └── IncludeFile3.md ├── en-US ├── IncludeFile3.md └── PSDocs-strings.psd1 └── psdocs.yml /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/articles/about-codeowners/ 2 | * @microsoft/psdocs 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report errors or unexpected behaviour 4 | --- 5 | 6 | **Description of the issue** 7 | 8 | 9 | 10 | **To Reproduce** 11 | 12 | Steps to reproduce the issue: 13 | 14 | ```powershell 15 | 16 | ``` 17 | 18 | **Expected behaviour** 19 | 20 | 21 | 22 | **Error output** 23 | 24 | 25 | 26 | ```text 27 | 28 | ``` 29 | 30 | **Module in use and version:** 31 | 32 | - Module: PSDocs 33 | - Version: **[e.g. 0.9.0]** 34 | 35 | Captured output from `$PSVersionTable`: 36 | 37 | ```text 38 | 39 | ``` 40 | 41 | **Additional context** 42 | 43 | 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea 4 | --- 5 | 6 | **Is your feature request related to a problem? Please describe.** 7 | 8 | 9 | 10 | **Describe the solution you'd like** 11 | 12 | 13 | 14 | **Describe alternatives you've considered** 15 | 16 | 17 | 18 | **Additional context** 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: If you have a question, please check out Discussions 4 | labels: 'question' 5 | --- 6 | 7 | We use Issues as an issue tracker; for help, discussion, and support questions, please use Discussions. 8 | 9 | Thanks! 😁. 10 | 11 | - https://github.com/Microsoft/PSDocs/discussions 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Summary 2 | 3 | 4 | 5 | ## PR Checklist 6 | 7 | - [ ] PR has a meaningful title 8 | - [ ] Summarized changes 9 | - [ ] Change is not breaking 10 | - [ ] This PR is ready to merge and is not **Work in Progress** 11 | - **Code changes** 12 | - [ ] Have unit tests created/ updated 13 | - [ ] Link to a filed issue 14 | - [ ] [Change log](https://github.com/Microsoft/PSDocs/blob/main/CHANGELOG.md) has been updated with change under unreleased section 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Dependabot configuration 3 | # 4 | 5 | # Please see the documentation for all configuration options: 6 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 7 | 8 | version: 2 9 | updates: 10 | 11 | # Maintain dependencies for NuGet 12 | - package-ecosystem: 'nuget' # See documentation for possible values 13 | directory: '/' # Location of package manifests 14 | schedule: 15 | interval: 'daily' 16 | labels: 17 | - 'dependencies' 18 | reviewers: 19 | - 'microsoft/psdocs' 20 | ignore: 21 | # Ignore upgrades to PS 7.1 for tool chain components at this time 22 | # Testing against PS 7.1 is already completed 23 | - dependency-name: 'Microsoft.PowerShell.SDK' 24 | 25 | # Maintain dependencies for GitHub Actions 26 | - package-ecosystem: 'github-actions' 27 | directory: '/' 28 | schedule: 29 | interval: 'daily' 30 | labels: 31 | - 'ci-quality' 32 | reviewers: 33 | - 'microsoft/psdocs' 34 | -------------------------------------------------------------------------------- /.github/workflows/analyze.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Repository analysis 3 | # 4 | 5 | # NOTES: 6 | # This workflow uses PSRule, CodeQL, and DevSkim. 7 | # You can read more about these linting tools and configuration options here: 8 | # PSRule - https://aka.ms/ps-rule and https://github.com/Microsoft/PSRule.Rules.MSFT.OSS 9 | # CodeQL - https://codeql.github.com/docs/codeql-overview/about-codeql/ 10 | # DevSkim - https://github.com/microsoft/DevSkim-Action and https://github.com/Microsoft/DevSkim 11 | 12 | name: Analyze 13 | on: 14 | push: 15 | branches: [main, 'release/*'] 16 | pull_request: 17 | branches: [main, 'release/*'] 18 | schedule: 19 | - cron: '24 22 * * 0' # At 10:24 PM, on Sunday each week 20 | workflow_dispatch: 21 | 22 | permissions: {} 23 | 24 | jobs: 25 | oss: 26 | name: Analyze with PSRule 27 | runs-on: ubuntu-latest 28 | permissions: 29 | contents: read 30 | security-events: write 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | 35 | - name: Run PSRule analysis 36 | uses: microsoft/ps-rule@main 37 | with: 38 | modules: PSRule.Rules.MSFT.OSS 39 | prerelease: true 40 | outputFormat: Sarif 41 | outputPath: reports/ps-rule-results.sarif 42 | 43 | - name: Upload results to security tab 44 | uses: github/codeql-action/upload-sarif@v2 45 | with: 46 | sarif_file: reports/ps-rule-results.sarif 47 | 48 | devskim: 49 | name: Analyze with DevSkim 50 | runs-on: ubuntu-latest 51 | permissions: 52 | actions: read 53 | contents: read 54 | security-events: write 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v4 58 | 59 | - name: Run DevSkim scanner 60 | uses: microsoft/DevSkim-Action@v1 61 | with: 62 | directory-to-scan: . 63 | 64 | - name: Upload results to security tab 65 | uses: github/codeql-action/upload-sarif@v2 66 | with: 67 | sarif_file: devskim-results.sarif 68 | 69 | codeql: 70 | name: Analyze with CodeQL 71 | runs-on: ubuntu-latest 72 | permissions: 73 | actions: read 74 | contents: read 75 | security-events: write 76 | steps: 77 | - name: Checkout 78 | uses: actions/checkout@v4 79 | 80 | - name: Initialize CodeQL 81 | uses: github/codeql-action/init@v2 82 | with: 83 | languages: 'csharp' 84 | 85 | - name: Autobuild 86 | uses: github/codeql-action/autobuild@v2 87 | 88 | - name: Perform CodeQL Analysis 89 | uses: github/codeql-action/analyze@v2 90 | -------------------------------------------------------------------------------- /.github/workflows/first-interaction.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Stale item management 3 | # 4 | 5 | # NOTES: 6 | # This workflow greets a person for their a first issue or PR. 7 | 8 | name: First interaction 9 | 10 | on: [pull_request_target, issues] 11 | 12 | permissions: {} 13 | 14 | jobs: 15 | greeting: 16 | name: Greeting 17 | runs-on: ubuntu-latest 18 | permissions: 19 | issues: write 20 | pull-requests: write 21 | steps: 22 | - uses: actions/first-interaction@v1 23 | with: 24 | repo-token: ${{ secrets.GITHUB_TOKEN }} 25 | issue-message: 'Thanks for raising your first issue, the team appreciates the time you have taken 😉' 26 | pr-message: 'Thank you for your contribution, one of the team will evaluate shortly.' 27 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Stale item management 3 | # 4 | 5 | # NOTES: 6 | # This workflow manages stale work items on the repository. 7 | 8 | name: Stale maintenance 9 | on: 10 | schedule: 11 | - cron: '30 1 * * *' 12 | workflow_dispatch: 13 | 14 | permissions: {} 15 | 16 | jobs: 17 | issue: 18 | name: Close stale issues 19 | runs-on: ubuntu-latest 20 | if: github.repository == 'microsoft/PSDocs' 21 | permissions: 22 | issues: write 23 | steps: 24 | - uses: actions/stale@v9 25 | with: 26 | stale-issue-message: > 27 | This issue has been automatically marked as stale because it has not had 28 | recent activity. It will be closed if no further activity occurs within 7 days. 29 | Thank you for your contributions. 30 | 31 | close-issue-message: 'This issue was closed because it has not had any recent activity.' 32 | 33 | days-before-stale: 14 34 | days-before-pr-stale: -1 35 | 36 | days-before-close: 7 37 | days-before-pr-close: -1 38 | 39 | any-of-labels: 'question,duplicate,incomplete,waiting-feedback' 40 | stale-issue-label: stale 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .vs/ 3 | build/ 4 | reports/ 5 | out/ 6 | **/bin/ 7 | **/obj/ 8 | *.user 9 | *.diagsession 10 | src/**/*-help.xml 11 | src/**/*.help.txt 12 | BenchmarkDotNet.Artifacts/ 13 | PSDocs.Benchmark*.log 14 | .idea/.idea.PSDocs/.idea/.gitignore 15 | .idea/.idea.PSDocs/.idea/indexLayout.xml 16 | .idea/.idea.PSDocs/.idea/vcs.xml 17 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "header-increment": true, 4 | "first-header-h1": { 5 | "level": 1 6 | }, 7 | "header-style": { 8 | "style": "atx" 9 | }, 10 | "ul-style": { 11 | "style": "dash" 12 | }, 13 | "list-indent": true, 14 | "ul-start-left": true, 15 | "ul-indent": { 16 | "indent": 2 17 | }, 18 | "no-trailing-spaces": true, 19 | "no-hard-tabs": true, 20 | "no-reversed-links": true, 21 | "no-multiple-blanks": true, 22 | "line-length": { 23 | "line_length": 100, 24 | "code_blocks": false, 25 | "tables": false, 26 | "headers": true 27 | }, 28 | "commands-show-output": true, 29 | "no-missing-space-atx": true, 30 | "no-multiple-space-atx": true, 31 | "no-missing-space-closed-atx": true, 32 | "no-multiple-space-closed-atx": true, 33 | "blanks-around-headers": true, 34 | "header-start-left": true, 35 | "no-duplicate-header": true, 36 | "single-h1": true, 37 | "no-trailing-punctuation": { 38 | "punctuation": ".,;:!" 39 | }, 40 | "no-multiple-space-blockquote": true, 41 | "no-blanks-blockquote": true, 42 | "ol-prefix": { 43 | "style": "one_or_ordered" 44 | }, 45 | "list-marker-space": true, 46 | "blanks-around-fences": true, 47 | "blanks-around-lists": true, 48 | "no-bare-urls": true, 49 | "hr-style": { 50 | "style": "---" 51 | }, 52 | "no-emphasis-as-header": true, 53 | "no-space-in-emphasis": true, 54 | "no-space-in-code": true, 55 | "no-space-in-links": true, 56 | "fenced-code-language": false, 57 | "first-line-h1": false, 58 | "no-empty-links": true, 59 | "proper-names": { 60 | "names": [ 61 | "PowerShell", 62 | "JavaScript" 63 | ], 64 | "code_blocks": false 65 | }, 66 | "no-alt-text": true 67 | } 68 | -------------------------------------------------------------------------------- /.playps.yml: -------------------------------------------------------------------------------- 1 | 2 | markdown: 3 | # Should a line break be added after headers 4 | sectionFormat: LineBreakAfterHeader 5 | # Set the default infostring for fenced sections 6 | infoString: text 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "PowerShell", 6 | "request": "launch", 7 | "name": "PowerShell Launch Current File", 8 | "script": "${file}", 9 | "args": [], 10 | "cwd": "${file}" 11 | }, 12 | { 13 | "type": "PowerShell", 14 | "request": "launch", 15 | "name": "PowerShell Launch Current File in Temporary Console", 16 | "script": "${file}", 17 | "args": [], 18 | "cwd": "${file}", 19 | "createTemporaryIntegratedConsole": true 20 | }, 21 | { 22 | "type": "PowerShell", 23 | "request": "launch", 24 | "name": "PowerShell Launch Current File w/Args Prompt", 25 | "script": "${file}", 26 | "args": [ 27 | "${command:SpecifyScriptArgs}" 28 | ], 29 | "cwd": "${file}" 30 | }, 31 | { 32 | "type": "PowerShell", 33 | "request": "attach", 34 | "name": "PowerShell Attach to Host Process", 35 | "processId": "${command:PickPSHostProcess}", 36 | "runspaceId": 1 37 | }, 38 | { 39 | "type": "PowerShell", 40 | "request": "launch", 41 | "name": "PowerShell Interactive Session", 42 | "cwd": "${workspaceRoot}" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | ".vs/": true, 4 | "out/": true, 5 | "reports/": true, 6 | "**/bin/": true, 7 | "**/obj/": true 8 | }, 9 | "yaml.format.singleQuote": true, 10 | "yaml.schemas": { 11 | "./schemas/PSDocs-options.schema.json": [ 12 | "/tests/PSDocs.Tests/PSDocs.*.yml", 13 | "/ps-docs.yaml" 14 | ], 15 | "./schemas/PSDocs-language.schema.json": [ 16 | "/tests/PSDocs.Tests/**.Doc.yaml" 17 | ] 18 | }, 19 | "[yaml]": { 20 | "editor.tabSize": 2 21 | }, 22 | "[markdown]": { 23 | "editor.tabSize": 2 24 | }, 25 | "files.associations": { 26 | "**/.azure-pipelines/**/*.yaml": "azure-pipelines" 27 | }, 28 | "cSpell.words": [ 29 | "cmdlet", 30 | "cmdlets", 31 | "hashtable", 32 | "runspace" 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Configure GitVersion 3 | # 4 | 5 | next-version: 0.10.0 6 | branches: 7 | main: 8 | regex: ^main$ 9 | tag: 'B' 10 | increment: Minor 11 | is-mainline: true 12 | feature: 13 | regex: ^feature/ 14 | source-branches: [ 'main' ] 15 | increment: Inherit 16 | tag: B 17 | release: 18 | regex: ^release/ 19 | ignore: 20 | sha: [] 21 | merge-message-formats: {} 22 | tag-prefix: v 23 | mode: ContinuousDeployment 24 | increment: Inherit 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 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 all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | 4 | 5 | ## Security 6 | 7 | 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/). 8 | 9 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 10 | 11 | ## Reporting Security Issues 12 | 13 | **Please do not report security vulnerabilities through public GitHub issues.** 14 | 15 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 16 | 17 | 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://www.microsoft.com/en-us/msrc/pgp-key-msrc). 18 | 19 | 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://www.microsoft.com/msrc). 20 | 21 | 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: 22 | 23 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 24 | * Full paths of source file(s) related to the manifestation of the issue 25 | * The location of the affected source code (tag/branch/commit or direct URL) 26 | * Any special configuration required to reproduce the issue 27 | * Step-by-step instructions to reproduce the issue 28 | * Proof-of-concept or exploit code (if possible) 29 | * Impact of the issue, including how an attacker might exploit the issue 30 | 31 | This information will help us triage your report more quickly. 32 | 33 | 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://microsoft.com/msrc/bounty) page for more details about our active programs. 34 | 35 | ## Preferred Languages 36 | 37 | We prefer all communications to be in English. 38 | 39 | ## Policy 40 | 41 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 42 | 43 | 44 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. 6 | Please search the existing issues before filing new issues to avoid duplicates. 7 | 8 | - For new issues, file your bug or feature request as a new [issue]. 9 | - For help, discussion, and support questions about using this project, join or start a [discussion]. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this project/ product is limited to the resources listed above. 14 | 15 | [issue]: https://github.com/Microsoft/PSDocs/issues 16 | [discussion]: https://github.com/Microsoft/PSDocs/discussions 17 | -------------------------------------------------------------------------------- /ThirdPartyNotices.txt: -------------------------------------------------------------------------------- 1 | Do Not Translate or Localize 2 | 3 | This file is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Microsoft product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. 4 | 5 | --------------------------------------------- 6 | File: YamlDotNet 7 | --------------------------------------------- 8 | 9 | https://github.com/aaubry/YamlDotNet 10 | 11 | Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy of 14 | this software and associated documentation files (the "Software"), to deal in 15 | the Software without restriction, including without limitation the rights to 16 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 17 | of the Software, and to permit persons to whom the Software is furnished to do 18 | so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in all 21 | copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | 31 | --------------------------------------------- 32 | File: Newtonsoft.Json 33 | --------------------------------------------- 34 | 35 | https://github.com/JamesNK/Newtonsoft.Json 36 | 37 | The MIT License (MIT) 38 | 39 | Copyright (c) 2007 James Newton-King 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a copy of 42 | this software and associated documentation files (the "Software"), to deal in 43 | the Software without restriction, including without limitation the rights to 44 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 45 | the Software, and to permit persons to whom the Software is furnished to do so, 46 | subject to the following conditions: 47 | 48 | The above copyright notice and this permission notice shall be included in all 49 | copies or substantial portions of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 52 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 53 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 54 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 55 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 56 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 57 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Note: 5 | # This manually builds the project locally 6 | 7 | . ./scripts/pipeline-deps.ps1 8 | Invoke-Build Test 9 | 10 | Write-Host 'If no build errors occurred. The module has been saved to out/modules/PSDocs' 11 | -------------------------------------------------------------------------------- /docs/commands/.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "first-line-h1": false, 3 | "no-bare-urls": false 4 | } -------------------------------------------------------------------------------- /docs/commands/PSDocs/en-US/Get-PSDocumentHeader.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: PSDocs-help.xml 3 | Module Name: PSDocs 4 | online version: https://github.com/Microsoft/PSDocs/blob/main/docs/commands/PSDocs/en-US/Get-PSDocumentHeader.md 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-PSDocumentHeader 9 | 10 | ## SYNOPSIS 11 | 12 | Get the Yaml header from a PSDocs generated markdown file. 13 | 14 | ## SYNTAX 15 | 16 | ```text 17 | Get-PSDocumentHeader [[-Path] ] [] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Get the Yaml header from a PSDocs generated markdown file. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | 28 | ```powershell 29 | PS C:\> Get-PSDocumentHeader -Path '.\build\Default'; 30 | ``` 31 | 32 | Get the Yaml header for all markdown files in the Default directory. 33 | 34 | ### Example 2 35 | 36 | ```powershell 37 | PS C:\> Get-PSDocumentHeader -Path '.\build\Default\Server1.md'; 38 | ``` 39 | 40 | Get the Yaml header for a specific file Server1.md. 41 | 42 | ### Example 3 43 | 44 | ```powershell 45 | PS C:\> Get-PSDocumentHeader; 46 | ``` 47 | 48 | Get the Yaml header for all markdown files in the current working directory. 49 | 50 | ## PARAMETERS 51 | 52 | ### -Path 53 | 54 | The path to a specific markdown file or a parent directory containing one or more markdown files. 55 | A trailing slash is not required. 56 | 57 | If a path is not specified the current working path will be used. 58 | 59 | ```yaml 60 | Type: String 61 | Parameter Sets: (All) 62 | Aliases: FullName 63 | 64 | Required: False 65 | Position: 0 66 | Default value: $PWD 67 | Accept pipeline input: True (ByPropertyName) 68 | Accept wildcard characters: False 69 | ``` 70 | 71 | ### CommonParameters 72 | 73 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 74 | 75 | ## INPUTS 76 | 77 | ### System.String 78 | 79 | ## OUTPUTS 80 | 81 | ### System.Object 82 | 83 | ## NOTES 84 | 85 | ## RELATED LINKS 86 | -------------------------------------------------------------------------------- /docs/commands/PSDocs/en-US/PSDocs.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: PSDocs 3 | Module Guid: 1f6df554-c081-40d8-9aca-32c1abe4a1b6 4 | Download Help Link: https://github.com/Microsoft/PSDocs 5 | Help Version: 0.1.0.0 6 | Locale: en-US 7 | --- 8 | 9 | # PSDocs Module 10 | 11 | ## Description 12 | 13 | Generate markdown from PowerShell. 14 | 15 | ## PSDocs Cmdlets 16 | 17 | ### [Get-PSDocumentHeader](Get-PSDocumentHeader.md) 18 | 19 | Get the Yaml header from a PSDocs generated markdown file. 20 | 21 | ### [Invoke-PSDocument](Invoke-PSDocument.md) 22 | 23 | Create markdown from an input object. 24 | 25 | ### [Get-PSDocument](Get-PSDocument.md) 26 | 27 | Get document definitions. 28 | 29 | ### [New-PSDocumentOption](New-PSDocumentOption.md) 30 | 31 | Create options to configure document generation. 32 | -------------------------------------------------------------------------------- /docs/concepts/PSDocs/en-US/about_PSDocs_Configuration.md: -------------------------------------------------------------------------------- 1 | # PSDocs_Configuration 2 | 3 | ## about_PSDocs_Configuration 4 | 5 | ## SHORT DESCRIPTION 6 | 7 | Describes custom configuration that can be used within PSDocs document definitions. 8 | 9 | ## LONG DESCRIPTION 10 | 11 | PSDocs lets you generate dynamic markdown documents using PowerShell blocks known as document definitions. 12 | Document definitions can read custom configuration set at runtime or within options to change rendering. 13 | Within a document definition, PSDocs exposes custom configuration through the `$PSDocs` automatic variable. 14 | 15 | ### Setting configuration 16 | 17 | To specify custom configuration, set a property of the `configuration` object. 18 | Configuration can be set at runtime or as YAML by configuring `ps-docs.yaml`. 19 | 20 | For example: 21 | 22 | ```yaml 23 | # Example: ps-docs.yaml 24 | 25 | # YAML: Using the configuration YAML property to set custom configuration 'MODULE1_KEY1' 26 | configuration: 27 | MODULE1_KEY1: Value1 28 | ``` 29 | 30 | To ensure each custom key is unique use a prefix followed by an underscore that represent your module. 31 | Key names are not case sensitive, however we recommend you use uppercase for consistency. 32 | 33 | ### Reading configuration 34 | 35 | The `$PSDocs` automatic variable can be used within a Document definition to read configuration. 36 | Each custom configuration key is available under the `.Configuration` property. 37 | Additionally, several helper methods are available for advanced usage. 38 | 39 | Syntax: 40 | 41 | ```powershell 42 | $PSDocs.Configuration. 43 | ``` 44 | 45 | For example: 46 | 47 | ```powershell 48 | # Get the value of the custom configuration 'MODULE1_KEY1' 49 | $PSDocs.Configuration.MODULE1_KEY1 50 | ``` 51 | 52 | The following helper methods are available: 53 | 54 | - `GetStringValues(string configurationKey)` - The configuration value as an array of strings. 55 | This helper will always returns an array of strings. 56 | The array will be empty if the configuration key is not defined or empty. 57 | - `GetValueOrDefault(string configurationKey, object defaultValue)` - Returns the configuration value. 58 | When the configuration key is not defined the default value will be used instead. 59 | - `GetBoolOrDefault(string configurationKey, bool defaultValue)` - The configuration value as a boolean. 60 | When the configuration key is not defined the default value will be used instead. 61 | 62 | Syntax: 63 | 64 | ```powershell 65 | $PSDocs.Configuration.helper() 66 | ``` 67 | 68 | For example: 69 | 70 | ```powershell 71 | # Example using GetStringValues 72 | $values = $PSDocs.Configuration.GetStringValues('SAMPLE_AUTHORS'); 73 | 74 | # Example using GetValueOrDefault 75 | $value = $PSDocs.Configuration.GetValueOrDefault('SAMPLE_CONTENT_OWNER', 'defaultUser'); 76 | 77 | # Example using GetBoolOrDefault 78 | if ($PSDocs.Configuration.GetBoolOrDefault('SAMPLE_USE_PARAMETERS_SNIPPET', $True)) { 79 | # Execute code 80 | } 81 | ``` 82 | 83 | ## NOTE 84 | 85 | An online version of this document is available at https://github.com/Microsoft/PSDocs/blob/main/docs/concepts/PSDocs/en-US/about_PSDocs_Configuration.md. 86 | 87 | ## SEE ALSO 88 | 89 | - [Invoke-PSDocument](https://github.com/Microsoft/PSDocs/blob/main/docs/commands/PSDocs/en-US/Invoke-PSDocument.md) 90 | 91 | ## KEYWORDS 92 | 93 | - Configuration 94 | - PSDocs 95 | - GetStringValues 96 | - GetValueOrDefault 97 | - GetBoolOrDefault 98 | -------------------------------------------------------------------------------- /docs/examples/Get-child-item-output.md: -------------------------------------------------------------------------------- 1 | 2 | ## Introduction 3 | This is a sample file list from C:\\ 4 | 5 | |Name|PSIsContainer| 6 | | --- | --- | 7 | |PerfLogs|True| 8 | |Program Files|True| 9 | |Program Files (x86)|True| 10 | |Users|True| 11 | |Windows|True| -------------------------------------------------------------------------------- /docs/examples/SharePoint-config-output.md: -------------------------------------------------------------------------------- 1 | # Server1 2 | 3 | ## Installation 4 | 5 | |InstallerPath|OnlineMode| 6 | | --- | --- | 7 | |C:\\binaries\\prerequisiteinstaller.exe|True| 8 | 9 | |BinaryDir| 10 | | --- | 11 | |C:\\binaries\\| 12 | 13 | ## Farm 14 | 15 | |DatabaseServer|FarmConfigDatabaseName|AdminContentDatabaseName| 16 | | --- | --- | --- | 17 | |sql.contoso.com|SP_Config|SP_AdminContent| 18 | 19 | ## Installed services 20 | 21 | |Name|Ensure| 22 | | --- | --- | 23 | |Claims to Windows Token Service|Present| 24 | |Secure Store Service|Present| 25 | |SharePoint Server Search|Present| 26 | 27 | ## Site 28 | See the site configuration below. 29 | 30 | |Url|OwnerAlias|Name|Template| 31 | | --- | --- | --- | --- | 32 | |http://sites.contoso.com|CONTOSO\\SP_Admin|DSC Demo Site|STS#0| 33 | 34 | ### Web applications 35 | 36 | |Name|Url|Port|HostHeader|ApplicationPool|AuthenticationMethod|AllowAnonymous| 37 | | --- | --- | --- | --- | --- | --- | --- | 38 | |SharePoint Sites|http://sites.contoso.com|80|sites.contoso.com|SharePoint Sites|NTLM|False| 39 | 40 | ## Logging 41 | 42 | |LogPath|DaysToKeepLogs|LogCutInterval| 43 | | --- | --- | --- | 44 | |C:\\ULS|7|15| 45 | -------------------------------------------------------------------------------- /docs/examples/Yaml-header-output.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: An example title 3 | author: bewhite 4 | last-updated: 2018-05-17 5 | --- 6 | Yaml header may not be rendered by some markdown viewers. See source to view yaml. 7 | -------------------------------------------------------------------------------- /docs/install-instructions.md: -------------------------------------------------------------------------------- 1 | # Install instructions 2 | 3 | ## Prerequisites 4 | 5 | - Windows PowerShell 5.1 with .NET Framework 4.7.2+ or 6 | - PowerShell Core 6.2 or greater on Windows, MacOS and Linux or 7 | - PowerShell 7.0 or greater on Windows, MacOS and Linux 8 | 9 | For a list of platforms that PowerShell 7.0 is supported on [see][get-powershell]. 10 | 11 | ## Getting the module 12 | 13 | Install from [PowerShell Gallery][module] for all users (requires permissions): 14 | 15 | ```powershell 16 | # Install PSDocs module 17 | Install-Module -Name 'PSDocs' -Repository PSGallery; 18 | ``` 19 | 20 | Install from [PowerShell Gallery][module] for current user only: 21 | 22 | ```powershell 23 | # Install PSDocs module 24 | Install-Module -Name 'PSDocs' -Repository PSGallery -Scope CurrentUser; 25 | ``` 26 | 27 | Save for offline use from PowerShell Gallery: 28 | 29 | ```powershell 30 | # Save PSDocs module, in the .\modules directory 31 | Save-Module -Name 'PSDocs' -Repository PSGallery -Path '.\modules'; 32 | ``` 33 | 34 | > For pre-release versions the `-AllowPrerelease` switch must be added when calling `Install-Module` or `Save-Module`. 35 | > 36 | > To install pre-release module versions, upgrading to the latest version of _PowerShellGet_ may be required. 37 | To do this use: 38 | > 39 | > `Install-Module -Name PowerShellGet -Repository PSGallery -Scope CurrentUser -Force` 40 | 41 | ## Building from source 42 | 43 | To build this module from source run `./build.ps1`. 44 | This build script will compile the module and documentation then output the result into `out/modules/PSDocs`. 45 | 46 | The following PowerShell modules will be automatically downloaded if the required versions are not present: 47 | 48 | - PlatyPS 49 | - Pester 50 | - PSScriptAnalyzer 51 | - PowerShellGet 52 | - PackageManagement 53 | - InvokeBuild 54 | 55 | These additional modules are only required for building PSDocs and are not required for running PSDocs. 56 | 57 | If you are on a network that does not permit Internet access to the PowerShell Gallery, 58 | download these modules on an alternative device that has access. 59 | The following script can be used to download the required modules to an alternative device. 60 | After downloading the modules copy the module directories to devices with restricted Internet access. 61 | 62 | ```powershell 63 | # Save modules, in the .\modules directory 64 | Save-Module -Name PlatyPS, Pester, PSScriptAnalyzer, PowerShellGet, PackageManagement, InvokeBuild -Repository PSGallery -Path '.\modules'; 65 | ``` 66 | 67 | Additionally .NET Core SDK v3.1 is required. 68 | .NET Core will not be automatically downloaded and installed. 69 | To download and install the latest SDK see [Download .NET Core 3.1][dotnet]. 70 | 71 | ## Upgrading from previous versions 72 | 73 | If you are upgrading PSDocs from a previous version: 74 | 75 | - Please review any breaking changes in the [change log](../CHANGELOG.md). 76 | - See [upgrade notes](upgrade-notes.md) for helpful information. 77 | 78 | [module]: https://www.powershellgallery.com/packages/PSDocs 79 | [get-powershell]: https://github.com/PowerShell/PowerShell#get-powershell 80 | [dotnet]: https://dotnet.microsoft.com/download/dotnet-core/3.1 81 | -------------------------------------------------------------------------------- /docs/scenarios/arm-template/arm-template.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Azure Resource Manager documentation definitions 6 | # 7 | 8 | # A function to break out parameters from an ARM template 9 | function global:GetTemplateParameter { 10 | param ( 11 | [Parameter(Mandatory = $True)] 12 | [String]$Path 13 | ) 14 | 15 | process { 16 | $template = Get-Content $Path | ConvertFrom-Json; 17 | foreach ($property in $template.parameters.PSObject.Properties) { 18 | [PSCustomObject]@{ 19 | Name = $property.Name 20 | Description = $property.Value.metadata.description 21 | } 22 | } 23 | } 24 | } 25 | 26 | # A function to import metadata 27 | function global:GetTemplateMetadata { 28 | param ( 29 | [Parameter(Mandatory = $True)] 30 | [String]$Path 31 | ) 32 | 33 | process { 34 | $metadata = Get-Content $Path | ConvertFrom-Json; 35 | return $metadata; 36 | } 37 | } 38 | 39 | # Synopsis: A definition to generate markdown for an ARM template 40 | document 'arm-template' { 41 | 42 | # Read JSON files 43 | $metadata = GetTemplateMetadata -Path $PSScriptRoot/metadata.json; 44 | $parameters = GetTemplateParameter -Path $PSScriptRoot/template.json; 45 | 46 | # Set document title 47 | Title $metadata.itemDisplayName 48 | 49 | # Write opening line 50 | $metadata.Description 51 | 52 | # Add each parameter to a table 53 | Section 'Parameters' { 54 | $parameters | Table -Property @{ Name = 'Parameter name'; Expression = { $_.Name }},Description 55 | } 56 | 57 | # Generate example command line 58 | Section 'Use the template' { 59 | Section 'PowerShell' { 60 | 'New-AzResourceGroupDeployment -Name -ResourceGroupName -TemplateFile ' | Code powershell 61 | } 62 | 63 | Section 'Azure CLI' { 64 | 'az group deployment create --name --resource-group --template-file ' | Code text 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /docs/scenarios/arm-template/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", 3 | "type": "QuickStart", 4 | "itemDisplayName": "AKS deployment with a virtual network", 5 | "description": "This template creates a AKS cluster using a virtual network in the same resource group", 6 | "summary": "Creates and deploys a Kubernetes cluster in a virtual network.", 7 | "githubUsername": "BernieWhite", 8 | "dateUpdated": "2018-10-10" 9 | } 10 | -------------------------------------------------------------------------------- /docs/scenarios/arm-template/output.md: -------------------------------------------------------------------------------- 1 | # AKS deployment with a virtual network 2 | 3 | This template creates a AKS cluster using a virtual network in the same resource group 4 | 5 | ## Parameters 6 | 7 | Parameter name | Description 8 | -------------- | ----------- 9 | resourceName | The name of the Managed Cluster resource. 10 | environment | The environment that the resource will be deployed to. Either production or internal. 11 | location | The location of AKS resource. 12 | dnsPrefix | Optional DNS prefix to use with hosted Kubernetes API server FQDN. 13 | agentCount | The number of agent nodes for the cluster. 14 | agentVMSize | The size of the Virtual Machine. 15 | servicePrincipalClientId | Client ID (used by cloudprovider) 16 | servicePrincipalClientSecret | The Service Principal Client Secret. 17 | osType | The type of operating system. 18 | kubernetesVersion | The version of Kubernetes. 19 | enableOmsAgent | boolean flag to turn on and off of omsagent addon 20 | workspaceRegion | Specify the region for your OMS workspace 21 | workspaceName | Specify the name of the OMS workspace 22 | omsWorkspaceId | Specify the resource id of the OMS workspace 23 | omsSku | Select the SKU for your workspace 24 | enableHttpApplicationRouting | boolean flag to turn on and off of http application routing 25 | networkPlugin | Network plugin used for building Kubernetes network. 26 | maxPods | Maximum number of pods that can run on a node. 27 | vnetSubnetID | Resource ID of virtual network subnet used for nodes and/or pods IP assignment. 28 | serviceCidr | A CIDR notation IP range from which to assign service cluster IPs. 29 | dnsServiceIP | Containers DNS server IP address. 30 | dockerBridgeCidr | A CIDR notation IP for Docker bridge. 31 | 32 | ## Use the template 33 | 34 | ### PowerShell 35 | 36 | ```powershell 37 | New-AzResourceGroupDeployment -Name -ResourceGroupName -TemplateFile 38 | ``` 39 | 40 | ### Azure CLI 41 | 42 | ```text 43 | az group deployment create --name --resource-group --template-file 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/scenarios/benchmark/results-v0.7.0.md: -------------------------------------------------------------------------------- 1 | ``` ini 2 | 3 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450 (2004/?/20H1) 4 | Intel Core i7-1065G7 CPU 1.30GHz, 1 CPU, 8 logical and 4 physical cores 5 | .NET Core SDK=3.1.401 6 | [Host] : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT 7 | DefaultJob : .NET Core 3.1.7 (CoreCLR 4.700.20.36602, CoreFX 4.700.20.37001), X64 RyuJIT 8 | 9 | 10 | ``` 11 | | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | 12 | |------------------------ |---------------:|-------------:|-------------:|---------:|-------:|------:|----------:| 13 | | InvokeMarkdownProcessor | 239.9 ns | 2.33 ns | 1.94 ns | 0.1450 | - | - | 608 B | 14 | | InvokePipeline | 1,959,246.0 ns | 39,108.35 ns | 56,088.03 ns | 175.7813 | 3.9063 | - | 761570 B | 15 | -------------------------------------------------------------------------------- /docs/scenarios/directory/directory.md: -------------------------------------------------------------------------------- 1 | # Generate a document from a directory listing 2 | 3 | ```powershell 4 | # File: Sample.Doc.ps1 5 | 6 | # Define a document called Sample 7 | Document Sample { 8 | 9 | # Add an introduction section 10 | Section Introduction { 11 | # Add a comment 12 | "This is a sample file list from $TargetObject" 13 | 14 | # Generate a table 15 | Get-ChildItem -Path $TargetObject | Table -Property Name,PSIsContainer 16 | } 17 | } 18 | ``` 19 | 20 | To execute the document use `Invoke-PSDocument`. 21 | 22 | For example: 23 | 24 | ```powershell 25 | Invoke-PSDocument -InputObject 'C:\'; 26 | ``` 27 | 28 | ```powershell 29 | # Import PSDocs module 30 | Import-Module -Name PSDocs; 31 | 32 | # Call the document definition as a function to generate markdown from an object 33 | Invoke-PSDocument -InputObject 'C:\'; 34 | ``` 35 | 36 | An example of the output generated is available [here](../../examples/Get-child-item-output.md). 37 | -------------------------------------------------------------------------------- /docs/scenarios/docfx/integration-with-docfx.md: -------------------------------------------------------------------------------- 1 | # Integration with DocFX 2 | 3 | DocFX is a open source tool that converts markdown documentation into HTML. PSDocs can be used to dynamically generate markdown that can be processed by DocFX. 4 | 5 | DocFX uses a `docfx.json` to determine what content to include and how to process each file. To process markdown files a `build.content` section should be added to reference the output location where PSDocs will generate markdown relative to the location of `docfx.json`. 6 | 7 | An example `docfx.json` is provided below. 8 | 9 | ```json 10 | { 11 | "metadata": [ 12 | ], 13 | "build": { 14 | "content": [ 15 | { 16 | "files": [ 17 | "**/**.md", 18 | "**/toc.yml", 19 | "*.md", 20 | "toc.yml" 21 | ] 22 | } 23 | ], 24 | "resource": [ 25 | { 26 | "files": [ 27 | "**/media/**" 28 | ] 29 | } 30 | ], 31 | "overwrite": [ 32 | { 33 | "files": [ ], 34 | "exclude": [ 35 | "out/**", 36 | "build/**" 37 | ] 38 | } 39 | ], 40 | "dest": "_site", 41 | "globalMetadataFiles": [], 42 | "fileMetadataFiles": [], 43 | "template": [ 44 | "default" 45 | ], 46 | "postProcessors": [], 47 | "noLangKeyword": false, 48 | "keepFileLink": false, 49 | "cleanupCacheHistory": false, 50 | "disableGitFeatures": false 51 | } 52 | } 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/scenarios/dsc-mof/dsc-mof.md: -------------------------------------------------------------------------------- 1 | # Generate documentation from Desired State Configuration 2 | 3 | ```powershell 4 | # Import PSDocs.Dsc module 5 | Import-Module -Name PSDocs.Dsc; 6 | 7 | # Define a document called Sample 8 | Document 'Sample' { 9 | 10 | # Add an 'Installed features' section in the document 11 | Section 'Installed features' { 12 | # Add a comment 13 | 'The following Windows features have been installed.' 14 | 15 | # Generate a table of Windows Features 16 | $TargetObject.ResourceType.WindowsFeature | Table -Property Name,Ensure 17 | } 18 | } 19 | 20 | # Call the document definition and generate markdown for each .mof file in the .\nodes directory 21 | Invoke-DscNodeDocument -DocumentName 'Sample' -Path '.\nodes' -OutputPath '.\docs'; 22 | ``` 23 | -------------------------------------------------------------------------------- /modules.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": {}, 3 | "devDependencies": { 4 | "Pester": { 5 | "version": "5.6.1" 6 | }, 7 | "platyPS": { 8 | "version": "0.14.2" 9 | }, 10 | "PSScriptAnalyzer": { 11 | "version": "1.21.0" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ps-docs.yaml: -------------------------------------------------------------------------------- 1 | 2 | output: 3 | culture: 'en-US' 4 | -------------------------------------------------------------------------------- /ps-project.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # PSDocs 3 | # 4 | 5 | info: 6 | name: PSDocs 7 | 8 | repository: 9 | type: git 10 | url: https://github.com/Microsoft/PSDocs.git 11 | 12 | tasks: 13 | clear: 14 | steps: 15 | - gitPrune: 16 | name: origin 17 | removeGone: true 18 | -------------------------------------------------------------------------------- /ps-rule.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # PSRule configuration 3 | # 4 | 5 | # Please see the documentation for all configuration options: 6 | # https://microsoft.github.io/PSRule/ 7 | 8 | output: 9 | culture: 10 | - 'en-US' 11 | 12 | input: 13 | pathIgnore: 14 | - '*.Designer.cs' 15 | - '*.md' 16 | -------------------------------------------------------------------------------- /scripts/pipeline-deps.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Install dependencies for integration with Azure DevOps 6 | # 7 | 8 | if ($Env:SYSTEM_DEBUG -eq 'true') { 9 | $VerbosePreference = 'Continue'; 10 | } 11 | 12 | if ($Null -eq (Get-PackageProvider -Name NuGet -ErrorAction Ignore)) { 13 | Install-PackageProvider -Name NuGet -Force -Scope CurrentUser; 14 | } 15 | 16 | if ($Null -eq (Get-InstalledModule -Name PowerShellGet -MinimumVersion 2.1.2 -ErrorAction Ignore)) { 17 | Install-Module PowerShellGet -MinimumVersion 2.1.2 -Scope CurrentUser -Force -AllowClobber; 18 | } 19 | 20 | if ($Null -eq (Get-InstalledModule -Name InvokeBuild -MinimumVersion 5.4.0 -ErrorAction Ignore)) { 21 | Install-Module InvokeBuild -MinimumVersion 5.4.0 -Scope CurrentUser -Force; 22 | } 23 | -------------------------------------------------------------------------------- /src/PSDocs.Benchmark/Benchmark.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Document defintions for benchmarks 6 | # 7 | 8 | #region BlockQuote 9 | 10 | document 'BlockQuoteSingleMarkdown' { 11 | 'This is a single line' | BlockQuote 12 | } 13 | 14 | document 'BlockQuoteMultiMarkdown' { 15 | @('This is the first line.' 16 | 'This is the second line.') | BlockQuote 17 | } 18 | 19 | document 'BlockQuoteTitleMarkdown' { 20 | 'This is a single block quote' | BlockQuote -Title 'Test' 21 | } 22 | 23 | document 'BlockQuoteInfoMarkdown' { 24 | 'This is a single block quote' | BlockQuote -Info 'Tip' 25 | } 26 | 27 | #endregion BlockQuote 28 | 29 | #region Code 30 | 31 | document 'CodeMarkdown' { 32 | Code { 33 | # This is a comment 34 | This is code 35 | 36 | # Another comment 37 | And code 38 | } 39 | } 40 | 41 | document 'CodeMarkdownNamedFormat' { 42 | Code powershell { 43 | Get-Content 44 | } 45 | } 46 | 47 | document 'CodeMarkdownEval' { 48 | $a = 1; $a += 1; $a | Code powershell; 49 | } 50 | 51 | #endregion Code 52 | 53 | #region Include 54 | 55 | #endregion Include 56 | 57 | #region Metadata 58 | 59 | document 'MetadataSingleEntry' { 60 | Metadata ([ordered]@{ 61 | title = 'Test' 62 | }) 63 | } 64 | 65 | document 'MetadataMultipleEntry' { 66 | Metadata ([ordered]@{ 67 | value1 = 'ABC' 68 | value2 = 'EFG' 69 | }) 70 | } 71 | 72 | document 'MetadataMultipleBlock' { 73 | Metadata ([ordered]@{ 74 | value1 = 'ABC' 75 | }) 76 | Section 'Test' { 77 | 'A test section spliting metadata blocks.' 78 | } 79 | Metadata @{ 80 | value2 = 'EFG' 81 | } 82 | } 83 | 84 | document 'NoMetdata' { 85 | Section 'Test' { 86 | 'A test section.' 87 | } 88 | } 89 | 90 | document 'NullMetdata' { 91 | Metadata $Null 92 | Section 'Test' { 93 | 'A test section.' 94 | } 95 | } 96 | 97 | #endregion Metadata 98 | 99 | #region Note 100 | 101 | document 'NoteSingleMarkdown' { 102 | 'This is a single line' | Note 103 | } 104 | 105 | document 'NoteMultiMarkdown' { 106 | @('This is the first line.' 107 | 'This is the second line.') | Note 108 | } 109 | 110 | document 'NoteScriptBlockMarkdown' { 111 | Note { 112 | 'This is a single line' 113 | } 114 | } 115 | 116 | #endregion Note 117 | 118 | #region Section 119 | 120 | document 'SectionBlockTests' { 121 | Section 'SingleLine' { 122 | 'This is a single line markdown section.' 123 | } 124 | Section 'MultiLine' { 125 | "This is a multiline`r`ntest." 126 | } 127 | Section 'Empty' { 128 | } 129 | Section 'Forced' -Force { 130 | } 131 | } 132 | 133 | document 'SectionWhen' { 134 | Section 'Section 1' -If { $False } { 135 | 'Content 1' 136 | } 137 | Section 'Section 2' -If { $True } { 138 | 'Content 2' 139 | } 140 | # Support for When alias of If 141 | Section 'Section 3' -When { $True } { 142 | 'Content 3' 143 | } 144 | } 145 | 146 | #endregion Section 147 | 148 | #region Table 149 | 150 | #endregion Table 151 | -------------------------------------------------------------------------------- /src/PSDocs.Benchmark/PSDocs.Benchmark.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net7.0 6 | AnyCPU 7 | {a9f68ba6-1381-4e35-a619-a948d47ec752} 8 | 9 | 10 | 11 | TRACE;BENCHMARK 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/PSDocs.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Threading; 5 | using BenchmarkDotNet.Analysers; 6 | using BenchmarkDotNet.Columns; 7 | using BenchmarkDotNet.Configs; 8 | using BenchmarkDotNet.Loggers; 9 | using BenchmarkDotNet.Running; 10 | using Microsoft.Extensions.CommandLineUtils; 11 | 12 | namespace PSDocs.Benchmark 13 | { 14 | internal static class Program 15 | { 16 | private static void Main(string[] args) 17 | { 18 | var app = new CommandLineApplication 19 | { 20 | Name = "PSDocs Benchmark", 21 | Description = "A runner for testing PSDocs performance" 22 | }; 23 | 24 | #if !BENCHMARK 25 | // Do profiling 26 | DebugProfile(); 27 | #endif 28 | 29 | #if BENCHMARK 30 | RunProfile(app); 31 | app.Execute(args); 32 | #endif 33 | } 34 | 35 | private static void RunProfile(CommandLineApplication app) 36 | { 37 | var config = ManualConfig.CreateEmpty() 38 | .AddLogger(ConsoleLogger.Default) 39 | .AddColumnProvider(DefaultColumnProviders.Instance) 40 | .AddAnalyser(EnvironmentAnalyser.Default) 41 | .AddAnalyser(OutliersAnalyser.Default) 42 | .AddAnalyser(MinIterationTimeAnalyser.Default) 43 | .AddAnalyser(MultimodalDistributionAnalyzer.Default) 44 | .AddAnalyser(RuntimeErrorAnalyser.Default) 45 | .AddAnalyser(ZeroMeasurementAnalyser.Default); 46 | 47 | app.Command("benchmark", cmd => 48 | { 49 | var output = cmd.Option("-o | --output", "The path to store report output.", CommandOptionType.SingleValue); 50 | 51 | cmd.OnExecute(() => 52 | { 53 | if (output.HasValue()) 54 | { 55 | config.WithArtifactsPath(output.Value()); 56 | } 57 | 58 | // Do benchmarks 59 | BenchmarkRunner.Run(config); 60 | 61 | return 0; 62 | }); 63 | 64 | cmd.HelpOption("-? | -h | --help"); 65 | }); 66 | 67 | app.HelpOption("-? | -h | --help"); 68 | } 69 | 70 | private static void DebugProfile() 71 | { 72 | Thread.Sleep(2000); 73 | var profile = new PSDocs(); 74 | profile.Prepare(); 75 | 76 | for (var i = 0; i < 1000; i++) 77 | { 78 | profile.InvokeMarkdownProcessor(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/PSDocs/Annotations/CommentMetadata.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Diagnostics; 5 | 6 | namespace PSDocs.Annotations 7 | { 8 | /// 9 | /// Metadata properties that can be exposed by comment help. 10 | /// 11 | [DebuggerDisplay("Synopsis = {Synopsis}")] 12 | internal sealed class CommentMetadata 13 | { 14 | public string Synopsis; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/BlockQuoteCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Management.Automation; 6 | using PSDocs.Models; 7 | 8 | namespace PSDocs.Commands 9 | { 10 | internal abstract class BlockQuoteCommandBase : KeywordCmdlet 11 | { 12 | private List _Content; 13 | 14 | #region Properties 15 | 16 | [Parameter()] 17 | public string Title { get; set; } 18 | 19 | [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] 20 | public string Text { get; set; } 21 | 22 | #endregion Properties 23 | 24 | protected override void BeginProcessing() 25 | { 26 | _Content = new List(); 27 | } 28 | 29 | protected override void ProcessRecord() 30 | { 31 | _Content.Add(Text); 32 | } 33 | 34 | protected override void EndProcessing() 35 | { 36 | try 37 | { 38 | var node = ModelHelper.BlockQuote(GetInfo(), Title); 39 | node.Content = _Content.ToArray(); 40 | WriteObject(node); 41 | } 42 | finally 43 | { 44 | _Content.Clear(); 45 | } 46 | } 47 | 48 | protected abstract string GetInfo(); 49 | } 50 | 51 | [Cmdlet(VerbsCommon.Format, LanguageKeywords.BlockQuote)] 52 | internal sealed class BlockQuoteCommand : BlockQuoteCommandBase 53 | { 54 | [Parameter()] 55 | public string Info { get; set; } 56 | 57 | protected override string GetInfo() 58 | { 59 | return Info; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/DefinitionCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | using PSDocs.Runtime; 6 | 7 | namespace PSDocs.Commands 8 | { 9 | [Cmdlet(VerbsCommon.New, LanguageKeywords.Definition)] 10 | internal sealed class DefinitionCommand : PSCmdlet 11 | { 12 | private const string InvokeCmdletName = "Invoke-Block"; 13 | private const string InvokeCmdlet_IfParameter = "If"; 14 | private const string InvokeCmdlet_WithParameter = "With"; 15 | private const string InvokeCmdlet_BodyParameter = "Body"; 16 | 17 | /// 18 | /// Document block name. 19 | /// 20 | [Parameter(Mandatory = true, Position = 0)] 21 | [ValidateNotNullOrEmpty()] 22 | public string Name { get; set; } 23 | 24 | /// 25 | /// Document block body. 26 | /// 27 | [Parameter(Mandatory = true, Position = 1)] 28 | public ScriptBlock Body { get; set; } 29 | 30 | /// 31 | /// Document block tags. 32 | /// 33 | [Parameter(Mandatory = false)] 34 | public string[] Tag { get; set; } 35 | 36 | /// 37 | /// An optional script precondition before the Document is evaluated. 38 | /// 39 | [Parameter(Mandatory = false)] 40 | public ScriptBlock If { get; set; } 41 | 42 | /// 43 | /// An optional selector precondition before the Document is evaluated. 44 | /// 45 | [Parameter(Mandatory = false)] 46 | public string[] With { get; set; } 47 | 48 | protected override void ProcessRecord() 49 | { 50 | var context = RunspaceContext.CurrentThread; 51 | var source = context.Source; 52 | var extent = new ResourceExtent( 53 | file: source.File.Path, 54 | startLineNumber: Body.Ast.Extent.StartLineNumber 55 | ); 56 | 57 | // Create PS instance for execution 58 | var ps = context.NewPowerShell(); 59 | ps.AddCommand(new CmdletInfo(InvokeCmdletName, typeof(InvokeDocumentCommand))); 60 | ps.AddParameter(InvokeCmdlet_IfParameter, If); 61 | ps.AddParameter(InvokeCmdlet_WithParameter, With); 62 | ps.AddParameter(InvokeCmdlet_BodyParameter, Body); 63 | 64 | var block = new ScriptDocumentBlock( 65 | source: source.File, 66 | name: Name, 67 | body: ps, 68 | tag: Tag, 69 | extent: extent 70 | ); 71 | WriteObject(block); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/ExportConventionCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | using PSDocs.Definitions.Conventions; 6 | using PSDocs.Runtime; 7 | 8 | namespace PSDocs.Commands 9 | { 10 | [Cmdlet(VerbsLifecycle.Register, LanguageKeywords.Convention)] 11 | internal sealed class ExportConventionCommand : PSCmdlet 12 | { 13 | private const string InvokeCmdletName = "Invoke-PSDocumentConvention"; 14 | private const string InvokeCmdlet_BodyParameter = "Body"; 15 | 16 | /// 17 | /// Convention name. 18 | /// 19 | [Parameter(Mandatory = false, Position = 0)] 20 | [ValidateNotNullOrEmpty()] 21 | public string Name { get; set; } 22 | 23 | /// 24 | /// Begin block. 25 | /// 26 | [Parameter(Mandatory = false)] 27 | public ScriptBlock Begin { get; set; } 28 | 29 | /// 30 | /// Process block. 31 | /// 32 | [Parameter(Mandatory = false, Position = 1)] 33 | public ScriptBlock Process { get; set; } 34 | 35 | /// 36 | /// End block. 37 | /// 38 | [Parameter(Mandatory = false)] 39 | public ScriptBlock End { get; set; } 40 | 41 | protected override void ProcessRecord() 42 | { 43 | var context = RunspaceContext.CurrentThread; 44 | var source = context.Source; 45 | 46 | // Return convention 47 | var result = new ScriptBlockDocumentConvention( 48 | source: source.File, 49 | name: Name, 50 | begin: ConventionBlock(context, Begin), 51 | process: ConventionBlock(context, Process), 52 | end: ConventionBlock(context, End) 53 | ); 54 | WriteObject(result); 55 | } 56 | 57 | private static LanguageScriptBlock ConventionBlock(RunspaceContext context, ScriptBlock block) 58 | { 59 | if (block == null) 60 | return null; 61 | 62 | // Create PS instance for execution 63 | var ps = context.NewPowerShell(); 64 | ps.AddCommand(new CmdletInfo(InvokeCmdletName, typeof(InvokeConventionCommand))); 65 | ps.AddParameter(InvokeCmdlet_BodyParameter, block); 66 | return new LanguageScriptBlock(ps); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/IncludeCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.IO; 6 | using System.Management.Automation; 7 | using PSDocs.Models; 8 | using PSDocs.Resources; 9 | using PSDocs.Runtime; 10 | 11 | namespace PSDocs.Commands 12 | { 13 | [Cmdlet(VerbsCommon.Add, LanguageKeywords.Include)] 14 | [OutputType(typeof(Include))] 15 | internal sealed class IncludeCommand : KeywordCmdlet 16 | { 17 | [Parameter(Position = 0, Mandatory = true)] 18 | public string FileName { get; set; } 19 | 20 | [Parameter(Mandatory = false)] 21 | public string BaseDirectory { get; set; } 22 | 23 | [Parameter(Mandatory = false)] 24 | public string Culture { get; set; } 25 | 26 | [Parameter(Mandatory = false)] 27 | public SwitchParameter UseCulture { get; set; } 28 | 29 | [Parameter(Mandatory = false)] 30 | public IDictionary Replace { get; set; } 31 | 32 | protected override void BeginProcessing() 33 | { 34 | if (string.IsNullOrEmpty(Culture)) 35 | Culture = RunspaceContext.CurrentThread.Culture; 36 | } 37 | 38 | protected override void EndProcessing() 39 | { 40 | var result = ModelHelper.Include(BaseDirectory, Culture, FileName, UseCulture, Replace); 41 | if (result == null || !result.Exists) 42 | { 43 | WriteError(new ErrorRecord( 44 | exception: new FileNotFoundException(PSDocsResources.IncludeNotFound, result?.Path), 45 | errorId: "PSDocs.Runtime.IncludeNotFound", 46 | errorCategory: ErrorCategory.ObjectNotFound, 47 | targetObject: result?.Path 48 | )); 49 | return; 50 | } 51 | WriteObject(result); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/InvokeConventionCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Commands 7 | { 8 | [Cmdlet(VerbsLifecycle.Invoke, LanguageKeywords.Convention)] 9 | internal sealed class InvokeConventionCommand : PSCmdlet 10 | { 11 | [Parameter()] 12 | public string[] Type; 13 | 14 | [Parameter()] 15 | public ScriptBlock If; 16 | 17 | [Parameter()] 18 | public ScriptBlock Body; 19 | 20 | [Parameter(ValueFromPipeline = true)] 21 | public PSObject InputObject; 22 | 23 | protected override void ProcessRecord() 24 | { 25 | try 26 | { 27 | if (Body == null) 28 | return; 29 | 30 | WriteObject(Body.Invoke(InputObject), true); 31 | } 32 | finally 33 | { 34 | 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/InvokeDocumentCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.ObjectModel; 5 | using System.Management.Automation; 6 | using PSDocs.Runtime; 7 | 8 | namespace PSDocs.Commands 9 | { 10 | [Cmdlet(VerbsLifecycle.Invoke, LanguageKeywords.Block)] 11 | internal sealed class InvokeDocumentCommand : PSCmdlet 12 | { 13 | [Parameter()] 14 | public string[] With; 15 | 16 | [Parameter()] 17 | public ScriptBlock If; 18 | 19 | [Parameter()] 20 | public ScriptBlock Body; 21 | 22 | protected override void ProcessRecord() 23 | { 24 | try 25 | { 26 | if (Body == null) 27 | return; 28 | 29 | // Evalute selector pre-condition 30 | if (!AcceptsWith()) 31 | return; 32 | 33 | // Evaluate script pre-condition 34 | if (!AcceptsIf()) 35 | return; 36 | 37 | WriteObject(Body.Invoke(), true); 38 | } 39 | finally 40 | { 41 | 42 | } 43 | } 44 | 45 | private bool AcceptsWith() 46 | { 47 | if (With == null || With.Length == 0) 48 | return true; 49 | 50 | for (var i = 0; i < With.Length; i++) 51 | { 52 | if (RunspaceContext.CurrentThread.TrySelector(With[i])) 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | private bool AcceptsIf() 59 | { 60 | if (If == null) 61 | return true; 62 | 63 | try 64 | { 65 | RunspaceContext.CurrentThread.PushScope(RunspaceScope.Condition); 66 | return GetResult(If.Invoke()); 67 | } 68 | finally 69 | { 70 | RunspaceContext.CurrentThread.PopScope(); 71 | } 72 | } 73 | 74 | private static bool GetResult(Collection result) 75 | { 76 | if (result == null || result.Count == 0) 77 | return false; 78 | 79 | for (var i = 0; i < result.Count; i++) 80 | { 81 | if (result[i] == null || result[i].BaseObject == null) 82 | return false; 83 | 84 | if (result[i].BaseObject is bool bResult && !bResult) 85 | return false; 86 | } 87 | return true; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/ListCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Commands 7 | { 8 | [Cmdlet(VerbsCommon.Format, LanguageKeywords.List)] 9 | internal sealed class ListCommand : KeywordCmdlet 10 | { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/MetadataCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Management.Automation; 6 | 7 | namespace PSDocs.Commands 8 | { 9 | [Cmdlet(VerbsCommon.Set, LanguageKeywords.Metadata)] 10 | internal sealed class MetadataCommand : KeywordCmdlet 11 | { 12 | [Parameter(Position = 0)] 13 | public IDictionary Body { get; set; } 14 | 15 | protected override void BeginProcessing() 16 | { 17 | if (Body == null || Body.Count == 0) 18 | return; 19 | 20 | GetBuilder().Metadata(Body); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/NoteCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Commands 7 | { 8 | [Cmdlet(VerbsCommon.Format, LanguageKeywords.Note)] 9 | internal sealed class NoteCommand : BlockQuoteCommandBase 10 | { 11 | private const string InfoString = "NOTE"; 12 | 13 | protected override string GetInfo() 14 | { 15 | return InfoString; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/SectionCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Commands 7 | { 8 | [Cmdlet(VerbsCommon.New, LanguageKeywords.Section)] 9 | internal sealed class SectionCommand : KeywordCmdlet 10 | { 11 | [Parameter(Position = 0, Mandatory = true)] 12 | public string Name { get; set; } 13 | 14 | [Parameter(Position = 1, Mandatory = true)] 15 | public ScriptBlock Body { get; set; } 16 | 17 | [Parameter(Mandatory = false)] 18 | public ScriptBlock If { get; set; } 19 | 20 | [Parameter(Mandatory = false)] 21 | public SwitchParameter Force { get; set; } 22 | 23 | [Parameter(Mandatory = false, ValueFromPipeline = true)] 24 | public PSObject InputObject { get; set; } 25 | 26 | protected override void ProcessRecord() 27 | { 28 | if (!TryCondition()) 29 | return; 30 | 31 | var builder = GetBuilder(); 32 | var section = builder.EnterSection(Name); 33 | var shouldWrite = true; 34 | 35 | try 36 | { 37 | shouldWrite = section.AddNodes(Body.Invoke()) || ShouldForce(); 38 | } 39 | finally 40 | { 41 | builder.ExitSection(); 42 | } 43 | if (shouldWrite) 44 | WriteObject(section); 45 | } 46 | 47 | private bool ShouldForce() 48 | { 49 | return Force.ToBool() || !GetPipeline().Option.Markdown.SkipEmptySections.Value; 50 | } 51 | 52 | private bool TryCondition() 53 | { 54 | return If == null || True(If.InvokeReturnAsIs()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/TitleCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | using PSDocs.Pipeline; 6 | 7 | namespace PSDocs.Commands 8 | { 9 | [Cmdlet(VerbsCommon.Set, LanguageKeywords.Title)] 10 | internal sealed class TitleCommand : KeywordCmdlet 11 | { 12 | [Parameter(Position = 0, Mandatory = true)] 13 | [AllowNull, AllowEmptyString] 14 | public string Text { get; set; } 15 | 16 | protected override void BeginProcessing() 17 | { 18 | if (string.IsNullOrEmpty(Text)) 19 | { 20 | GetPipeline().Writer.WarnTitleEmpty(); 21 | return; 22 | } 23 | GetBuilder().Title(Text); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/PSDocs/Commands/WarningCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Commands 7 | { 8 | [Cmdlet(VerbsCommon.Format, LanguageKeywords.Warning)] 9 | internal sealed class WarningCommand : BlockQuoteCommandBase 10 | { 11 | private const string InfoString = "WARNING"; 12 | 13 | protected override string GetInfo() 14 | { 15 | return InfoString; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/PSDocs/Common/EnvironmentHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Net; 7 | using System.Security; 8 | 9 | namespace PSDocs 10 | { 11 | internal sealed class EnvironmentHelper 12 | { 13 | private static readonly char[] STRINGARRAY_SEPARATOR = new char[] { ';' }; 14 | 15 | public static readonly EnvironmentHelper Default = new(); 16 | 17 | internal bool TryString(string key, out string value) 18 | { 19 | return TryVariable(key, out value) && !string.IsNullOrEmpty(value); 20 | } 21 | 22 | internal bool TrySecureString(string key, out SecureString value) 23 | { 24 | value = null; 25 | if (!TryString(key, out var variable)) 26 | return false; 27 | 28 | value = new NetworkCredential("na", variable).SecurePassword; 29 | return true; 30 | } 31 | 32 | internal bool TryInt(string key, out int value) 33 | { 34 | value = default; 35 | return TryVariable(key, out var variable) && int.TryParse(variable, out value); 36 | } 37 | 38 | internal bool TryBool(string key, out bool value) 39 | { 40 | value = default; 41 | return TryVariable(key, out var variable) && TryParseBool(variable, out value); 42 | } 43 | 44 | internal bool TryEnum(string key, out TEnum value) where TEnum : struct 45 | { 46 | value = default; 47 | if (!TryVariable(key, out var variable)) 48 | return false; 49 | 50 | return Enum.TryParse(variable, ignoreCase: true, out value); 51 | } 52 | 53 | internal bool TryStringArray(string key, out string[] value) 54 | { 55 | value = default; 56 | if (!TryVariable(key, out var variable)) 57 | return false; 58 | 59 | value = variable.Split(STRINGARRAY_SEPARATOR, options: StringSplitOptions.RemoveEmptyEntries); 60 | return value != null; 61 | } 62 | 63 | private bool TryVariable(string key, out string variable) 64 | { 65 | variable = Environment.GetEnvironmentVariable(key); 66 | return variable != null; 67 | } 68 | 69 | private static bool TryParseBool(string variable, out bool value) 70 | { 71 | if (bool.TryParse(variable, out value)) 72 | return true; 73 | 74 | if (int.TryParse(variable, out var ivalue)) 75 | { 76 | value = ivalue > 0; 77 | return true; 78 | } 79 | return false; 80 | } 81 | 82 | internal IEnumerable> WithPrefix(string prefix) 83 | { 84 | var env = Environment.GetEnvironmentVariables(); 85 | var enumerator = env.GetEnumerator(); 86 | while (enumerator.MoveNext()) 87 | { 88 | var key = enumerator.Key.ToString(); 89 | if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) 90 | { 91 | yield return new KeyValuePair(key, enumerator.Value); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/PSDocs/Common/HashtableExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | 9 | namespace PSDocs 10 | { 11 | internal static class HashtableExtensions 12 | { 13 | [DebuggerStepThrough] 14 | public static void AddUnique(this Hashtable hashtable, Hashtable values) 15 | { 16 | if (values == null) 17 | return; 18 | 19 | foreach (var key in values.Keys) 20 | if (!hashtable.ContainsKey(key)) 21 | hashtable.Add(key, values[key]); 22 | } 23 | 24 | /// 25 | /// Build index to allow mapping. 26 | /// 27 | public static Dictionary BuildIndex(this Hashtable hashtable, bool caseSensitive = false) 28 | { 29 | var comparer = caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; 30 | var index = new Dictionary(comparer); 31 | foreach (DictionaryEntry entry in hashtable) 32 | index.Add(entry.Key.ToString(), entry.Value); 33 | 34 | return index; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PSDocs/Common/IBindingContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs 5 | { 6 | internal interface IBindingContext 7 | { 8 | bool GetNameToken(string expression, out NameToken nameToken); 9 | 10 | void CacheNameToken(string expression, NameToken nameToken); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Common/NameToken.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Diagnostics; 5 | 6 | namespace PSDocs 7 | { 8 | /// 9 | /// The type of NameToken. 10 | /// 11 | internal enum NameTokenType 12 | { 13 | /// 14 | /// The token represents a field/ property of an object. 15 | /// 16 | Field = 0, 17 | 18 | /// 19 | /// The token is an index in an object. 20 | /// 21 | Index = 1, 22 | 23 | /// 24 | /// The token is a reference to the parent object. Can only be the first token. 25 | /// 26 | Self = 2 27 | } 28 | 29 | /// 30 | /// A token for expressing a path through a tree of fields. 31 | /// 32 | [DebuggerDisplay("{Type}, Name = {Name}, Index = {Index}")] 33 | internal sealed class NameToken 34 | { 35 | /// 36 | /// The name of the field if the token type if Field. 37 | /// 38 | public string Name; 39 | 40 | /// 41 | /// The index if the token type if Index. 42 | /// 43 | public int Index; 44 | 45 | /// 46 | /// The next token. 47 | /// 48 | public NameToken Next; 49 | 50 | /// 51 | /// The type of the token. 52 | /// 53 | public NameTokenType Type = NameTokenType.Field; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/PSDocs/Common/PSObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs 7 | { 8 | internal static class PSObjectExtensions 9 | { 10 | public static bool HasProperty(this PSObject o, string propertyName) 11 | { 12 | return o.Properties[propertyName] != null; 13 | } 14 | 15 | /// 16 | /// Determines if the PSObject has any note properties. 17 | /// 18 | public static bool HasNoteProperty(this PSObject o) 19 | { 20 | foreach (var property in o.Properties) 21 | { 22 | if (property.MemberType == PSMemberTypes.NoteProperty) 23 | return true; 24 | } 25 | return false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PSDocs/Common/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | 6 | namespace PSDocs 7 | { 8 | internal static class StringExtensions 9 | { 10 | public static bool IsUri(this string s) 11 | { 12 | return s.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || s.StartsWith("https://", StringComparison.OrdinalIgnoreCase); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/ColumnPadding.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Configuration 5 | { 6 | public enum ColumnPadding 7 | { 8 | None, 9 | 10 | Single, 11 | 12 | MatchHeader, 13 | 14 | Undefined 15 | } 16 | } -------------------------------------------------------------------------------- /src/PSDocs/Configuration/ConfigurationOption.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace PSDocs.Configuration 8 | { 9 | /// 10 | /// A set of key/ value configuration options for document definitions. 11 | /// 12 | public sealed class ConfigurationOption : KeyMapDictionary 13 | { 14 | private const string ENVIRONMENT_PREFIX = "PSDOCS_CONFIGURATION_"; 15 | private const string DICTIONARY_PREFIX = "Configuration."; 16 | 17 | public ConfigurationOption() 18 | : base() { } 19 | 20 | public ConfigurationOption(ConfigurationOption option) 21 | : base(option) { } 22 | 23 | private ConfigurationOption(Hashtable hashtable) 24 | : base(hashtable) { } 25 | 26 | public static implicit operator ConfigurationOption(Hashtable hashtable) 27 | { 28 | return new ConfigurationOption(hashtable); 29 | } 30 | 31 | internal static ConfigurationOption Combine(ConfigurationOption o1, ConfigurationOption o2) 32 | { 33 | var result = new ConfigurationOption(o1); 34 | result.AddUnique(o2); 35 | return result; 36 | } 37 | 38 | internal void Load(EnvironmentHelper env) 39 | { 40 | base.Load(ENVIRONMENT_PREFIX, env); 41 | } 42 | 43 | internal void Load(IDictionary dictionary) 44 | { 45 | base.Load(DICTIONARY_PREFIX, dictionary); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/DocumentOption.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace PSDocs.Configuration 8 | { 9 | public sealed class DocumentOption : IEquatable 10 | { 11 | internal static readonly DocumentOption Default = new() 12 | { 13 | Include = null, 14 | Tag = null, 15 | }; 16 | 17 | public DocumentOption() 18 | { 19 | Include = null; 20 | Tag = null; 21 | } 22 | 23 | internal DocumentOption(DocumentOption option) 24 | { 25 | Include = option.Include; 26 | Tag = option.Tag; 27 | } 28 | 29 | public override bool Equals(object obj) 30 | { 31 | return obj is DocumentOption option && Equals(option); 32 | } 33 | 34 | public bool Equals(DocumentOption other) 35 | { 36 | return other != null && 37 | Include == other.Include && 38 | Tag == other.Tag; 39 | } 40 | 41 | public override int GetHashCode() 42 | { 43 | unchecked // Overflow is fine 44 | { 45 | var hash = 17; 46 | hash = hash * 23 + (Include != null ? Include.GetHashCode() : 0); 47 | hash = hash * 23 + (Tag != null ? Tag.GetHashCode() : 0); 48 | return hash; 49 | } 50 | } 51 | 52 | internal static DocumentOption Combine(DocumentOption o1, DocumentOption o2) 53 | { 54 | return new DocumentOption(o1) 55 | { 56 | Include = o1.Include ?? o2.Include, 57 | Tag = o1.Tag ?? o2.Tag 58 | }; 59 | } 60 | 61 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Exposed for serialization.")] 62 | public string[] Include { get; set; } 63 | 64 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Exposed for serialization.")] 65 | public string[] Tag { get; set; } 66 | 67 | internal void Load(EnvironmentHelper env) 68 | { 69 | if (env.TryStringArray("PSDOCS_DOCUMENT_INCLUDE", out var include)) 70 | Include = include; 71 | 72 | if (env.TryStringArray("PSDOCS_DOCUMENT_TAG", out var tag)) 73 | Tag = tag; 74 | } 75 | 76 | internal void Load(Dictionary index) 77 | { 78 | if (index.TryPopStringArray("Document.Include", out var include)) 79 | Include = include; 80 | 81 | if (index.TryPopStringArray("Document.Tag", out var tag)) 82 | Tag = tag; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/EdgePipeOption.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Configuration 5 | { 6 | public enum EdgePipeOption 7 | { 8 | WhenRequired, 9 | 10 | Always 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/ExecutionOption.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | 8 | namespace PSDocs.Configuration 9 | { 10 | /// 11 | /// Options that affect document execution. 12 | /// 13 | public sealed class ExecutionOption : IEquatable 14 | { 15 | private const LanguageMode DEFAULT_LANGUAGEMODE = Configuration.LanguageMode.FullLanguage; 16 | 17 | internal static readonly ExecutionOption Default = new() 18 | { 19 | LanguageMode = DEFAULT_LANGUAGEMODE, 20 | }; 21 | 22 | public ExecutionOption() 23 | { 24 | LanguageMode = null; 25 | } 26 | 27 | internal ExecutionOption(ExecutionOption option) 28 | { 29 | LanguageMode = option.LanguageMode; 30 | } 31 | 32 | public override bool Equals(object obj) 33 | { 34 | return obj is ExecutionOption option && Equals(option); 35 | } 36 | 37 | public bool Equals(ExecutionOption other) 38 | { 39 | return other != null && 40 | LanguageMode == other.LanguageMode; 41 | } 42 | 43 | public override int GetHashCode() 44 | { 45 | unchecked // Overflow is fine 46 | { 47 | var hash = 17; 48 | hash = hash * 23 + (LanguageMode.HasValue ? LanguageMode.Value.GetHashCode() : 0); 49 | return hash; 50 | } 51 | } 52 | 53 | internal static ExecutionOption Combine(ExecutionOption o1, ExecutionOption o2) 54 | { 55 | return new ExecutionOption(o1) 56 | { 57 | LanguageMode = o1.LanguageMode ?? o2.LanguageMode 58 | }; 59 | } 60 | 61 | /// 62 | /// The PowerShell language mode to use for document execution. 63 | /// 64 | /// 65 | /// The default is FullLanguage. 66 | /// 67 | [DefaultValue(null)] 68 | public LanguageMode? LanguageMode { get; set; } 69 | 70 | internal void Load(EnvironmentHelper env) 71 | { 72 | if (env.TryEnum("PSDOCS_EXECUTION_LANGUAGEMODE", out LanguageMode languageMode)) 73 | LanguageMode = languageMode; 74 | } 75 | 76 | internal void Load(Dictionary index) 77 | { 78 | if (index.TryPopEnum("Execution.LanguageMode", out LanguageMode languageMode)) 79 | LanguageMode = languageMode; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/FieldMap.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Dynamic; 8 | 9 | namespace PSDocs.Configuration 10 | { 11 | /// 12 | /// A mapping of fields to property names. 13 | /// 14 | public sealed class FieldMap : DynamicObject, IEnumerable> 15 | { 16 | private readonly Dictionary _Map; 17 | 18 | public FieldMap() 19 | { 20 | _Map = new Dictionary(StringComparer.OrdinalIgnoreCase); 21 | } 22 | 23 | internal FieldMap(FieldMap map) 24 | { 25 | _Map = new Dictionary(map._Map, StringComparer.OrdinalIgnoreCase); 26 | } 27 | 28 | internal FieldMap(Dictionary map) 29 | { 30 | _Map = new Dictionary(map, StringComparer.OrdinalIgnoreCase); 31 | } 32 | 33 | public int Count => _Map.Count; 34 | 35 | public bool TryField(string fieldName, out string[] fields) 36 | { 37 | return _Map.TryGetValue(fieldName, out fields); 38 | } 39 | 40 | internal void Set(string fieldName, string[] fields) 41 | { 42 | _Map[fieldName] = fields; 43 | } 44 | 45 | internal static void Load(FieldMap map, Dictionary properties) 46 | { 47 | foreach (var property in properties) 48 | { 49 | if (property.Value is string value && !string.IsNullOrEmpty(value)) 50 | map.Set(property.Key, new string[] { value }); 51 | else if (property.Value is string[] array && array.Length > 0) 52 | map.Set(property.Key, array); 53 | } 54 | } 55 | 56 | public IEnumerator> GetEnumerator() 57 | { 58 | return ((IEnumerable>)_Map).GetEnumerator(); 59 | } 60 | 61 | IEnumerator IEnumerable.GetEnumerator() 62 | { 63 | return GetEnumerator(); 64 | } 65 | 66 | public override bool TryGetMember(GetMemberBinder binder, out object result) 67 | { 68 | var found = TryField(binder.Name, out var value); 69 | result = value; 70 | return found; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/InputFormat.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Converters; 6 | 7 | namespace PSDocs.Configuration 8 | { 9 | /// 10 | /// The formats to convert input from. 11 | /// 12 | [JsonConverter(typeof(StringEnumConverter))] 13 | public enum InputFormat 14 | { 15 | None = 0, 16 | 17 | Yaml = 1, 18 | 19 | Json = 2, 20 | 21 | PowerShellData = 3, 22 | 23 | Detect = 255 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/LanguageMode.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Configuration 5 | { 6 | public enum LanguageMode 7 | { 8 | FullLanguage = 0, 9 | 10 | ConstrainedLanguage = 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/MarkdownEncoding.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Configuration 5 | { 6 | public enum MarkdownEncoding 7 | { 8 | Default = 0, 9 | 10 | UTF8, 11 | 12 | UTF7, 13 | 14 | Unicode, 15 | 16 | UTF32, 17 | 18 | ASCII 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/PSDocs/Configuration/OutputOption.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | 8 | namespace PSDocs.Configuration 9 | { 10 | /// 11 | /// Options that affect how output is generated. 12 | /// 13 | public sealed class OutputOption : IEquatable 14 | { 15 | internal static readonly OutputOption Default = new() 16 | { 17 | Culture = null, 18 | Path = null, 19 | }; 20 | 21 | public OutputOption() 22 | { 23 | Culture = null; 24 | Path = null; 25 | } 26 | 27 | internal OutputOption(OutputOption option) 28 | { 29 | Culture = option.Culture; 30 | Path = option.Path; 31 | } 32 | 33 | public override bool Equals(object obj) 34 | { 35 | return obj is OutputOption option && Equals(option); 36 | } 37 | 38 | public bool Equals(OutputOption other) 39 | { 40 | return other != null && 41 | Culture == other.Culture && 42 | Path == other.Path; 43 | } 44 | 45 | public override int GetHashCode() 46 | { 47 | unchecked // Overflow is fine 48 | { 49 | var hash = 17; 50 | hash = hash * 23 + (Culture != null ? Culture.GetHashCode() : 0); 51 | hash = hash * 23 + (Path != null ? Path.GetHashCode() : 0); 52 | return hash; 53 | } 54 | } 55 | 56 | internal static OutputOption Combine(OutputOption o1, OutputOption o2) 57 | { 58 | return new OutputOption(o1) 59 | { 60 | Culture = o1.Culture ?? o2.Culture, 61 | Path = o1.Path ?? o2.Path 62 | }; 63 | } 64 | 65 | [DefaultValue(null)] 66 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Exposed for serialization.")] 67 | public string[] Culture { get; set; } 68 | 69 | /// 70 | /// The file path location to save results. 71 | /// 72 | [DefaultValue(null)] 73 | public string Path { get; set; } 74 | 75 | internal void Load(EnvironmentHelper env) 76 | { 77 | if (env.TryStringArray("PSDOCS_OUTPUT_CULTURE", out var culture)) 78 | Culture = culture; 79 | 80 | if (env.TryString("PSDOCS_OUTPUT_PATH", out var path)) 81 | Path = path; 82 | } 83 | 84 | internal void Load(Dictionary index) 85 | { 86 | if (index.TryPopStringArray("Output.Culture", out var culture)) 87 | Culture = culture; 88 | 89 | if (index.TryPopString("Output.Path", out var path)) 90 | Path = path; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/PSDocs/Data/CodeContent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Data 7 | { 8 | /// 9 | /// A convertable object for code content. 10 | /// 11 | public sealed class CodeContent 12 | { 13 | public string Content { get; } 14 | 15 | public CodeContentType Type { get; } 16 | 17 | private CodeContent(string content, CodeContentType type) 18 | { 19 | Content = content; 20 | Type = type; 21 | } 22 | 23 | private CodeContent(object value) 24 | { 25 | Type = CodeContentType.Object; 26 | } 27 | 28 | public static implicit operator CodeContent(ScriptBlock content) 29 | { 30 | return FromScriptBlock(content); 31 | } 32 | 33 | public static implicit operator CodeContent(string content) 34 | { 35 | return FromString(content); 36 | } 37 | 38 | public static implicit operator CodeContent(PSObject content) 39 | { 40 | if (content == null) 41 | return null; 42 | 43 | if (content.BaseObject is ScriptBlock sb) 44 | return FromScriptBlock(sb); 45 | 46 | if (content.BaseObject is string s) 47 | return FromString(s); 48 | 49 | return FromObject(content); 50 | } 51 | 52 | public static CodeContent FromScriptBlock(ScriptBlock content) 53 | { 54 | if (content == null) 55 | return null; 56 | 57 | return new CodeContent(content.ToString(), CodeContentType.ScriptBlock); 58 | } 59 | 60 | public static CodeContent FromString(string content) 61 | { 62 | return new CodeContent(content, CodeContentType.String); 63 | } 64 | 65 | public static CodeContent FromObject(object content) 66 | { 67 | return new CodeContent(content); 68 | } 69 | 70 | public enum CodeContentType 71 | { 72 | String, 73 | 74 | ScriptBlock, 75 | 76 | Object 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/PSDocs/Data/IDocumentBuilder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Management.Automation; 6 | using PSDocs.Models; 7 | using PSDocs.Processor; 8 | using PSDocs.Runtime; 9 | 10 | namespace PSDocs.Data 11 | { 12 | internal interface IDocumentBuilder : IDisposable 13 | { 14 | string Name { get; } 15 | 16 | Document Process(RunspaceContext context, PSObject sourceObject); 17 | 18 | void End(RunspaceContext context, IDocumentResult[] completed); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/PSDocs/Data/ITargetInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Data 5 | { 6 | internal interface ITargetInfo 7 | { 8 | string TargetName { get; } 9 | 10 | string TargetType { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Data/InputFileInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.IO; 5 | using PSDocs.Configuration; 6 | 7 | namespace PSDocs.Data 8 | { 9 | public sealed class InputFileInfo : ITargetInfo 10 | { 11 | private readonly string _TargetType; 12 | 13 | internal readonly bool IsUrl; 14 | 15 | internal InputFileInfo(string basePath, string path) 16 | { 17 | if (path.IsUri()) 18 | { 19 | FullName = path; 20 | IsUrl = true; 21 | return; 22 | } 23 | path = PSDocumentOption.GetRootedPath(path); 24 | FullName = path; 25 | BasePath = basePath; 26 | Name = System.IO.Path.GetFileName(path); 27 | Extension = System.IO.Path.GetExtension(path); 28 | DirectoryName = System.IO.Path.GetDirectoryName(path); 29 | Path = ExpressionHelpers.NormalizePath(basePath, FullName); 30 | _TargetType = string.IsNullOrEmpty(Extension) ? System.IO.Path.GetFileNameWithoutExtension(path) : Extension; 31 | } 32 | 33 | public string FullName { get; } 34 | 35 | public string BasePath { get; } 36 | 37 | public string Name { get; } 38 | 39 | public string Extension { get; } 40 | 41 | public string DirectoryName { get; } 42 | 43 | public string Path { get; } 44 | 45 | string ITargetInfo.TargetName => Path; 46 | 47 | string ITargetInfo.TargetType => _TargetType; 48 | 49 | /// 50 | /// Convert to string. 51 | /// 52 | public override string ToString() 53 | { 54 | return FullName; 55 | } 56 | 57 | /// 58 | /// Convert to FileInfo. 59 | /// 60 | public FileInfo AsFileInfo() 61 | { 62 | return new FileInfo(FullName); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Conventions/BaseDocumentConvention.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using PSDocs.Runtime; 6 | 7 | namespace PSDocs.Definitions.Conventions 8 | { 9 | internal abstract class BaseDocumentConvention : IDocumentConvention 10 | { 11 | protected BaseDocumentConvention(string name) 12 | { 13 | Name = name; 14 | } 15 | 16 | public string Name { get; } 17 | 18 | public virtual void Begin(RunspaceContext context, IEnumerable input) 19 | { 20 | 21 | } 22 | 23 | public virtual void Process(RunspaceContext context, IEnumerable input) 24 | { 25 | 26 | } 27 | 28 | public virtual void End(RunspaceContext context, IEnumerable input) 29 | { 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Conventions/DefaultDocumentConvention.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.IO; 6 | using PSDocs.Configuration; 7 | using PSDocs.Runtime; 8 | 9 | namespace PSDocs.Definitions.Conventions 10 | { 11 | internal sealed class DefaultDocumentConvention : BaseDocumentConvention 12 | { 13 | internal DefaultDocumentConvention(string name) 14 | : base(name) { } 15 | 16 | public override void Process(RunspaceContext context, IEnumerable input) 17 | { 18 | var culture = context.Builder.Document.Culture; 19 | var rootedPath = PSDocumentOption.GetRootedPath(context.Pipeline.Option.Output.Path); 20 | var outputPath = !string.IsNullOrEmpty(culture) && context.Pipeline.Option.Output?.Culture?.Length > 1 ? 21 | Path.Combine(rootedPath, culture) : rootedPath; 22 | 23 | context.DocumentContext.InstanceName = context.Builder.Document.Name; 24 | context.DocumentContext.OutputPath = outputPath; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Conventions/ScriptBlockDocumentConvention.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using PSDocs.Pipeline; 6 | using PSDocs.Runtime; 7 | 8 | namespace PSDocs.Definitions.Conventions 9 | { 10 | internal sealed class ScriptBlockDocumentConvention : BaseDocumentConvention, ILanguageBlock 11 | { 12 | private readonly LanguageScriptBlock _Begin; 13 | private readonly LanguageScriptBlock _Process; 14 | private readonly LanguageScriptBlock _End; 15 | 16 | public ScriptBlockDocumentConvention(SourceFile source, string name, LanguageScriptBlock begin, LanguageScriptBlock process, LanguageScriptBlock end) 17 | : base(name) 18 | { 19 | Source = source; 20 | Id = ResourceHelper.GetId(source.ModuleName, name); 21 | _Begin = begin; 22 | _Process = process; 23 | _End = end; 24 | } 25 | 26 | public string Id { get; } 27 | 28 | public SourceFile Source { get; } 29 | 30 | string ILanguageBlock.Module => Source.ModuleName; 31 | 32 | string ILanguageBlock.SourcePath => Source.Path; 33 | 34 | public override void Begin(RunspaceContext context, IEnumerable input) 35 | { 36 | InvokeConventionBlock(_Begin, input); 37 | } 38 | 39 | public override void Process(RunspaceContext context, IEnumerable input) 40 | { 41 | InvokeConventionBlock(_Process, input); 42 | } 43 | 44 | public override void End(RunspaceContext context, IEnumerable input) 45 | { 46 | InvokeConventionBlock(_End, input); 47 | } 48 | 49 | private void InvokeConventionBlock(LanguageScriptBlock block, IEnumerable input) 50 | { 51 | if (block == null) 52 | return; 53 | 54 | try 55 | { 56 | block.Invoke(); 57 | } 58 | finally 59 | { 60 | 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/IAnnotated.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Definitions 5 | { 6 | internal interface IAnnotated 7 | { 8 | TAnnotation GetAnnotation() where TAnnotation : T; 9 | 10 | void SetAnnotation(TAnnotation annotation) where TAnnotation : T; 11 | } 12 | 13 | internal static class AnnotatedExtensions 14 | { 15 | internal static TAnnotation RequireAnnotation(this IAnnotated annotated) where TAnnotation : T, new() 16 | { 17 | var result = annotated.GetAnnotation(); 18 | return result == null ? new TAnnotation() : result; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/IDocumentConvention.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using PSDocs.Runtime; 6 | 7 | namespace PSDocs.Definitions 8 | { 9 | internal interface IDocumentConvention 10 | { 11 | string Name { get; } 12 | 13 | void Begin(RunspaceContext context, IEnumerable input); 14 | 15 | void Process(RunspaceContext context, IEnumerable input); 16 | 17 | void End(RunspaceContext context, IEnumerable input); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/IDocumentDefinition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Runtime; 5 | 6 | namespace PSDocs.Definitions 7 | { 8 | public interface IDocumentDefinition : ILanguageBlock 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Selectors/Selector.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using PSDocs.Pipeline; 7 | 8 | namespace PSDocs.Definitions.Selectors 9 | { 10 | [Spec(Specs.V1, Specs.Selector)] 11 | internal sealed class SelectorV1 : InternalResource 12 | { 13 | public SelectorV1(string apiVersion, SourceFile source, ResourceMetadata metadata, ResourceHelpInfo info, SelectorV1Spec spec) 14 | : base(ResourceKind.Selector, apiVersion, source, metadata, info, spec) { } 15 | } 16 | 17 | internal sealed class SelectorV1Spec : Spec 18 | { 19 | public SelectorIf If { get; set; } 20 | } 21 | 22 | internal abstract class SelectorExpression 23 | { 24 | public SelectorExpression(SelectorExpresssionDescriptor descriptor) 25 | { 26 | Descriptor = descriptor; 27 | } 28 | 29 | internal sealed class PropertyBag : KeyMapDictionary 30 | { 31 | public PropertyBag() 32 | : base() { } 33 | } 34 | 35 | public SelectorExpresssionDescriptor Descriptor { get; } 36 | } 37 | 38 | [DebuggerDisplay("Selector If")] 39 | internal sealed class SelectorIf : SelectorExpression 40 | { 41 | public SelectorIf(SelectorExpression expression) 42 | : base(null) 43 | { 44 | Expression = expression; 45 | } 46 | 47 | public SelectorExpression Expression { get; set; } 48 | } 49 | 50 | [DebuggerDisplay("Selector {Descriptor.Name}")] 51 | internal sealed class SelectorOperator : SelectorExpression 52 | { 53 | internal SelectorOperator(SelectorExpresssionDescriptor descriptor) 54 | : base(descriptor) 55 | { 56 | Children = new List(); 57 | } 58 | 59 | public List Children { get; } 60 | 61 | public void Add(SelectorExpression item) 62 | { 63 | Children.Add(item); 64 | } 65 | } 66 | 67 | [DebuggerDisplay("Selector {Descriptor.Name}")] 68 | internal sealed class SelectorCondition : SelectorExpression 69 | { 70 | internal SelectorCondition(SelectorExpresssionDescriptor descriptor, PropertyBag properties) 71 | : base(descriptor) 72 | { 73 | Property = properties ?? new PropertyBag(); 74 | } 75 | 76 | public PropertyBag Property { get; } 77 | 78 | internal void Add(PropertyBag properties) 79 | { 80 | Property.AddUnique(properties); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Selectors/SelectorContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using PSDocs.Runtime; 6 | 7 | namespace PSDocs.Definitions.Selectors 8 | { 9 | internal class SelectorContext : IBindingContext 10 | { 11 | private readonly Dictionary _NameTokenCache; 12 | 13 | internal SelectorContext() 14 | { 15 | _NameTokenCache = new Dictionary(); 16 | } 17 | 18 | void IBindingContext.CacheNameToken(string expression, NameToken nameToken) 19 | { 20 | _NameTokenCache[expression] = nameToken; 21 | } 22 | 23 | bool IBindingContext.GetNameToken(string expression, out NameToken nameToken) 24 | { 25 | return _NameTokenCache.TryGetValue(expression, out nameToken); 26 | } 27 | 28 | internal void Debug(string message, params object[] args) 29 | { 30 | if (RunspaceContext.CurrentThread?.Pipeline?.Writer == null) 31 | return; 32 | 33 | RunspaceContext.CurrentThread.Pipeline.Writer.WriteDebug(message, args); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Selectors/SelectorVisitor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using PSDocs.Resources; 7 | 8 | namespace PSDocs.Definitions.Selectors 9 | { 10 | [DebuggerDisplay("Id: {Id}")] 11 | internal sealed class SelectorVisitor 12 | { 13 | private readonly SelectorExpressionOuterFn _Fn; 14 | 15 | public SelectorVisitor(string id, SelectorIf expression) 16 | { 17 | Id = id; 18 | InstanceId = Guid.NewGuid(); 19 | var builder = new SelectorExpressionBuilder(); 20 | _Fn = builder.Build(expression); 21 | } 22 | 23 | public Guid InstanceId { get; } 24 | 25 | public string Id { get; } 26 | 27 | public bool Match(object o) 28 | { 29 | var context = new SelectorContext(); 30 | context.Debug(PSDocsResources.SelectorMatchTrace, Id); 31 | return _Fn(context, o); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/Spec.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Definitions.Selectors; 5 | 6 | namespace PSDocs.Definitions 7 | { 8 | public abstract class Spec 9 | { 10 | private const string FullNameSeparator = "/"; 11 | 12 | public static string GetFullName(string apiVersion, string name) 13 | { 14 | return string.Concat(apiVersion, FullNameSeparator, name); 15 | } 16 | } 17 | 18 | internal static class Specs 19 | { 20 | internal const string V1 = "github.com/microsoft/PSDocs/v1"; 21 | internal const string Selector = "Selector"; 22 | 23 | public static readonly ISpecDescriptor[] BuiltinTypes = new ISpecDescriptor[] 24 | { 25 | new SpecDescriptor(V1, Selector), 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PSDocs/Definitions/SpecFactory.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using PSDocs.Annotations; 7 | using PSDocs.Pipeline; 8 | 9 | namespace PSDocs.Definitions 10 | { 11 | internal sealed class SpecFactory 12 | { 13 | private readonly Dictionary _Descriptors; 14 | 15 | public SpecFactory() 16 | { 17 | _Descriptors = new Dictionary(); 18 | foreach (var d in Specs.BuiltinTypes) 19 | With(d); 20 | } 21 | 22 | public bool TryDescriptor(string apiVersion, string name, out ISpecDescriptor descriptor) 23 | { 24 | var fullName = Spec.GetFullName(apiVersion, name); 25 | return _Descriptors.TryGetValue(fullName, out descriptor); 26 | } 27 | 28 | public void With(string name, string apiVersion) where T : Resource, IResource where TSpec : Spec, new() 29 | { 30 | var descriptor = new SpecDescriptor(name, apiVersion); 31 | _Descriptors.Add(descriptor.FullName, descriptor); 32 | } 33 | 34 | private void With(ISpecDescriptor descriptor) 35 | { 36 | _Descriptors.Add(descriptor.FullName, descriptor); 37 | } 38 | } 39 | 40 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] 41 | internal sealed class SpecAttribute : Attribute 42 | { 43 | public SpecAttribute() 44 | { 45 | 46 | } 47 | 48 | public SpecAttribute(string apiVersion, string kind) 49 | { 50 | ApiVersion = apiVersion; 51 | Kind = kind; 52 | } 53 | 54 | public string ApiVersion { get; } 55 | 56 | public string Kind { get; } 57 | } 58 | 59 | internal sealed class SpecDescriptor : ISpecDescriptor where T : Resource, IResource where TSpec : Spec, new() 60 | { 61 | public SpecDescriptor(string apiVersion, string name) 62 | { 63 | ApiVersion = apiVersion; 64 | Name = name; 65 | FullName = Spec.GetFullName(apiVersion, name); 66 | } 67 | 68 | public string Name { get; } 69 | 70 | public string ApiVersion { get; } 71 | 72 | public string FullName { get; } 73 | 74 | public Type SpecType => typeof(TSpec); 75 | 76 | public IResource CreateInstance(SourceFile source, ResourceMetadata metadata, CommentMetadata comment, object spec) 77 | { 78 | var info = new ResourceHelpInfo(comment.Synopsis); 79 | return (IResource)Activator.CreateInstance(typeof(T), ApiVersion, source, metadata, info, spec); 80 | } 81 | } 82 | 83 | internal interface ISpecDescriptor 84 | { 85 | string Name { get; } 86 | 87 | string ApiVersion { get; } 88 | 89 | string FullName { get; } 90 | 91 | Type SpecType { get; } 92 | 93 | IResource CreateInstance(SourceFile source, ResourceMetadata metadata, CommentMetadata comment, object spec); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Alignment.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public enum Alignment 7 | { 8 | Center, 9 | 10 | Left, 11 | 12 | Right, 13 | 14 | Undefined 15 | } 16 | } -------------------------------------------------------------------------------- /src/PSDocs/Models/BlockQuote.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public sealed class BlockQuote : DocumentNode 7 | { 8 | public override DocumentNodeType Type => DocumentNodeType.BlockQuote; 9 | 10 | public string Info { get; set; } 11 | 12 | public string Title { get; set; } 13 | 14 | public string[] Content { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/PSDocs/Models/Code.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public sealed class Code : DocumentNode 7 | { 8 | public override DocumentNodeType Type => DocumentNodeType.Code; 9 | 10 | public string Content { get; set; } 11 | 12 | public string Info { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Document.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Collections.Specialized; 6 | using PSDocs.Runtime; 7 | 8 | namespace PSDocs.Models 9 | { 10 | public sealed class Document : SectionNode 11 | { 12 | internal readonly DocumentContext Context; 13 | 14 | internal Document(DocumentContext context) 15 | { 16 | Context = context; 17 | } 18 | 19 | public string Name => Context?.InstanceName; 20 | 21 | public string Culture => Context?.Culture; 22 | 23 | public override DocumentNodeType Type => DocumentNodeType.Document; 24 | 25 | public OrderedDictionary Metadata => Context?.Metadata; 26 | 27 | public Hashtable Data => Context?.Data; 28 | 29 | public string Path => Context?.OutputPath; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/PSDocs/Models/DocumentFilter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace PSDocs.Models 8 | { 9 | public sealed class DocumentFilter 10 | { 11 | private readonly HashSet _AcceptedNames; 12 | private readonly HashSet _RequiredTags; 13 | 14 | private DocumentFilter(string[] name, string[] tag) 15 | { 16 | _AcceptedNames = new HashSet(name, StringComparer.OrdinalIgnoreCase); 17 | _RequiredTags = new HashSet(tag, StringComparer.OrdinalIgnoreCase); 18 | } 19 | 20 | public static DocumentFilter Create(string[] name, string[] tag) 21 | { 22 | return new DocumentFilter( 23 | name: name ?? Array.Empty(), 24 | tag: tag ?? Array.Empty() 25 | ); 26 | } 27 | 28 | public bool Match(string name, string[] tag) 29 | { 30 | // If name is filtered, the name must be listed 31 | if (_AcceptedNames.Count > 0 && !_AcceptedNames.Contains(name)) 32 | { 33 | return false; 34 | } 35 | 36 | // Check if no tags are required 37 | if (_RequiredTags.Count == 0) 38 | { 39 | return true; 40 | } 41 | 42 | // Check for impossible match 43 | if (tag == null || _RequiredTags.Count > tag.Length) 44 | { 45 | return false; 46 | } 47 | 48 | // Check each tag 49 | foreach (var t in tag) 50 | { 51 | if (!_RequiredTags.Contains(t)) 52 | { 53 | return false; 54 | } 55 | } 56 | 57 | return true; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/PSDocs/Models/DocumentNode.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public abstract class DocumentNode 7 | { 8 | public abstract DocumentNodeType Type { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/PSDocs/Models/DocumentNodeType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public enum DocumentNodeType 7 | { 8 | Document, 9 | 10 | Section, 11 | 12 | Table, 13 | 14 | Code, 15 | 16 | BlockQuote, 17 | 18 | Text, 19 | 20 | Include 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Include.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.IO; 5 | 6 | namespace PSDocs.Models 7 | { 8 | public sealed class Include : DocumentNode 9 | { 10 | private string _Path; 11 | 12 | public override DocumentNodeType Type => DocumentNodeType.Include; 13 | 14 | public string Path 15 | { 16 | get => _Path; 17 | set 18 | { 19 | _Path = value; 20 | Exists = File.Exists(_Path); 21 | } 22 | } 23 | 24 | public string Content { get; set; } 25 | 26 | internal bool Exists { get; private set; } 27 | 28 | public override string ToString() 29 | { 30 | return Content; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/PSDocs/Models/ModelHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.IO; 6 | using PSDocs.Configuration; 7 | 8 | namespace PSDocs.Models 9 | { 10 | internal static class ModelHelper 11 | { 12 | public static Document NewDocument() 13 | { 14 | return new Document(null); 15 | } 16 | 17 | public static Section NewSection(string name, int level) 18 | { 19 | return new Section 20 | { 21 | Title = name, 22 | Level = level 23 | }; 24 | } 25 | 26 | public static TableBuilder Table() 27 | { 28 | return new TableBuilder(); 29 | } 30 | 31 | public static Code NewCode() 32 | { 33 | return new Code 34 | { 35 | 36 | }; 37 | } 38 | 39 | public static BlockQuote BlockQuote(string info, string title) 40 | { 41 | return new BlockQuote 42 | { 43 | Info = info, 44 | Title = title 45 | }; 46 | } 47 | 48 | public static Text Text(string value) 49 | { 50 | return new Text 51 | { 52 | Content = value 53 | }; 54 | } 55 | 56 | public static Include Include(string baseDirectory, string culture, string fileName, bool useCulture) 57 | { 58 | return Include(baseDirectory, culture, fileName, useCulture, null); 59 | } 60 | 61 | internal static Include Include(string baseDirectory, string culture, string fileName, bool useCulture, IDictionary replace) 62 | { 63 | baseDirectory = PSDocumentOption.GetRootedPath(baseDirectory); 64 | var absolutePath = Path.IsPathRooted(fileName) ? fileName : Path.Combine(baseDirectory, (useCulture ? culture : string.Empty), fileName); 65 | var result = new Include 66 | { 67 | Path = absolutePath 68 | }; 69 | if (result.Exists) 70 | { 71 | var text = File.ReadAllText(absolutePath); 72 | if (replace != null && replace.Count > 0) 73 | { 74 | foreach (var key in replace.Keys) 75 | { 76 | var k = key?.ToString(); 77 | var v = replace[key]?.ToString(); 78 | text = text.Replace(k, v); 79 | } 80 | } 81 | result.Content = text; 82 | } 83 | return result; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/PSDocs/Models/PSDocsContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.IO; 5 | using PSDocs.Configuration; 6 | 7 | namespace PSDocs.Models 8 | { 9 | public delegate object WriteDocumentDelegate(PSDocumentOption option, Document document); 10 | 11 | public sealed class PSDocsContext 12 | { 13 | public PSDocumentOption Option { get; private set; } 14 | 15 | public DocumentFilter Filter { get; private set; } 16 | 17 | public string OutputPath { get; set; } 18 | 19 | public string[] InstanceName { get; set; } 20 | 21 | public string[] Culture { get; set; } 22 | 23 | public WriteDocumentDelegate WriteDocumentHook { get; set; } 24 | 25 | public static PSDocsContext Create(PSDocumentOption option, string[] name, string[] tag, string outputPath) 26 | { 27 | var actualPath = outputPath; 28 | 29 | if (!Path.IsPathRooted(actualPath)) 30 | { 31 | actualPath = Path.Combine(PSDocumentOption.GetWorkingPath(), outputPath); 32 | } 33 | 34 | return new PSDocsContext 35 | { 36 | Option = option, 37 | Filter = DocumentFilter.Create(name, tag), 38 | OutputPath = actualPath 39 | }; 40 | } 41 | 42 | public object WriteDocument(Document document) 43 | { 44 | return WriteDocumentHook?.Invoke(Option, document); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/PSDocs/Models/PSDocsHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace PSDocs.Models 8 | { 9 | public static class PSDocsHelper 10 | { 11 | /// 12 | /// Check if the defined tags match the expected tags. 13 | /// 14 | /// The tags of the document definition. 15 | /// The tags to match. 16 | /// Returns true when all the matching tags are found on the document definition. 17 | public static bool MatchTags(string[] definitionTags, string[] match) 18 | { 19 | if (match == null || match.Length == 0) 20 | { 21 | return true; 22 | } 23 | 24 | if (definitionTags == null || definitionTags.Length < match.Length) 25 | { 26 | return false; 27 | } 28 | 29 | var tags = new HashSet(definitionTags, StringComparer.InvariantCultureIgnoreCase); 30 | 31 | foreach (var m in match) 32 | { 33 | if (!tags.Contains(m)) 34 | { 35 | return false; 36 | } 37 | } 38 | 39 | return true; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Section.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public sealed class Section : SectionNode 7 | { 8 | public override DocumentNodeType Type => DocumentNodeType.Section; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/PSDocs/Models/SectionNode.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Management.Automation; 7 | 8 | namespace PSDocs.Models 9 | { 10 | public abstract class SectionNode : DocumentNode 11 | { 12 | protected SectionNode() 13 | { 14 | Title = string.Empty; 15 | Node = new List(); 16 | Level = 1; 17 | } 18 | 19 | public string Title { get; set; } 20 | 21 | public int Level { get; set; } 22 | 23 | public List Node { get; set; } 24 | 25 | internal bool AddNodes(Collection collection) 26 | { 27 | if (collection == null || collection.Count == 0) 28 | return false; 29 | 30 | var items = new PSObject[collection.Count]; 31 | collection.CopyTo(items, 0); 32 | 33 | var count = 0; 34 | for (var i = 0; i < items.Length; i++) 35 | { 36 | if (items[i] == null || items[i].BaseObject == null) 37 | continue; 38 | 39 | if (items[i].BaseObject is not DocumentNode node) 40 | node = new Text { Content = items[i].BaseObject.ToString() }; 41 | 42 | count++; 43 | Node.Add(node); 44 | } 45 | return count > 0; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Table.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace PSDocs.Models 7 | { 8 | public sealed class Table : DocumentNode 9 | { 10 | public Table() 11 | { 12 | Headers = new List(); 13 | Rows = new List(); 14 | } 15 | 16 | public override DocumentNodeType Type => DocumentNodeType.Table; 17 | 18 | public List Rows { get; set; } 19 | 20 | public List Headers { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/PSDocs/Models/TableBuilder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using PSDocs.Pipeline; 8 | using PSDocs.Resources; 9 | 10 | namespace PSDocs.Models 11 | { 12 | public sealed class TableBuilder 13 | { 14 | private const string FIELD_NAME = "name"; 15 | private const string FIELD_LABEL = "label"; 16 | private const string FIELD_WIDTH = "width"; 17 | private const string FIELD_ALIGNMENT = "alignment"; 18 | 19 | private readonly List _Headers; 20 | 21 | public TableBuilder() 22 | { 23 | _Headers = new List(); 24 | } 25 | 26 | public Table Build() 27 | { 28 | return new Table 29 | { 30 | Headers = _Headers 31 | }; 32 | } 33 | 34 | public void Header(string label) 35 | { 36 | _Headers.Add(new TableColumnHeader 37 | { 38 | Label = label 39 | }); 40 | } 41 | 42 | public void Header(Hashtable hashtable) 43 | { 44 | var header = new TableColumnHeader(); 45 | 46 | // Build index to allow mapping 47 | var index = GetIndex(hashtable); 48 | 49 | // Start loading matching values 50 | if (index.TryGetValue(FIELD_NAME, out var value)) 51 | header.Label = (string)value; 52 | 53 | if (index.TryGetValue(FIELD_LABEL, out value)) 54 | header.Label = (string)value; 55 | 56 | if (index.TryGetValue(FIELD_WIDTH, out value)) 57 | header.Width = (int)value; 58 | 59 | if (index.TryGetValue(FIELD_ALIGNMENT, out value)) 60 | header.Alignment = (Alignment)Enum.Parse(typeof(Alignment), (string)value, true); 61 | 62 | // Validate header 63 | if (string.IsNullOrEmpty(header.Label)) 64 | throw new RuntimeException(PSDocsResources.LabelNullOrEmpty); 65 | 66 | _Headers.Add(header); 67 | } 68 | 69 | public IDictionary GetIndex(Hashtable hashtable) 70 | { 71 | // Build index to allow mapping 72 | var index = new Dictionary(StringComparer.OrdinalIgnoreCase); 73 | foreach (DictionaryEntry entry in hashtable) 74 | index.Add(entry.Key.ToString(), entry.Value); 75 | 76 | return index; 77 | } 78 | 79 | public IDictionary GetPropertyFilter(Hashtable hashtable) 80 | { 81 | var index = GetIndex(hashtable); 82 | if (index.ContainsKey(FIELD_ALIGNMENT)) 83 | index.Remove(FIELD_ALIGNMENT); 84 | 85 | if (index.ContainsKey(FIELD_WIDTH)) 86 | index.Remove(FIELD_WIDTH); 87 | 88 | if (index.ContainsKey(FIELD_NAME)) 89 | { 90 | index[FIELD_LABEL] = index[FIELD_NAME]; 91 | index.Remove(FIELD_NAME); 92 | } 93 | return index; 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/PSDocs/Models/TableColumnHeader.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public sealed class TableColumnHeader 7 | { 8 | public TableColumnHeader() 9 | { 10 | Alignment = Alignment.Undefined; 11 | } 12 | 13 | public Alignment Alignment { get; set; } 14 | 15 | public string Label { get; set; } 16 | 17 | public int Width { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/PSDocs/Models/Text.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | public sealed class Text : DocumentNode 7 | { 8 | public override DocumentNodeType Type => DocumentNodeType.Text; 9 | 10 | public string Content { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/PSDocs/PSDocs.Format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | PSDocs.Runtime.ScriptDocumentBlock 7 | 8 | PSDocs.Runtime.ScriptDocumentBlock 9 | 10 | 11 | 12 | 13 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | Name 26 | 27 | 28 | Module 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/PSDocs/PSDocs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 11.0 6 | Library 7 | {1f6df554-c081-40d8-9aca-32c1abe4a1b6} 8 | portable 9 | en-US 10 | true 11 | true 12 | 13 | Bernie White 14 | PSDocs 15 | https://github.com/microsoft/PSDocs 16 | https://github.com/microsoft/PSDocs/blob/main/LICENSE 17 | 0.0.1 18 | Copyright (c) Microsoft Corporation. Licensed under the MIT License. 19 | Generate markdown from objects using PowerShell syntax. 20 | 21 | This project uses GitHub Issues to track bugs and feature requests. See GitHub project for more information. 22 | 23 | 24 | 25 | AllEnabledByDefault 26 | true 27 | 28 | 29 | 30 | Windows 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | all 40 | runtime; build; native; contentfiles; analyzers; buildtransitive 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | True 50 | True 51 | PSDocsResources.resx 52 | 53 | 54 | True 55 | True 56 | ViewStrings.resx 57 | 58 | 59 | 60 | 61 | 62 | ResXFileCodeGenerator 63 | PSDocsResources.Designer.cs 64 | 65 | 66 | PublicResXFileCodeGenerator 67 | ViewStrings.Designer.cs 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/GetPipeline.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Runtime; 5 | 6 | namespace PSDocs.Pipeline 7 | { 8 | public interface IGetPipelineBuilder : IPipelineBuilder 9 | { 10 | 11 | } 12 | 13 | /// 14 | /// A helper to construct a get pipeline. 15 | /// 16 | internal sealed class GetPipelineBuilder : PipelineBuilderBase, IGetPipelineBuilder 17 | { 18 | internal GetPipelineBuilder(Source[] source, HostContext hostContext) 19 | : base(source, hostContext) { } 20 | 21 | public override IPipeline Build() 22 | { 23 | if (RequireSources()) 24 | return null; 25 | 26 | return new GetPipeline(PrepareContext(), Source); 27 | } 28 | } 29 | 30 | internal sealed class GetPipeline : PipelineBase, IPipeline 31 | { 32 | private readonly RunspaceContext _Runspace; 33 | 34 | internal GetPipeline(PipelineContext context, Source[] source) 35 | : base(context, source) 36 | { 37 | _Runspace = new RunspaceContext(Context); 38 | HostHelper.ImportResource(Source, _Runspace); 39 | } 40 | 41 | public override void End() 42 | { 43 | Context.Writer.WriteObject(HostHelper.GetDocumentBlock(_Runspace, Source), true); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/InputPathBuilder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Pipeline 5 | { 6 | internal sealed class InputPathBuilder : PathBuilder 7 | { 8 | public InputPathBuilder(IPipelineWriter logger, string basePath, string searchPattern, PathFilter filter) 9 | : base(logger, basePath, searchPattern, filter) { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/InstanceNameBinder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace PSDocs.Pipeline 7 | { 8 | /// 9 | /// Handles binding of instance names to objects. 10 | /// 11 | internal sealed class InstanceNameBinder 12 | { 13 | private readonly string[] _InstanceName; 14 | 15 | internal InstanceNameBinder(string[] instanceName) 16 | { 17 | _InstanceName = instanceName; 18 | } 19 | 20 | internal IEnumerable GetInstanceName(string defaultInstanceName) 21 | { 22 | if (_InstanceName == null || _InstanceName.Length == 0) 23 | yield return defaultInstanceName; 24 | 25 | for (var i = 0; _InstanceName != null && i < _InstanceName.Length; i++) 26 | yield return _InstanceName[i]; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/OptionContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections.Generic; 5 | using PSDocs.Configuration; 6 | 7 | namespace PSDocs.Pipeline 8 | { 9 | internal sealed class OptionContext : IPSDocumentOption 10 | { 11 | private readonly IPSDocumentOption _Workspace; 12 | private readonly Dictionary _ModuleScope; 13 | 14 | private IPSDocumentOption _Option; 15 | 16 | public OptionContext(IPSDocumentOption option) 17 | { 18 | _Workspace = option; 19 | _ModuleScope = new Dictionary(); 20 | _Option = _Workspace; 21 | } 22 | 23 | #region IPSDocumentOption 24 | 25 | public ConfigurationOption Configuration => _Option.Configuration; 26 | 27 | public DocumentOption Document => _Option.Document; 28 | 29 | public ExecutionOption Execution => _Option.Execution; 30 | 31 | public InputOption Input => _Option.Input; 32 | 33 | public MarkdownOption Markdown => _Option.Markdown; 34 | 35 | public OutputOption Output => _Option.Output; 36 | 37 | #endregion IPSDocumentOption 38 | 39 | internal enum ScopeType 40 | { 41 | Parameter = 0, 42 | 43 | Explicit = 1, 44 | 45 | Workspace = 2, 46 | 47 | Module = 3 48 | } 49 | 50 | internal void WithScope(IPSDocumentOption option, ScopeType type, string moduleName = null) 51 | { 52 | if (type == ScopeType.Module && !_ModuleScope.ContainsKey(moduleName)) 53 | { 54 | _ModuleScope[moduleName] = option; 55 | } 56 | } 57 | 58 | internal void SwitchScope(string module) 59 | { 60 | _Option = !string.IsNullOrEmpty(module) && _ModuleScope.TryGetValue(module, out var moduleOption) ? moduleOption : _Workspace; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/Output/HostPipelineWriter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Pipeline.Output 7 | { 8 | internal sealed class HostPipelineWriter : PipelineWriterBase, IPipelineWriter 9 | { 10 | private readonly HostContext _HostContext; 11 | 12 | // Track whether Dispose has been called. 13 | private bool _Disposed; 14 | 15 | internal HostPipelineWriter(HostContext hostContext) 16 | : base(hostContext) 17 | { 18 | _HostContext = hostContext; 19 | } 20 | 21 | protected override void DoWriteError(ErrorRecord record) 22 | { 23 | if (GuardOutputReady()) 24 | return; 25 | 26 | _HostContext.CommandRuntime.WriteError(record); 27 | } 28 | 29 | protected override void DoWriteWarning(string text) 30 | { 31 | if (GuardOutputReady()) 32 | return; 33 | 34 | _HostContext.CommandRuntime.WriteWarning(text); 35 | } 36 | 37 | protected override void DoWriteInformation(InformationRecord record) 38 | { 39 | if (GuardOutputReady() || record == null) 40 | return; 41 | 42 | _HostContext.CommandRuntime.WriteInformation(record); 43 | } 44 | 45 | protected override void DoWriteVerbose(string text) 46 | { 47 | if (GuardOutputReady()) 48 | return; 49 | 50 | _HostContext.CommandRuntime.WriteVerbose(text); 51 | } 52 | 53 | protected override void DoWriteDebug(DebugRecord record) 54 | { 55 | if (GuardOutputReady()) 56 | return; 57 | 58 | _HostContext.CommandRuntime.WriteDebug(record.Message); 59 | } 60 | 61 | protected override void DoWriteObject(object sendToPipeline, bool enumerateCollection) 62 | { 63 | if (GuardOutputReady()) 64 | return; 65 | 66 | _HostContext.CommandRuntime.WriteObject(sendToPipeline, enumerateCollection); 67 | } 68 | 69 | private bool GuardOutputReady() 70 | { 71 | return _HostContext == null || _HostContext.CommandRuntime == null; 72 | } 73 | 74 | #region IDisposable 75 | 76 | private void Dispose(bool disposing) 77 | { 78 | if (!_Disposed) 79 | { 80 | if (disposing) 81 | { 82 | // Do nothing 83 | } 84 | _Disposed = true; 85 | } 86 | } 87 | 88 | public void Dispose() 89 | { 90 | Dispose(disposing: true); 91 | System.GC.SuppressFinalize(this); 92 | } 93 | 94 | #endregion IDisposable 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/PipelineContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using PSDocs.Configuration; 7 | using PSDocs.Definitions; 8 | using PSDocs.Definitions.Selectors; 9 | using PSDocs.Models; 10 | using PSDocs.Processor; 11 | 12 | namespace PSDocs.Pipeline 13 | { 14 | /// 15 | /// A context for an end-to-end pipeline execution. 16 | /// 17 | internal sealed class PipelineContext : IDisposable 18 | { 19 | internal readonly OptionContext Option; 20 | internal readonly LanguageMode LanguageMode; 21 | internal readonly DocumentFilter Filter; 22 | internal readonly PipelineStream Stream; 23 | internal readonly IPipelineWriter Writer; 24 | internal readonly InstanceNameBinder InstanceNameBinder; 25 | internal readonly string[] Convention; 26 | internal readonly Dictionary Selector; 27 | 28 | private readonly Action _OutputVisitor; 29 | 30 | // Track whether Dispose has been called. 31 | private bool _Disposed; 32 | 33 | public PipelineContext(OptionContext option, PipelineStream stream, IPipelineWriter writer, Action _Output, InstanceNameBinder instanceNameBinder, string[] convention) 34 | { 35 | Option = option; 36 | LanguageMode = option.Execution.LanguageMode.GetValueOrDefault(ExecutionOption.Default.LanguageMode.Value); 37 | Filter = DocumentFilter.Create(option.Document.Include, option.Document.Tag); 38 | Stream = stream ?? new PipelineStream(null, null); 39 | Writer = writer; 40 | InstanceNameBinder = instanceNameBinder; 41 | _OutputVisitor = _Output; 42 | Convention = convention; 43 | Selector = new Dictionary(); 44 | } 45 | 46 | internal void WriteOutput(IDocumentResult result) 47 | { 48 | _OutputVisitor(result, false); 49 | } 50 | 51 | internal void Import(IResource resource) 52 | { 53 | if (resource.Kind == ResourceKind.Selector && resource is SelectorV1 selector) 54 | Selector[selector.Id] = new SelectorVisitor(selector.Id, selector.Spec.If); 55 | } 56 | 57 | #region IDisposable 58 | 59 | public void Dispose() 60 | { 61 | Dispose(true); 62 | GC.SuppressFinalize(this); 63 | } 64 | 65 | private void Dispose(bool disposing) 66 | { 67 | if (!_Disposed) 68 | { 69 | //if (disposing) 70 | //{ 71 | 72 | //} 73 | _Disposed = true; 74 | } 75 | } 76 | 77 | #endregion IDisposable 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/PipelineStream.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Management.Automation; 7 | using PSDocs.Data; 8 | 9 | namespace PSDocs.Pipeline 10 | { 11 | internal sealed class PipelineStream 12 | { 13 | private readonly VisitTargetObject _Input; 14 | private readonly InputFileInfo[] _InputPath; 15 | private readonly ConcurrentQueue _Queue; 16 | 17 | public PipelineStream(VisitTargetObject input, InputFileInfo[] inputPath) 18 | { 19 | _Input = input; 20 | _InputPath = inputPath; 21 | _Queue = new ConcurrentQueue(); 22 | } 23 | 24 | public int Count => _Queue.Count; 25 | 26 | public bool IsEmpty => _Queue.IsEmpty; 27 | 28 | public void Enqueue(PSObject sourceObject) 29 | { 30 | if (sourceObject == null) 31 | return; 32 | 33 | var targetObject = new TargetObject(sourceObject); 34 | if (_Input == null) 35 | { 36 | _Queue.Enqueue(targetObject); 37 | return; 38 | } 39 | 40 | // Visit the object, which may change or expand the object 41 | var input = _Input(targetObject); 42 | if (input == null) 43 | return; 44 | 45 | foreach (var item in input) 46 | _Queue.Enqueue(item); 47 | } 48 | 49 | public bool TryDequeue(out TargetObject targetObject) 50 | { 51 | return _Queue.TryDequeue(out targetObject); 52 | } 53 | 54 | public void Open() 55 | { 56 | if (_InputPath == null || _InputPath.Length == 0) 57 | return; 58 | 59 | // Read each file 60 | for (var i = 0; i < _InputPath.Length; i++) 61 | { 62 | if (_InputPath[i].IsUrl) 63 | { 64 | Enqueue(PSObject.AsPSObject(new Uri(_InputPath[i].FullName))); 65 | } 66 | else 67 | { 68 | Enqueue(PSObject.AsPSObject(_InputPath[i])); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/StreamPipeline.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Pipeline 7 | { 8 | internal abstract class StreamPipeline : PipelineBase 9 | { 10 | private readonly PipelineStream _Stream; 11 | 12 | internal StreamPipeline(PipelineContext context, Source[] source) 13 | : base(context, source) 14 | { 15 | _Stream = context.Stream; 16 | } 17 | 18 | public override void Begin() 19 | { 20 | _Stream.Open(); 21 | } 22 | 23 | public sealed override void Process(PSObject sourceObject) 24 | { 25 | _Stream.Enqueue(sourceObject); 26 | while (!_Stream.IsEmpty && _Stream.TryDequeue(out var nextObject)) 27 | ProcessObject(nextObject); 28 | } 29 | 30 | protected abstract void ProcessObject(TargetObject targetObject); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/PSDocs/Pipeline/TargetObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Management.Automation; 7 | using PSDocs.Data; 8 | using PSDocs.Definitions.Selectors; 9 | 10 | namespace PSDocs.Pipeline 11 | { 12 | internal abstract class TargetObjectAnnotation 13 | { 14 | 15 | } 16 | 17 | internal sealed class SelectorTargetAnnotation : TargetObjectAnnotation 18 | { 19 | private readonly Dictionary _Results; 20 | 21 | public SelectorTargetAnnotation() 22 | { 23 | _Results = new Dictionary(); 24 | } 25 | 26 | public bool TryGetSelectorResult(SelectorVisitor selector, out bool result) 27 | { 28 | return _Results.TryGetValue(selector.InstanceId, out result); 29 | } 30 | 31 | public void SetSelectorResult(SelectorVisitor selector, bool result) 32 | { 33 | _Results[selector.InstanceId] = result; 34 | } 35 | } 36 | 37 | internal sealed class TargetObject 38 | { 39 | private readonly Dictionary _Annotations; 40 | 41 | internal TargetObject(PSObject o) 42 | { 43 | Value = o; 44 | _Annotations = new Dictionary(); 45 | } 46 | 47 | public PSObject Value { get; } 48 | 49 | public InputFileInfo Source { get; private set; } 50 | 51 | public T GetAnnotation() where T : TargetObjectAnnotation, new() 52 | { 53 | if (!_Annotations.TryGetValue(typeof(T), out var value)) 54 | { 55 | value = new T(); 56 | _Annotations.Add(typeof(T), value); 57 | } 58 | return (T)value; 59 | } 60 | 61 | internal void SetSourceInfo(InputFileInfo sourceInfo) 62 | { 63 | Source = sourceInfo; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/PSDocs/Processor/IDocumentProcessor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Configuration; 5 | using PSDocs.Models; 6 | 7 | namespace PSDocs.Processor 8 | { 9 | public interface IDocumentProcessor 10 | { 11 | void Process(PSDocumentOption option, Document document); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/PSDocs/Processor/IDocumentResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Collections.Specialized; 6 | 7 | namespace PSDocs.Processor 8 | { 9 | internal interface IDocumentResult 10 | { 11 | string InstanceName { get; } 12 | 13 | string Extension { get; } 14 | 15 | string Culture { get; } 16 | 17 | string OutputPath { get; } 18 | 19 | string FullName { get; } 20 | 21 | OrderedDictionary Metadata { get; } 22 | 23 | Hashtable Data { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PSDocs/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("PSDocs.Benchmark")] 7 | [assembly: InternalsVisibleTo("PSDocs.Tests")] 8 | 9 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/Configuration.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Dynamic; 7 | 8 | namespace PSDocs.Runtime 9 | { 10 | /// 11 | /// A set of custom configuration values that are exposed at runtime. 12 | /// 13 | public sealed class Configuration : DynamicObject 14 | { 15 | private readonly RunspaceContext _Context; 16 | 17 | internal Configuration(RunspaceContext context) 18 | { 19 | _Context = context; 20 | } 21 | 22 | public override bool TryGetMember(GetMemberBinder binder, out object result) 23 | { 24 | result = null; 25 | if (_Context == null || binder == null || string.IsNullOrEmpty(binder.Name)) 26 | return false; 27 | 28 | // Get from configuration 29 | return TryGetValue(binder.Name, out result); 30 | } 31 | 32 | public string[] GetStringValues(string configurationKey) 33 | { 34 | if (!TryGetValue(configurationKey, out var value) || value == null) 35 | return System.Array.Empty(); 36 | 37 | if (value is string valueT) 38 | return new string[] { valueT }; 39 | 40 | if (value is string[] result) 41 | return result; 42 | 43 | if (value is IEnumerable c) 44 | { 45 | var cList = new List(); 46 | foreach (var v in c) 47 | cList.Add(v.ToString()); 48 | 49 | return cList.ToArray(); 50 | } 51 | return new string[] { value.ToString() }; 52 | } 53 | 54 | public object GetValueOrDefault(string configurationKey, object defaultValue) 55 | { 56 | if (!TryGetValue(configurationKey, out var value) || value == null) 57 | return defaultValue; 58 | 59 | return value; 60 | } 61 | 62 | public bool GetBoolOrDefault(string configurationKey, bool defaultValue) 63 | { 64 | if (!TryGetValue(configurationKey, out var value) || !TryBool(value, out var bvalue)) 65 | return defaultValue; 66 | 67 | return bvalue; 68 | } 69 | 70 | private bool TryGetValue(string name, out object value) 71 | { 72 | value = null; 73 | if (_Context == null) 74 | return false; 75 | 76 | return _Context.Pipeline.Option.Configuration.TryGetValue(name, out value); 77 | } 78 | 79 | private bool TryBool(object o, out bool value) 80 | { 81 | value = default; 82 | if (o is bool bvalue || (o is string svalue && bool.TryParse(svalue, out bvalue))) 83 | { 84 | value = bvalue; 85 | return true; 86 | } 87 | return false; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/DocumentContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Collections; 5 | using System.Collections.Specialized; 6 | 7 | namespace PSDocs.Runtime 8 | { 9 | internal sealed class DocumentContext 10 | { 11 | internal DocumentContext(RunspaceContext runspaceContext) 12 | { 13 | Culture = runspaceContext?.Culture; 14 | Metadata = new OrderedDictionary(); 15 | Data = new Hashtable(); 16 | } 17 | 18 | public string InstanceName { get; internal set; } 19 | 20 | public string Culture { get; } 21 | 22 | public string OutputPath { get; internal set; } 23 | 24 | public OrderedDictionary Metadata { get; } 25 | 26 | public Hashtable Data { get; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/Host.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | using System; 4 | using System.Globalization; 5 | using System.Management.Automation.Host; 6 | 7 | namespace PSDocs.Runtime 8 | { 9 | internal sealed class Host : PSHost 10 | { 11 | public override CultureInfo CurrentCulture => throw new NotImplementedException(); 12 | 13 | public override CultureInfo CurrentUICulture => throw new NotImplementedException(); 14 | 15 | public override Guid InstanceId => throw new NotImplementedException(); 16 | 17 | public override string Name => throw new NotImplementedException(); 18 | 19 | public override PSHostUserInterface UI => throw new NotImplementedException(); 20 | 21 | public override Version Version => throw new NotImplementedException(); 22 | 23 | public override void EnterNestedPrompt() 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | 28 | public override void ExitNestedPrompt() 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | 33 | public override void NotifyBeginApplication() 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | 38 | public override void NotifyEndApplication() 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | 43 | public override void SetShouldExit(int exitCode) 44 | { 45 | throw new NotImplementedException(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/ILanguageBlock.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Runtime 5 | { 6 | public interface ILanguageBlock 7 | { 8 | string Id { get; } 9 | 10 | string SourcePath { get; } 11 | 12 | string Module { get; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/LanguageScriptBlock.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | 6 | namespace PSDocs.Runtime 7 | { 8 | internal sealed class LanguageScriptBlock 9 | { 10 | private readonly PowerShell _Block; 11 | 12 | public LanguageScriptBlock(PowerShell block) 13 | { 14 | _Block = block; 15 | } 16 | 17 | public void Invoke() 18 | { 19 | _Block.Invoke(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/LocalizedData.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Dynamic; 5 | 6 | namespace PSDocs.Runtime 7 | { 8 | public sealed class LocalizedData : DynamicObject 9 | { 10 | private readonly RunspaceContext _Context; 11 | 12 | public LocalizedData() { } 13 | 14 | internal LocalizedData(RunspaceContext context) 15 | { 16 | _Context = context; 17 | } 18 | 19 | public override bool TryGetMember(GetMemberBinder binder, out object result) 20 | { 21 | var hashtable = GetContext().GetLocalizedStrings(); 22 | if (hashtable.Count > 0 && binder != null && !string.IsNullOrEmpty(binder.Name) && hashtable.ContainsKey(binder.Name)) 23 | { 24 | result = hashtable[binder.Name]; 25 | return true; 26 | } 27 | result = null; 28 | return false; 29 | } 30 | 31 | private RunspaceContext GetContext() 32 | { 33 | if (_Context == null) 34 | return RunspaceContext.CurrentThread; 35 | 36 | return _Context; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/ResourceExtent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Runtime 5 | { 6 | internal interface IResourceExtent 7 | { 8 | string File { get; } 9 | 10 | int StartLineNumber { get; } 11 | } 12 | 13 | internal sealed class ResourceExtent : IResourceExtent 14 | { 15 | internal ResourceExtent(string file, int startLineNumber) 16 | { 17 | File = file; 18 | StartLineNumber = startLineNumber; 19 | } 20 | 21 | public string File { get; } 22 | 23 | public int StartLineNumber { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/ScopedItem.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Pipeline; 5 | 6 | namespace PSDocs.Runtime 7 | { 8 | public abstract class ScopedItem 9 | { 10 | private readonly RunspaceContext _Context; 11 | 12 | internal ScopedItem() 13 | { 14 | 15 | } 16 | 17 | internal ScopedItem(RunspaceContext context) 18 | { 19 | _Context = context; 20 | } 21 | 22 | #region Helper methods 23 | 24 | internal void RequireScope(RunspaceScope scope) 25 | { 26 | if (GetContext().IsScope(scope)) 27 | return; 28 | 29 | throw new RuntimeException(); 30 | } 31 | 32 | internal RunspaceContext GetContext() 33 | { 34 | if (_Context == null) 35 | return RunspaceContext.CurrentThread; 36 | 37 | return _Context; 38 | } 39 | 40 | #endregion Helper methods 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/ScriptDocumentBlock.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Management.Automation; 7 | using PSDocs.Definitions; 8 | using PSDocs.Pipeline; 9 | 10 | namespace PSDocs.Runtime 11 | { 12 | /// 13 | /// A Document block. 14 | /// 15 | [DebuggerDisplay("{Id} @{SourcePath}")] 16 | internal sealed class ScriptDocumentBlock : IDocumentDefinition, IDisposable 17 | { 18 | internal readonly PowerShell Body; 19 | internal readonly string[] Tag; 20 | 21 | // Track whether Dispose has been called. 22 | private bool _Disposed; 23 | 24 | internal ScriptDocumentBlock(SourceFile source, string name, PowerShell body, string[] tag, IResourceExtent extent) 25 | { 26 | Source = source; 27 | Name = name; 28 | Id = ResourceHelper.GetId(source.ModuleName, name); 29 | Body = body; 30 | Tag = tag; 31 | Extent = extent; 32 | } 33 | 34 | public string Id { get; } 35 | 36 | public string Name { get; } 37 | 38 | public string SourcePath => Source.Path; 39 | 40 | public string Module => Source.ModuleName; 41 | 42 | internal SourceFile Source { get; } 43 | 44 | internal IResourceExtent Extent { get; } 45 | 46 | #region IDisposable 47 | 48 | public void Dispose() 49 | { 50 | Dispose(true); 51 | GC.SuppressFinalize(this); 52 | } 53 | 54 | private void Dispose(bool disposing) 55 | { 56 | if (!_Disposed) 57 | { 58 | if (disposing) 59 | { 60 | Body.Dispose(); 61 | } 62 | _Disposed = true; 63 | } 64 | } 65 | 66 | #endregion IDisposable 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/SourceScope.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Text; 5 | using PSDocs.Pipeline; 6 | 7 | namespace PSDocs.Runtime 8 | { 9 | internal sealed class SourceScope 10 | { 11 | public readonly SourceFile File; 12 | public readonly string[] Content; 13 | 14 | public SourceScope(SourceFile source) 15 | { 16 | File = source; 17 | Content = System.IO.File.ReadAllLines(source.Path, Encoding.UTF8); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/StringContent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace PSDocs.Runtime 8 | { 9 | internal sealed class StringContent 10 | { 11 | private readonly string _Input; 12 | 13 | public StringContent(string input, string info = null) 14 | { 15 | _Input = input; 16 | Info = info; 17 | } 18 | 19 | public string Info { get; } 20 | 21 | internal string[] ReadLines() 22 | { 23 | if (string.IsNullOrEmpty(_Input)) 24 | return Array.Empty(); 25 | 26 | var lines = new List(); 27 | var stream = new StringStream(_Input); 28 | stream.SkipLineEnding(); 29 | var indent = stream.GetIndent(); 30 | 31 | while (!stream.EOF) 32 | { 33 | stream.SkipIndent(indent); 34 | if (stream.UntilLineEnding(out var text)) 35 | lines.Add(text); 36 | 37 | stream.SkipLineEnding(); 38 | } 39 | return lines.ToArray(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PSDocs/Runtime/StringStream.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Runtime 5 | { 6 | internal sealed class StringStream 7 | { 8 | private const char EmptyChar = '\0'; 9 | private const char LF = '\n'; 10 | private const char CR = '\r'; 11 | 12 | private readonly string _Input; 13 | private readonly int _LastIndex; 14 | 15 | private int _Position = -1; 16 | private char _Current = EmptyChar; 17 | 18 | public bool EOF => _Position > _LastIndex; 19 | 20 | public StringStream(string input) 21 | { 22 | _Input = input; 23 | _LastIndex = input.Length - 1; 24 | Next(); 25 | } 26 | 27 | internal void SkipLineEnding() 28 | { 29 | if (!IsLineEnding(_Current)) 30 | return; 31 | 32 | if (_Current == CR) // \r 33 | Next(); 34 | 35 | if (_Current == LF) // \n 36 | Next(); 37 | } 38 | 39 | public bool UntilLineEnding(out string text) 40 | { 41 | text = string.Empty; 42 | if (EOF) 43 | return false; 44 | 45 | var count = 0; 46 | var startingPos = _Position; 47 | while (!EOF && !IsLineEnding(_Current)) 48 | { 49 | count++; 50 | Next(); 51 | } 52 | text = _Input.Substring(startingPos, count); 53 | return true; 54 | } 55 | 56 | public int GetIndent() 57 | { 58 | var offset = 0; 59 | while (Peak(offset, out var c) && IsIndent(c)) 60 | offset++; 61 | 62 | return offset; 63 | } 64 | 65 | public bool Next() 66 | { 67 | if (_Position > _LastIndex) 68 | return false; 69 | 70 | _Position++; 71 | if (_Position > _LastIndex) 72 | { 73 | _Current = EmptyChar; 74 | return false; 75 | } 76 | _Current = _Input[_Position]; 77 | return true; 78 | } 79 | 80 | private bool Peak(int offset, out char c) 81 | { 82 | c = EmptyChar; 83 | var index = _Position + offset; 84 | if (index > _LastIndex) 85 | return false; 86 | 87 | c = _Input[index]; 88 | return true; 89 | } 90 | 91 | public void SkipIndent(int indent) 92 | { 93 | if (indent == 0 || EOF || IsLineEnding(_Current)) 94 | return; 95 | 96 | var count = 0; 97 | while (count < indent && IsIndent(_Current) && Next()) 98 | count++; 99 | } 100 | 101 | private static bool IsIndent(char c) 102 | { 103 | return char.IsWhiteSpace(c); 104 | } 105 | 106 | private static bool IsLineEnding(char c) 107 | { 108 | return c == LF || c == CR; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/PSDocs/en-AU/PSDocs.Resources.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | ConvertFrom-StringData @' 5 | ###PSLOC 6 | DocumentNotFound=Failed to find document: {0} 7 | PathNotFound=Path not found 8 | SourceNotFound=Path not found 9 | DocumentProcessFailure=Failed to process document 10 | SectionProcessFailure=Failed to process section: {0} 11 | ###PSLOC 12 | '@ -------------------------------------------------------------------------------- /src/PSDocs/en-GB/PSDocs.Resources.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | ConvertFrom-StringData @' 5 | ###PSLOC 6 | DocumentNotFound=Failed to find document: {0} 7 | PathNotFound=Path not found 8 | SourceNotFound=Path not found 9 | DocumentProcessFailure=Failed to process document 10 | SectionProcessFailure=Failed to process section: {0} 11 | ###PSLOC 12 | '@ -------------------------------------------------------------------------------- /src/PSDocs/en-US/PSDocs.Resources.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | ConvertFrom-StringData @' 5 | ###PSLOC 6 | DocumentNotFound=Failed to find document: {0} 7 | PathNotFound=Path not found 8 | SourceNotFound=Could not find any .Doc.ps1 script files in the path. 9 | DocumentProcessFailure=Failed to process document 10 | SectionProcessFailure=Failed to process section: {0} 11 | KeywordOutsideEngine=This keyword can only be called within PSDocs. Add document definitions to .Doc.ps1 files, then execute them with Invoke-PSDocument. 12 | ###PSLOC 13 | '@ 14 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.Cmdlets.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Document definitions for unit testing 6 | # 7 | 8 | # Synopsis: Document for unit testing 9 | Document 'WithoutInstanceName' { 10 | $InstanceName; 11 | $TargetObject.Object.Name; 12 | $TargetObject.Hashtable.Name; 13 | } 14 | 15 | # Synopsis: Document for unit testing 16 | Document 'WithInstanceName' { 17 | $InstanceName; 18 | } 19 | 20 | # Synopsis: Document for unit testing 21 | document 'WithMultiInstanceName' { 22 | $InstanceName; 23 | } 24 | 25 | # Synopsis: Document for unit testing 26 | document 'WithEncoding' { 27 | $InstanceName; 28 | } 29 | 30 | # Synopsis: Document for unit testing 31 | document 'WithPassThru' { 32 | $InstanceName; 33 | } 34 | 35 | # Synopsis: Document for unit testing 36 | document 'WithMetadata' { 37 | Metadata @{ 38 | key1 = 'value1' 39 | } 40 | Section 'Test' -Force { 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.Conventions.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Conventions for unit testing 6 | # 7 | 8 | # Synopsis: An example naming convention. 9 | Export-PSDocumentConvention 'TestNamingConvention1' { 10 | Write-Verbose -Message 'Convention process' 11 | $PSDocs.Document.InstanceName = [String]::Concat($PSDocs.Document.Data.testName, '_', $PSDocs.Document.Data.count); 12 | $newPath = Join-Path -Path $PSDocs.Document.OutputPath -ChildPath 'new'; 13 | $PSDocs.Document.OutputPath = $newPath; 14 | } -Begin { 15 | Write-Verbose -Message 'Convention begin'; 16 | $PSDocs.Document.Data['count'] = 1; 17 | } -End { 18 | Write-Verbose -Message "Convention end"; 19 | foreach ($output in $PSDocs.Output) { 20 | $tocPath = Join-Path -Path $output.OutputPath -ChildPath 'toc.yaml'; 21 | Set-Content -Path $tocPath -Value ''; 22 | } 23 | } 24 | 25 | # Synopsis: An example naming convention. 26 | Export-PSDocumentConvention 'TestNamingConvention2' { 27 | $PSDocs.Document.Data.count += 1; 28 | Write-Verbose -Message 'Convention process' 29 | $PSDocs.Document.InstanceName = [String]::Concat($PSDocs.Document.Data.testName, '_', $PSDocs.Document.Data.count); 30 | $newPath = Join-Path -Path $PSDocs.Document.OutputPath -ChildPath 'new'; 31 | $PSDocs.Document.OutputPath = $newPath; 32 | } 33 | 34 | # Synopsis: A test document for testing conventions. 35 | Document 'ConventionDoc1' { 36 | $PSDocs.Document.Data.testName = $PSDocs.TargetObject.Name; 37 | $PSDocs.Document.Data.count += 1; 38 | 'Convention test' 39 | } 40 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Export-PSDocumentConvention 'FromFileTest1' { 5 | 6 | } 7 | 8 | Document 'FromFileTest1' { 9 | Title 'Test title' 10 | Metadata @{ 11 | test = 'Test1' 12 | } 13 | Section 'Test' { 14 | 'Test 1' 15 | } 16 | } 17 | 18 | Document 'FromFileTest2' { 19 | Metadata @{ 20 | test = 'Test2' 21 | } 22 | Section 'Test' { 23 | 'Test 2' 24 | } 25 | } 26 | 27 | Document 'FromFileTest3' -Tag 'Test3' { 28 | Metadata @{ 29 | test = 'Test3' 30 | } 31 | Section 'Test' { 32 | 'Test 3' 33 | } 34 | } 35 | 36 | Document 'FromFileTest4' -Tag 'Test4' { 37 | Section 'Test' { 38 | 'Test 4' 39 | } 40 | } 41 | 42 | Document 'FromFileTest5' -Tag 'Test4','Test5' { 43 | Section 'Test' { 44 | 'Test 5' 45 | } 46 | } 47 | 48 | Document 'ConstrainedTest1' { 49 | Section 'Test' { 50 | 'Test 1' 51 | } 52 | } 53 | 54 | Document 'ConstrainedTest2' { 55 | 56 | [Console]::WriteLine('Should fail'); 57 | } 58 | 59 | Document 'WithIf' -If { $TargetObject.Generator -eq 'PSDocs' } { 60 | Metadata @{ 61 | Name = $PSDocs.TargetObject.Name 62 | } 63 | 64 | 'EOF' 65 | } 66 | 67 | Document 'WithSelector' -With 'GeneratorSelector' { 68 | Metadata @{ 69 | Name = $PSDocs.TargetObject.Name 70 | } 71 | 72 | 'EOF' 73 | } 74 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.Selector.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Definitions for testing selectors 6 | # 7 | 8 | Document 'Selector.WithInputObject' -With 'GeneratorSelector' { 9 | Metadata @{ 10 | Name = $PSDocs.TargetObject.Name 11 | } 12 | $PSDocs.TargetObject.Name 13 | } 14 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.TestObjects.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/PSDocs/7bab9feddf8a3b8ee6f2d2d3dc90df6553ee5f1b/tests/PSDocs.Tests/FromFile.TestObjects.yaml -------------------------------------------------------------------------------- /tests/PSDocs.Tests/FromFile.Variables.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # Synopsis: Document for unit testing 5 | Document 'PSAutomaticVariables' { 6 | "PWD=$PWD;" 7 | "PSScriptRoot=$PSScriptRoot;" 8 | "PSCommandPath=$PSCommandPath;" 9 | } 10 | 11 | # Synopsis: Test $PSDocs variable 12 | Document 'PSDocsVariable' { 13 | Metadata @{ 14 | author = $PSDocs.Configuration.author.name 15 | } 16 | $PSDocs.Format("TargetObject.Name={0};", $PSDocs.TargetObject.Name); 17 | if ($PSDocs.Configuration.GetBoolOrDefault('NotConfig', $True)) { 18 | "Document.Metadata=$($Document.Metadata['author']);" 19 | } 20 | if ($PSDocs.Configuration.GetBoolOrDefault('Enabled', $True)) { 21 | "Document.Enabled=true" 22 | } 23 | } 24 | 25 | # Synopsis: Test $Document variable 26 | Document 'PSDocsDocumentVariable' { 27 | Title '001' 28 | Metadata @{ 29 | author = '002' 30 | } 31 | "Document.Title=$($Document.Title);" 32 | "Document.Metadata=$($Document.Metadata['author']);" 33 | } 34 | 35 | # Synopsis: Test $LocalizedData variable 36 | Document 'PSDocsLocalizedDataVariable' { 37 | "LocalizedData.Key1=$($LocalizedData.Key1);" 38 | } 39 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/IncludeFile.md: -------------------------------------------------------------------------------- 1 | This is included from an external file. -------------------------------------------------------------------------------- /tests/PSDocs.Tests/IncludeFile2.md: -------------------------------------------------------------------------------- 1 | This is a second file to include. -------------------------------------------------------------------------------- /tests/PSDocs.Tests/KeywordTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Management.Automation; 7 | using PSDocs.Configuration; 8 | using PSDocs.Models; 9 | using PSDocs.Pipeline; 10 | 11 | namespace PSDocs 12 | { 13 | public sealed class KeywordTests 14 | { 15 | [Fact] 16 | public void TableTests() 17 | { 18 | var actual = BuildDocument("TableWithExpression"); 19 | Assert.Single(actual); 20 | Assert.IsType(actual[0].Node[0]); 21 | var table = actual[0].Node[0] as Table; 22 | Assert.Equal("Dummy", table.Rows[0][0]); 23 | Assert.Equal("3", table.Rows[0][3]); 24 | 25 | actual = BuildDocument("TableSingleEntryMarkdown"); 26 | Assert.Single(actual); 27 | Assert.IsType
(actual[0].Node[0]); 28 | 29 | actual = BuildDocument("TableWithMultilineColumn"); 30 | Assert.Single(actual); 31 | Assert.IsType
(actual[0].Node[0]); 32 | Assert.True((actual[0].Node[0] as Table).Rows[0].Length > 1); 33 | 34 | actual = BuildDocument("TableWithEmptyColumn"); 35 | Assert.Single(actual); 36 | Assert.IsType
(actual[0].Node[1]); 37 | } 38 | 39 | private static Document[] BuildDocument(string documentName) 40 | { 41 | var builder = PipelineBuilder.Invoke(GetSource(), GetOption(new string[] { documentName }), null, null); 42 | var pipeline = builder.Build() as InvokePipeline; 43 | var targetObject = new TargetObject(PSObject.AsPSObject(new TestModel())); 44 | return pipeline.BuildDocument(targetObject); 45 | } 46 | 47 | private static PSDocumentOption GetOption(string[] name = null) 48 | { 49 | var option = new PSDocumentOption(); 50 | if (name != null && name.Length > 0) 51 | { 52 | option.Document.Include = name; 53 | } 54 | option.Output.Culture = new string[] { "en-US" }; 55 | return option; 56 | } 57 | 58 | private static Source[] GetSource() 59 | { 60 | var builder = new SourcePipelineBuilder(new HostContext(null, null)); 61 | builder.Directory(GetSourcePath("FromFile.Keyword.Doc.ps1")); 62 | return builder.Build(); 63 | } 64 | 65 | private static string GetSourcePath(string fileName) 66 | { 67 | return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/LanguageVisitorTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Management.Automation; 5 | using PSDocs.Configuration; 6 | using PSDocs.Pipeline; 7 | using PSDocs.Runtime; 8 | 9 | namespace PSDocs 10 | { 11 | public sealed class LanguageVisitorTests 12 | { 13 | [Fact] 14 | public void NestedDefintion() 15 | { 16 | var content = @" 17 | # Header comment 18 | Document 'Doc1' { 19 | 20 | } 21 | "; 22 | var scriptAst = ScriptBlock.Create(content).Ast; 23 | var visitor = new LanguageAst(GetContext()); 24 | scriptAst.Visit(visitor); 25 | 26 | Assert.Null(visitor.Errors); 27 | 28 | content = @" 29 | # Header comment 30 | Document 'Doc1' { 31 | Document 'Doc2' { 32 | 33 | } 34 | } 35 | "; 36 | scriptAst = ScriptBlock.Create(content).Ast; 37 | visitor = new LanguageAst(GetContext()); 38 | scriptAst.Visit(visitor); 39 | 40 | Assert.Single(visitor.Errors); 41 | } 42 | 43 | [Fact] 44 | public void UnvalidDefinition() 45 | { 46 | var content = @" 47 | Document '' { 48 | 49 | } 50 | 51 | Document { 52 | 53 | } 54 | 55 | Document 'Doc1'; 56 | 57 | Document '' { 58 | 59 | } 60 | 61 | Document 'Doc2' { 62 | 63 | } 64 | 65 | Document -Name 'Doc3' { 66 | 67 | } 68 | 69 | Document -Name 'Doc3' -Body { 70 | 71 | } 72 | 73 | "; 74 | 75 | var scriptAst = ScriptBlock.Create(content).Ast; 76 | var visitor = new LanguageAst(GetContext()); 77 | scriptAst.Visit(visitor); 78 | 79 | Assert.NotNull(visitor.Errors); 80 | Assert.Equal(4, visitor.Errors.Count); 81 | } 82 | 83 | private static PipelineContext GetContext() 84 | { 85 | return new PipelineContext(GetOption(), null, null, null, null, null); 86 | } 87 | 88 | private static OptionContext GetOption(string[] name = null) 89 | { 90 | var option = new PSDocumentOption(); 91 | return new OptionContext(option); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/MarkdownProcessorTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Configuration; 5 | using PSDocs.Models; 6 | using PSDocs.Processor.Markdown; 7 | using PSDocs.Runtime; 8 | 9 | namespace PSDocs 10 | { 11 | public sealed class MarkdownProcessorTests 12 | { 13 | [Fact] 14 | public void LineEndings() 15 | { 16 | var document = GetDocument(); 17 | var actual = GetProcessor().Process(GetOption(), document).ToString(); 18 | var expected = @"# Test document 19 | 20 | ## Section 1 21 | "; 22 | Assert.Equal(expected, actual); 23 | } 24 | 25 | private static Document GetDocument() 26 | { 27 | var result = new Document(new DocumentContext(null)) 28 | { 29 | Title = "Test document" 30 | }; 31 | var section = new Section 32 | { 33 | Title = "Section 1", 34 | Level = 2 35 | }; 36 | result.Node.Add(section); 37 | return result; 38 | } 39 | 40 | private static MarkdownProcessor GetProcessor() 41 | { 42 | return new MarkdownProcessor(); 43 | } 44 | 45 | private static PSDocumentOption GetOption() 46 | { 47 | return new PSDocumentOption(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/Models/TestModel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | namespace PSDocs.Models 5 | { 6 | internal sealed class TestModel 7 | { 8 | public TestModel() 9 | { 10 | Name = "Test"; 11 | Description = "This is a\r\ndescription\r\nsplit\r\nover\r\nmultiple\r\nlines."; 12 | Generator = "PSDocs"; 13 | } 14 | 15 | public string Name { get; set; } 16 | 17 | public string Description { get; set; } 18 | 19 | public string Generator { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.BlockQuote.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the BlockQuote keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | 11 | BeforeAll{ 12 | # Setup error handling 13 | $ErrorActionPreference = 'Stop'; 14 | Set-StrictMode -Version latest; 15 | 16 | # Setup tests paths 17 | $rootPath = $PWD; 18 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 19 | $here = (Resolve-Path $PSScriptRoot).Path; 20 | $nl = [System.Environment]::NewLine; 21 | } 22 | Describe 'PSDocs -- BlockQuote keyword' -Tag BlockQuote { 23 | 24 | Context 'Markdown' { 25 | BeforeAll{ 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 27 | $testObject = [PSCustomObject]@{ 28 | Name = 'TestObject' 29 | } 30 | $invokeParams = @{ 31 | Path = $docFilePath 32 | InputObject = $testObject 33 | PassThru = $True 34 | } 35 | } 36 | It 'Should handle single line input' { 37 | $result = Invoke-PSDocument @invokeParams -Name 'BlockQuoteSingleMarkdown'; 38 | $result | Should -Match "Begin$($nl)$($nl)\> This is a single line$($nl)$($nl)End"; 39 | } 40 | It 'Should handle multiline input' { 41 | $result = Invoke-PSDocument @invokeParams -Name 'BlockQuoteMultiMarkdown'; 42 | $result | Should -Match "Begin$($nl)$($nl)\> This is the first line.$($nl)\> This is the second line.$($nl)$($nl)End"; 43 | } 44 | It 'Should add title' { 45 | $result = Invoke-PSDocument @invokeParams -Name 'BlockQuoteTitleMarkdown'; 46 | $result | Should -Match "Begin$($nl)$($nl)\> Test$($nl)\> This is a single block quote$($nl)$($nl)End"; 47 | } 48 | It 'Should add info' { 49 | $result = Invoke-PSDocument @invokeParams -Name 'BlockQuoteInfoMarkdown'; 50 | $result | Should -Match "Begin$($nl)$($nl)\> \[!TIP\]$($nl)\> This is a single block quote$($nl)$($nl)End"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Code.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Code keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | 11 | BeforeAll { 12 | # Setup error handling 13 | $ErrorActionPreference = 'Stop'; 14 | Set-StrictMode -Version latest; 15 | 16 | # Setup tests paths 17 | $rootPath = $PWD; 18 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 19 | $here = (Resolve-Path $PSScriptRoot).Path; 20 | $nl = [System.Environment]::NewLine; 21 | } 22 | 23 | Describe 'PSDocs -- Code keyword' -Tag Code { 24 | Context 'Markdown' { 25 | BeforeAll { 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 27 | $testObject = [PSCustomObject]@{ 28 | Name = 'TestObject' 29 | } 30 | $invokeParams = @{ 31 | Path = $docFilePath 32 | InputObject = $testObject 33 | PassThru = $True 34 | } 35 | } 36 | It 'Should have generated output' { 37 | $result = Invoke-PSDocument @invokeParams -Name 'CodeMarkdown'; 38 | $result | Should -Match "Begin$($nl)$($nl)``````powershell$($nl)`# This is a comment$($nl)This is code$($nl)$($nl)`# Another comment$($nl)And code$($nl)``````$($nl)$($nl)End"; 39 | } 40 | It 'Code markdown with named format' { 41 | $result = Invoke-PSDocument @invokeParams -Name 'CodeMarkdownNamedFormat'; 42 | $result | Should -Match "Begin$($nl)$($nl)``````powershell$($nl)Get-Content$($nl)``````$($nl)$($nl)End"; 43 | } 44 | It 'Code markdown with evaluation' { 45 | $result = Invoke-PSDocument @invokeParams -Name 'CodeMarkdownEval'; 46 | $result | Should -Match "Begin$($nl)$($nl)``````powershell$($nl)2$($nl)``````$($nl)$($nl)End"; 47 | } 48 | It 'Code markdown with include' { 49 | $result = Invoke-PSDocument @invokeParams -Name 'CodeInclude'; 50 | $result | Should -Match "Begin$($nl)$($nl)``````yaml$($nl)generator: PSDocs$($nl)``````$($nl)$($nl)End"; 51 | } 52 | It 'Code markdown with JSON conversion' { 53 | $result = Invoke-PSDocument @invokeParams -Name 'CodeJson'; 54 | $result | Should -Match "``````json$($nl){$($nl) `"Name`": `"Value`"$($nl)}$($nl)``````"; 55 | } 56 | It 'Code markdown with YAML conversion' { 57 | $result = Invoke-PSDocument @invokeParams -Name 'CodeYaml'; 58 | $result | Should -Match "``````yaml$($nl)name: Value$($nl)``````"; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Conventions.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the PSDocs conventions 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | BeforeAll { 11 | # Setup error handling 12 | $ErrorActionPreference = 'Stop'; 13 | Set-StrictMode -Version latest; 14 | 15 | # Setup tests paths 16 | $rootPath = $PWD; 17 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 18 | $outputPath = Join-Path -Path $rootPath -ChildPath out/tests/PSDocs.Tests/Conventions; 19 | $here = (Resolve-Path $PSScriptRoot).Path; 20 | } 21 | Describe 'PSDocs -- Conventions' -Tag Conventions { 22 | 23 | 24 | Context '-Convention' { 25 | BeforeAll { 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Conventions.Doc.ps1'; 27 | $testObject = [PSCustomObject]@{ 28 | Name = 'TestObject' 29 | } 30 | $invokeParams = @{ 31 | Path = $docFilePath 32 | InputObject = $testObject 33 | OutputPath = $outputPath 34 | } 35 | } 36 | It 'Generate output' { 37 | # Singe convention 38 | $result = Invoke-PSDocument @invokeParams -Name 'ConventionDoc1' -Convention 'TestNamingConvention1'; 39 | $result | Should -BeLike '*TestObject_2.md'; 40 | $testFile = Join-Path -Path $outputPath -ChildPath 'new/TestObject_2.md'; 41 | Test-Path -Path $testFile | Should -Be $True; 42 | $tocFile = Join-Path -Path $outputPath -ChildPath 'new/toc.yaml'; 43 | Test-Path -Path $tocFile | Should -Be $True; 44 | 45 | # Multiple conventions 46 | $result = Invoke-PSDocument @invokeParams -Name 'ConventionDoc1' -Convention 'TestNamingConvention1', 'TestNamingConvention2'; 47 | $result | Should -BeLike '*TestObject_3.md'; 48 | $testFile = Join-Path -Path $outputPath -ChildPath 'new/new/TestObject_3.md'; 49 | Test-Path -Path $testFile | Should -Be $True; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Include.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Include keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | 11 | BeforeAll { 12 | # Setup error handling 13 | $ErrorActionPreference = 'Stop'; 14 | Set-StrictMode -Version latest; 15 | 16 | # Setup tests paths 17 | $rootPath = $PWD; 18 | 19 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 20 | 21 | $outputPath = Join-Path -Path $rootPath -ChildPath out/tests/PSDocs.Tests/Include; 22 | Remove-Item -Path $outputPath -Force -Recurse -Confirm:$False -ErrorAction Ignore; 23 | $Null = New-Item -Path $outputPath -ItemType Directory -Force; 24 | $here = (Resolve-Path $PSScriptRoot).Path; 25 | } 26 | Describe 'PSDocs -- Include keyword' -Tag Include { 27 | Context 'Markdown' { 28 | BeforeAll { 29 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 30 | $testObject = [PSCustomObject]@{}; 31 | $invokeParams = @{ 32 | Path = $docFilePath 33 | OutputPath = $outputPath 34 | ErrorAction = [System.Management.Automation.ActionPreference]::Stop 35 | } 36 | } 37 | 38 | It 'Should include a relative path' { 39 | $outputDoc = "$outputPath\IncludeRelative.md"; 40 | $Null = Invoke-PSDocument @invokeParams -InputObject $rootPath -Name 'IncludeRelative'; 41 | 42 | Test-Path -Path $outputDoc | Should -Be $True; 43 | $outputDoc | Should -FileContentMatch 'This is included from an external file.'; 44 | $outputDoc | Should -FileContentMatch 'This is a second file to include.'; 45 | } 46 | 47 | It 'Should include an absolute path' { 48 | $outputDoc = "$outputPath\IncludeAbsolute.md"; 49 | $Null = Invoke-PSDocument @invokeParams -InputObject $rootPath -Name 'IncludeAbsolute'; 50 | 51 | Test-Path -Path $outputDoc | Should -Be $True; 52 | $outputDoc | Should -FileContentMatch 'This is included from an external file.'; 53 | } 54 | 55 | It 'Should include from culture' { 56 | $Null = $testObject | Invoke-PSDocument @invokeParams -Culture 'en-AU', 'en-US' -Name 'IncludeCulture'; 57 | 58 | $outputDoc = "$outputPath\en-AU\IncludeCulture.md"; 59 | Test-Path -Path $outputDoc | Should -Be $True; 60 | $outputDoc | Should -FileContentMatch 'This is en-AU.'; 61 | 62 | $outputDoc = "$outputPath\en-US\IncludeCulture.md"; 63 | Test-Path -Path $outputDoc | Should -Be $True; 64 | $outputDoc | Should -FileContentMatch 'This is en-US.'; 65 | } 66 | 67 | It 'Should include when file exists' { 68 | $Null = $testObject | Invoke-PSDocument @invokeParams -Name 'IncludeOptional'; 69 | { $Null = $testObject | Invoke-PSDocument @invokeParams -Name 'IncludeRequired'; } | Should -Throw -Because 'PSDocs.Runtime.IncludeNotFound'; 70 | } 71 | 72 | It 'Should replace tokens' { 73 | $result = $testObject | Invoke-PSDocument @invokeParams -Name 'IncludeReplace' -PassThru; 74 | $result | Should -BeLike 'This is a third file to include.*' 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Metadata.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Metadata keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param ( 10 | 11 | ) 12 | BeforeAll { 13 | # Setup error handling 14 | $ErrorActionPreference = 'Stop'; 15 | Set-StrictMode -Version latest; 16 | 17 | # Setup tests paths 18 | $rootPath = $PWD; 19 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 20 | $here = (Resolve-Path $PSScriptRoot).Path; 21 | $dummyObject = New-Object -TypeName PSObject; 22 | } 23 | Describe 'PSDocs -- Metadata keyword' -Tag Metadata { 24 | 25 | Context 'Markdown' { 26 | BeforeAll{ 27 | 28 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 29 | $invokeParams = @{ 30 | Path = $docFilePath 31 | InputObject = $dummyObject 32 | PassThru = $True 33 | } 34 | } 35 | It 'Metadata single entry' { 36 | $result = Invoke-PSDocument @invokeParams -Name 'MetadataSingleEntry'; 37 | $result | Should -Match '---(\r|\n|\r\n)title: Test(\r|\n|\r\n)---'; 38 | } 39 | 40 | It 'Metadata multiple entries' { 41 | $result = Invoke-PSDocument @invokeParams -Name 'MetadataMultipleEntry'; 42 | $result | Should -Match '---(\r|\n|\r\n)value1: ABC(\r|\n|\r\n)value2: EFG(\r|\n|\r\n)---'; 43 | } 44 | 45 | It 'Metadata multiple blocks' { 46 | $result = Invoke-PSDocument @invokeParams -Name 'MetadataMultipleBlock'; 47 | $result | Should -Match '---(\r|\n|\r\n)value1: ABC(\r|\n|\r\n)value2: EFG(\r|\n|\r\n)---'; 48 | } 49 | 50 | It 'Document without Metadata block' { 51 | $result = Invoke-PSDocument @invokeParams -Name 'NoMetdata'; 52 | $result | Should -Not -Match '---(\r|\n|\r\n)'; 53 | } 54 | 55 | It 'Document null Metadata block' { 56 | $result = Invoke-PSDocument @invokeParams -Name 'NullMetdata'; 57 | $result | Should -Not -Match '---(\r|\n|\r\n)'; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Note.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Note keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param ( 10 | 11 | ) 12 | BeforeAll { 13 | # Setup error handling 14 | $ErrorActionPreference = 'Stop'; 15 | Set-StrictMode -Version latest; 16 | 17 | # Setup tests paths 18 | $rootPath = $PWD; 19 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 20 | $here = (Resolve-Path $PSScriptRoot).Path; 21 | } 22 | Describe 'PSDocs -- Note keyword' -Tag Note { 23 | 24 | 25 | Context 'Markdown' { 26 | BeforeAll { 27 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 28 | $testObject = [PSCustomObject]@{ 29 | Name = 'TestObject' 30 | } 31 | $invokeParams = @{ 32 | Path = $docFilePath 33 | InputObject = $testObject 34 | PassThru = $True 35 | } 36 | } 37 | It 'Should handle single line input' { 38 | $result = Invoke-PSDocument @invokeParams -Name 'NoteSingleMarkdown'; 39 | $result | Should -Match '\> \[\!NOTE\](\r|\n|\r\n)> This is a single line'; 40 | } 41 | It 'Should handle multiline input' { 42 | $result = Invoke-PSDocument @invokeParams -Name 'NoteMultiMarkdown'; 43 | $result | Should -Match '\> \[\!NOTE\](\r|\n|\r\n)> This is the first line\.(\r|\n|\r\n)> This is the second line\.'; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Section.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Section keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | BeforeAll { 11 | # Setup error handling 12 | $ErrorActionPreference = 'Stop'; 13 | Set-StrictMode -Version latest; 14 | 15 | # Setup tests paths 16 | $rootPath = $PWD; 17 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 18 | $here = (Resolve-Path $PSScriptRoot).Path; 19 | $dummyObject = New-Object -TypeName PSObject; 20 | } 21 | Describe 'PSDocs -- Section keyword' -Tag Section { 22 | 23 | 24 | Context 'Markdown' { 25 | BeforeAll { 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 27 | $invokeParams = @{ 28 | Path = $docFilePath 29 | InputObject = $dummyObject 30 | PassThru = $True 31 | } 32 | } 33 | 34 | It 'With defaults' { 35 | $result = Invoke-PSDocument @invokeParams -Name 'SectionBlockTests' -InstanceName 'Section'; 36 | $result | Should -Match "`#`# SingleLine(\r|\n|\r\n){2,}This is a single line markdown section.(\r|\n|\r\n){2,}`#`# MultiLine(\r|\n|\r\n){2,}This is a multiline(\r|\n|\r\n)test."; 37 | $result | Should -Not -Match "`#`# Empty"; 38 | $result | Should -Match "`#`# Forced"; 39 | } 40 | 41 | It 'With -Force' { 42 | $result = Invoke-PSDocument @invokeParams -Name 'SectionBlockTests' -InstanceName 'Section2' -Option @{ 'Markdown.SkipEmptySections' = $False }; 43 | $result | Should -Match "`#`# Empty"; 44 | } 45 | 46 | It 'With -If' { 47 | $result = Invoke-PSDocument @invokeParams -Name 'SectionIf'; 48 | $result | Should -Match "`#`# Section 2(\r|\n|\r\n){2,}Content 2"; 49 | $result | Should -Not -Match "`#`# Section 1(\r|\n|\r\n){2,}Content 1"; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Selector.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for PSDocs selectors 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | BeforeAll { 11 | # Setup error handling 12 | $ErrorActionPreference = 'Stop'; 13 | Set-StrictMode -Version latest; 14 | 15 | # Setup tests paths 16 | $rootPath = $PWD; 17 | 18 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 19 | 20 | $outputPath = Join-Path -Path $rootPath -ChildPath out/tests/PSDocs.Tests/Selector; 21 | Remove-Item -Path $outputPath -Force -Recurse -Confirm:$False -ErrorAction Ignore; 22 | $Null = New-Item -Path $outputPath -ItemType Directory -Force; 23 | $here = (Resolve-Path $PSScriptRoot).Path; 24 | 25 | $dummyObject = @( 26 | [PSObject]@{ 27 | Name = 'ObjectName' 28 | Value = 'ObjectValue' 29 | generator = 'PSDocs' 30 | } 31 | 32 | [PSObject]@{ 33 | Name = 'HashName' 34 | Value = 'HashValue' 35 | generator = 'notPSDocs' 36 | } 37 | ) 38 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Selector.Doc.ps1'; 39 | $selectorFilePath = Join-Path -Path $here -ChildPath 'Selectors.Doc.yaml'; 40 | } 41 | Describe 'PSDocs selectors' -Tag 'Selector' { 42 | Context 'Invoke definitions' { 43 | BeforeAll { 44 | 45 | $invokeParams = @{ 46 | Path = @($docFilePath, $selectorFilePath) 47 | } 48 | 49 | It 'Generates documentation for matching objects' { 50 | $result = @($dummyObject | Invoke-PSDocument @invokeParams -Name 'Selector.WithInputObject' -PassThru); 51 | $result | Should -Not -BeNullOrEmpty; 52 | $result | Should -Not -Be 'Name: HashName'; 53 | } 54 | } 55 | } 56 | 57 | Context 'Get definitions' { 58 | It 'With selector' { 59 | $getParams = @{ 60 | Path = @($docFilePath, $selectorFilePath) 61 | } 62 | $result = @(Get-PSDocument @getParams) 63 | $result | Should -Not -BeNullOrEmpty; 64 | } 65 | 66 | It 'Missing selector' { 67 | $getParams = @{ 68 | Path = @($docFilePath) 69 | } 70 | { Get-PSDocument @getParams -ErrorAction Stop } | Should -Throw -ErrorId 'PSDocs.Parse.SelectorNotFound'; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | {4870f00e-8c1b-4df3-95ef-eddaa2e57205} 6 | true 7 | false 8 | PSDocs 9 | Full 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | PreserveNewest 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | PreserveNewest 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Tests.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # These are test options 5 | 6 | configuration: 7 | Key1: Value2 8 | UnitTests.String.1: Config string 1 9 | UnitTests.Bool.1: true 10 | 11 | execution: 12 | languageMode: ConstrainedLanguage 13 | 14 | input: 15 | format: Yaml 16 | objectPath: 'items' 17 | pathIgnore: 18 | - '*.Designer.cs' 19 | 20 | markdown: 21 | encoding: UTF8 22 | wrapSeparator: 'ZZZ' 23 | skipEmptySections: false 24 | columnPadding: Single 25 | useEdgePipes: Always 26 | 27 | output: 28 | culture: 'en-ZZ' 29 | path: out/ 30 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Title.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Title keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | 11 | BeforeAll { 12 | # Setup error handling 13 | $ErrorActionPreference = 'Stop'; 14 | Set-StrictMode -Version latest; 15 | 16 | # Setup tests paths 17 | $rootPath = $PWD; 18 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 19 | $here = (Resolve-Path $PSScriptRoot).Path; 20 | } 21 | Describe 'PSDocs -- Title keyword' -Tag Title { 22 | 23 | 24 | Context 'Markdown' { 25 | BeforeAll { 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 27 | $testObject = [PSCustomObject]@{ 28 | Name = 'TestObject' 29 | } 30 | 31 | $invokeParams = @{ 32 | Path = $docFilePath 33 | InputObject = $testObject 34 | PassThru = $True 35 | ErrorAction = [System.Management.Automation.ActionPreference]::Stop 36 | } 37 | } 38 | It 'With single title' { 39 | $result = Invoke-PSDocument @invokeParams -Name 'SingleTitle'; 40 | $result | Should -Match "^(`# Test title(\r|\n|\r\n))"; 41 | } 42 | It 'With multiple titles' { 43 | $result = Invoke-PSDocument @invokeParams -Name 'MultipleTitle'; 44 | $result | Should -Match "^(`# Title 2(\r|\n|\r\n))"; 45 | } 46 | It 'With empty title' { 47 | $Null = Invoke-PSDocument @invokeParams -Name 'EmptyTitle' -WarningAction SilentlyContinue -WarningVariable outWarnings; 48 | $outWarnings = @($outWarnings); 49 | $outWarnings | Should -Not -BeNullOrEmpty; 50 | $outWarnings.Length | Should -Be 2; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/PSDocs.Warning.Tests.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # 5 | # Unit tests for the Warning keyword 6 | # 7 | 8 | [CmdletBinding()] 9 | param () 10 | 11 | BeforeAll{ 12 | # Setup error handling 13 | $ErrorActionPreference = 'Stop'; 14 | Set-StrictMode -Version latest; 15 | 16 | # Setup tests paths 17 | $rootPath = $PWD; 18 | Import-Module (Join-Path -Path $rootPath -ChildPath out/modules/PSDocs) -Force; 19 | $here = (Resolve-Path $PSScriptRoot).Path; 20 | } 21 | Describe 'PSDocs -- Warning keyword' -Tag Warning { 22 | 23 | 24 | Context 'Markdown' { 25 | BeforeAll{ 26 | $docFilePath = Join-Path -Path $here -ChildPath 'FromFile.Keyword.Doc.ps1'; 27 | $testObject = [PSCustomObject]@{ 28 | Name = 'TestObject' 29 | } 30 | $invokeParams = @{ 31 | Path = $docFilePath 32 | InputObject = $testObject 33 | PassThru = $True 34 | } 35 | } 36 | It 'Should handle single line input' { 37 | $result = Invoke-PSDocument @invokeParams -Name 'WarningSingleMarkdown'; 38 | $result | Should -Match '\> \[\!WARNING\](\r|\n|\r\n)> This is a single line'; 39 | } 40 | It 'Should handle multiline input' { 41 | $result = Invoke-PSDocument @invokeParams -Name 'WarningMultiMarkdown'; 42 | $result | Should -Match '\> \[\!WARNING\](\r|\n|\r\n)> This is the first line\.(\r|\n|\r\n)> This is the second line\.'; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/StringContentTests.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using PSDocs.Runtime; 5 | 6 | namespace PSDocs 7 | { 8 | public sealed class StringContentTests 9 | { 10 | [Fact] 11 | public void ReadLines() 12 | { 13 | var lines = new StringContent(GetTestString1()).ReadLines(); 14 | Assert.Equal("Line 1", lines[0]); 15 | Assert.Equal("Line 2", lines[1]); 16 | Assert.Equal("Line 3", lines[2]); 17 | 18 | lines = new StringContent(GetTestString2()).ReadLines(); 19 | Assert.Equal("Line1", lines[0]); 20 | Assert.Equal("Line2", lines[1]); 21 | Assert.Equal(" Line3", lines[2]); 22 | 23 | lines = new StringContent(GetTestString3()).ReadLines(); 24 | Assert.Equal("Line 1", lines[0]); 25 | Assert.Equal("Line 2", lines[1]); 26 | Assert.Equal(string.Empty, lines[2]); 27 | Assert.Equal("Line 3", lines[3]); 28 | } 29 | 30 | private static string GetTestString1() 31 | { 32 | return @" 33 | Line 1 34 | Line 2 35 | Line 3 36 | "; 37 | } 38 | 39 | private static string GetTestString2() 40 | { 41 | return @" 42 | Line1 43 | Line2 44 | Line3 45 | "; 46 | } 47 | 48 | private static string GetTestString3() 49 | { 50 | return @" 51 | Line 1 52 | Line 2 53 | 54 | Line 3 55 | "; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/docs/Selector.Doc.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | --- 5 | # Synopsis: A selector for unit testing. 6 | apiVersion: github.com/microsoft/PSDocs/v1 7 | kind: Selector 8 | metadata: 9 | name: AlwaysTrue 10 | spec: 11 | if: 12 | field: '__not_value__' 13 | exists: false 14 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/docs/Test.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | Document 'TestDocument1' { 5 | "Culture=$($LocalizedData.Culture)"; 6 | } 7 | 8 | Document 'TestDocument2' -With 'AlwaysTrue' { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/en-AU/PSDocs-strings.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Culture = 'en-AU' 6 | } 7 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/en-US/PSDocs-strings.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Culture = 'en-US' 6 | } 7 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/en-ZZ/PSDocs-strings.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | # No strings 6 | } 7 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/TestModule/en/PSDocs-strings.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Culture = 'en' 6 | } 7 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | global using Xunit; 5 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/WithError.Doc.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | document 'InvalidCommand' { 5 | New-PSDocsInvalidCommand; 6 | } 7 | 8 | document 'InvalidCommandWithSection' { 9 | Section 'Invalid' { 10 | New-PSDocsInvalidCommand; 11 | } 12 | } 13 | 14 | document 'WithWriteError' { 15 | Write-Error -Message 'Verify Write-Error is raised as an exception'; 16 | } 17 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/en-AU/IncludeFile3.md: -------------------------------------------------------------------------------- 1 | This is en-AU. -------------------------------------------------------------------------------- /tests/PSDocs.Tests/en-US/IncludeFile3.md: -------------------------------------------------------------------------------- 1 | This is en-US. -------------------------------------------------------------------------------- /tests/PSDocs.Tests/en-US/PSDocs-strings.psd1: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | @{ 5 | Key1 = 'Value1' 6 | } 7 | -------------------------------------------------------------------------------- /tests/PSDocs.Tests/psdocs.yml: -------------------------------------------------------------------------------- 1 | 2 | generator: PSDocs 3 | --------------------------------------------------------------------------------