diff --git a/.docsettings.yml b/.docsettings.yml index dc9204a94ad6..93673850fa87 100644 --- a/.docsettings.yml +++ b/.docsettings.yml @@ -2,6 +2,8 @@ omitted_paths: - documentation/ServicePrincipal/* - eng/tools/analyze-deps/** - eng/tools/select-packages/** + - eng/tools/generate-doc/** + - eng/tools/generate-static-index/** - "sdk/*/arm-*" - "sdk/cognitiveservices/*" - "sdk/identity/identity/test/manual/*" diff --git a/common/scripts/generate-doc.js b/common/scripts/generate-doc.js deleted file mode 100644 index 7ec513f08894..000000000000 --- a/common/scripts/generate-doc.js +++ /dev/null @@ -1,278 +0,0 @@ -const fs = require("fs-extra"); -const path = require("path"); -const childProcess = require("child_process"); -const nunjucks = require("nunjucks"); - -nunjucks.configure("documentation/templateDocGen", { autoescape: true }); - -/* Traversing the directory */ -function walk(dir, checks) { - var list = fs.readdirSync(dir); - for (const fileName of list) { - const filePath = path.join(dir, fileName); - if (fileName == "node_modules") { - checks.isRush = true; - continue; - } - if (fileName == "src") { - checks.srcPresent = true; - } - if (fileName == "package.json") { - let data = fs.readFileSync(filePath, "utf8"); - let settings = JSON.parse(data); - if (settings["private"] === true) { - checks.isPrivate = true; - } - } - if (fileName == "typedoc.json") { - checks.typedocPresent = true; - } - const stat = fs.statSync(filePath); - if (stat && stat.isDirectory()) { - checks = walk(filePath, checks); - } - } - return checks; -} - -/* Checking if a package exists in the exclusion/ inclusion list */ -function isPackageInArray(package, inputArray) { - for (var i in inputArray) { - if (inputArray[i] == package) { - return true; - } - } - return false; -} - -/* Input arguments to the script */ -var argv = require("yargs") - .options({ - docGenOutput: { - alias: "dgOp", - type: "string", - choices: ["dg", "local"], - describe: - "If value = dg, generate the docs in root/docGen folder, else generated under dist/docs/ of local package", - demandOption: true - }, - includeMode: { - alias: "i", - type: "string", - describe: - "select whether there is inclusion mode, exclusion mode or neither", - choices: ["inc", "exc", "none"], - demandOption: true - }, - include: { - alias: "inc", - type: "array", - describe: - "inclusion list of packages for which the docs should be generated. The index template html is not created in this mode." - }, - exclude: { - alias: "exc", - type: "array", - describe: - "exclusion list for packages for which the docs should be NOT generated.These packages will be added to index template html generated." - } - }) - .demandOption( - ["docGenOutput", "includeMode"], - "Please provide both docGen and includeMode arguments to work with this tool" - ) - .help().argv; - -/* Variables for inclusion or exclusion package lists */ -let exclusionList = []; -let inclusionList = []; - -/* Generate index html from the template by default */ -let generateIndexWithTemplate = true; -if((argv.dgOp === "local") && (argv.includeMode !== "none")){ - console.error(`--includeMode "inc" or "exc" is supported only when the documentGenoutput is set to "dg" instead of "local"!!`); - process.exit(1); -} - -if (argv.includeMode === "inc") { - generateIndexWithTemplate = false; - if (argv.include !== undefined) { - inclusionList = argv.include; - } else { - console.error(`--includeMode "inc" requires the inclusion list --inc to be passed as an argument!!`); - process.exit(1); - } -} -else if(argv.includeMode === "exc"){ - if(argv.exclude !== undefined) { - exclusionList = argv.exclude; - } else { - console.error(`--excludeMode "exc" requires the exclusion list --exc to be passed as an argument!!`); - process.exit(1); - } -} -else if((argv.includeMode === "none")){ - generateIndexWithTemplate = false; -} - -let docOutputFolder = "--out ./dist/docs ./src"; - -console.log("process.cwd = " + process.cwd()); -try { - const result = childProcess.spawnSync("rush", ["install"], { - cwd: process.cwd(), - env: process.env, - shell: true - }); - console.log('result.output for "rush install":' + result.output); -} catch (e) { - console.error(`\n\n${e.toString()}\n\n`); - process.exit(1); -} - -let workingDir = path.join(process.cwd(), "sdk"); -let pathToAssets = ""; - -const serviceFolders = fs.readdirSync(workingDir); - -/* Initializing package list for template index generation */ -let serviceList = []; -let count = 0; -for (const eachService of serviceFolders) { - count++; - console.log("count = " + count); - const eachServicePath = path.join(workingDir, eachService); - const stat = fs.statSync(eachServicePath); - - if (stat && stat.isDirectory()) { - var packageList = fs.readdirSync(eachServicePath); - - /* Initializing package list for template index generation */ - let indexPackageList = []; - for (const eachPackage of packageList) { - if ((argv.includeMode === "inc" && isPackageInArray(eachPackage, inclusionList)) || !(argv.includeMode === "inc")) { - let checks = { - isRush: false, - isPrivate: false, - srcPresent: false, - typedocPresent: false - }; - console.log( - "checks before walk: checks.isRush = " + checks.isRush + - " , checks.isPrivate = " + checks.isPrivate + - ", checks.srcPresent = " + checks.srcPresent + - ", typedocPresent = " + checks.typedocPresent - ); - eachPackagePath = path.join(eachServicePath, eachPackage); - pathToAssets = eachPackagePath + "/assets"; - const packageStat = fs.statSync(eachPackagePath); - if (packageStat && packageStat.isDirectory()) { - checks = walk(eachPackagePath, checks); - - console.log( - "checks after walk: checks.isRush = " + checks.isRush + - " , checks.isPrivate = " + checks.isPrivate + - ", checks.srcPresent = " + checks.srcPresent + - ", typedocPresent = " + checks.typedocPresent - ); - console.log("Path: " + eachPackagePath); - if (!checks.isPrivate) { - if (checks.srcPresent) { - if (!checks.isRush) { - try { - const npmResult = childProcess.spawnSync("npm", ["install"], { - stdio: "inherit", - cwd: eachPackagePath, - shell: true - }); - console.log('npmResult.output for "npm install":' + npmResult.output); - } catch (e) { - console.error(`\n\n${e.toString()}\n\n`); - process.exit(1); - } - } - if (argv.docGenOutput === "dg") { - docOutputFolder = "--out ../../../docGen/" + eachPackage + " ./src"; - } - - try { - if (!isPackageInArray(eachPackage, exclusionList)) { - if (checks.typedocPresent) { - const typedocResult = childProcess.spawnSync( - "typedoc", - [docOutputFolder, - "--ignoreCompilerErrors"], - { - cwd: eachPackagePath, - shell: true - } - ); - console.log('typedocResult.output for "typedoc ' + docOutputFolder +' ":' + typedocResult.output); - } else { - const typedocResult = childProcess.spawnSync( - "typedoc", - [ - "--excludePrivate", - "--excludeNotExported", - '--exclude "node_modules/**/*"', - "--ignoreCompilerErrors", - "--mode file", - docOutputFolder - ], - { - cwd: eachPackagePath, - shell: true - } - ); - console.log( - 'typedocResult.output for "typedoc --excludePrivate --excludeNotExported --exclude "node_modules/**/*" -ignoreCompilerErrors --mode file ' + docOutputFolder + ' ":' + typedocResult.output); - } - } else { - console.log("... NOT RUNNING TYPEDOC on excluded package " + eachPackage); - } - } catch (e) { - console.error(`\n\n${e.toString()}\n\n`); - process.exit(1); - } - if (generateIndexWithTemplate) { - /* Adding package to packageList for the template index generation */ - indexPackageList.push(eachPackage); - } - } else { - console.log("...SKIPPING Since src folder could not be found....."); - } - } else { - console.log("...SKIPPING Since package marked as private..."); - } - } - } - } //end-for each-package - /* Adding service entry for the template index generation */ - serviceList.push({ name: eachService, packageList: indexPackageList }); - } -} // end-for ServiceFolders -console.log("generateIndexWithTemplate=" + generateIndexWithTemplate); -if (generateIndexWithTemplate) { - var renderedIndex = nunjucks.render("template.html", { - serviceList: serviceList - }); - - var dest = process.cwd() + "/docGen/index.html"; - fs.writeFile(dest, renderedIndex, function(err, result) { - if (err) - console.log("error in writing the generated html to docGen/index.html", err); - console.log("Generated html written to docGen/index.html"); - }); - - console.log("serviceList length = " + serviceList.length); - if (serviceList.length > 0) { - /* Copy from pathToAssets to docGen/assets */ - pathToAssets = process.cwd() + "/docGen/" + serviceList[0].packageList[0] + "/assets"; - var assetsDest = process.cwd() + "/docGen/assets/"; - fs.copy(pathToAssets, assetsDest, err => { - if (err) - return console.error("error copying the assets folder to docGen/assets/",err); - console.log("assets folder copied to docGen!"); - }); - } -} diff --git a/eng/pipelines/doc-index.yml b/eng/pipelines/doc-index.yml new file mode 100644 index 000000000000..ef87d66c8f13 --- /dev/null +++ b/eng/pipelines/doc-index.yml @@ -0,0 +1,60 @@ +jobs: + - job: "Build" + variables: + - template: templates/variables/globals.yml + pool: + vmImage: "windows-2019" + steps: + - pwsh: | + Invoke-WebRequest -Uri "https://github.com/dotnet/docfx/releases/download/v2.43.2/docfx.zip" ` + -OutFile "docfx.zip" | Wait-Process; Expand-Archive -Path "docfx.zip" -DestinationPath "./docfx/" + workingDirectory: $(Build.BinariesDirectory) + displayName: Download and Extract DocFX + + - pwsh: | + $(Build.BinariesDirectory)/docfx/docfx.exe init -q + displayName: Provision DocFX Directory + workingDirectory: $(Build.SourcesDirectory) + + - pwsh: | + mkdir "templates" + displayName: Create Template Directory + workingDirectory: $(Build.SourcesDirectory)/docfx_project/ + + - pwsh: | + Copy-Item "$(Build.SourcesDirectory)/eng/tools/generate-static-index/static-files/docfx.json" -Destination "$(Build.SourcesDirectory)/docfx_project/" -Force + displayName: Copy over docfx.json + + - script: | + npm install + workingDirectory: $(System.DefaultWorkingDirectory)/eng/tools/generate-static-index + displayName: "Install tool dependencies" + + - pwsh: | + node $(Build.SourcesDirectory)/eng/tools/generate-static-index/index.js + displayName: "Generate Index Toc" + + - pwsh: | + New-Item -Path "$(Build.SourcesDirectory)/docfx_project" -Name "toc.yml" -Force + Add-Content -Path "$(Build.SourcesDirectory)/docfx_project/toc.yml" -Value "- name: Azure SDK for JavaScript APIs`r`n href: api/`r`n homepage: api/index.md" + Copy-Item "$(Build.SourcesDirectory)/README.md" -Destination "$(Build.SourcesDirectory)/docfx_project/api/index.md" -Force + Copy-Item "$(Build.SourcesDirectory)/README.md" -Destination "$(Build.SourcesDirectory)/docfx_project/index.md" -Force + displayName: Update toc.yml and index + + - pwsh: | + $(Build.BinariesDirectory)/docfx/docfx.exe build + displayName: Build Doc Content + workingDirectory: $(Build.SourcesDirectory)/docfx_project/ + + - pwsh: | + Copy-Item "$(Build.SourcesDirectory)/eng/tools/generate-static-index/static-files/assets/*" -Destination "$(Build.SourcesDirectory)/docfx_project/_site/" -Force + Get-Content "$(Build.SourcesDirectory)/eng/tools/generate-static-index/static-files/main.js" |Out-File "$(Build.SourcesDirectory)/docfx_project/_site/styles/main.js" + Get-Content "$(Build.SourcesDirectory)/eng/tools/generate-static-index/static-files/docfx.css" |Out-File "$(Build.SourcesDirectory)/docfx_project/_site/styles/docfx.css" + Copy-Item "$(Build.SourcesDirectory)/docfx_project/*" -Destination "$(Build.ArtifactStagingDirectory)/docfx_project/" -Recursive -Force + displayName: Replace site assets and Copy HTML to Artifacts Directory + + - task: PublishPipelineArtifact@0 + condition: succeeded() + inputs: + artifactName: "Doc.Index" + targetPath: $(Build.ArtifactStagingDirectory)/docfx_project/_site diff --git a/eng/pipelines/docs.yml b/eng/pipelines/docs.yml index 78cb8211b822..2400bb3a2483 100644 --- a/eng/pipelines/docs.yml +++ b/eng/pipelines/docs.yml @@ -1,5 +1,5 @@ #Pipeline variables: -# $(additionalArgs) eg : -i "exc" --exc arm-advisor arm-apimanagement OR -i "inc" --inc arm-advisor arm-apimanagement +# $(additionalArgs) eg : -i "exc" --exc advisor apimanagement OR -i "inc" --inc advisor apimanagement storage OR -i "inc" --inc eventhub --clientOnly trigger: - master @@ -34,20 +34,13 @@ jobs: displayName: "Install typedoc" - script: | - npm install nunjucks - displayName: "Install nunjucks" - - - script: | - npm install fs-extra - displayName: "Install fs-extra" - - - script: | - npm install yargs - displayName: "Install yargs" + npm install + workingDirectory: $(System.DefaultWorkingDirectory)/eng/tools/generate-doc + displayName: "Install tool dependencies" - pwsh: | cd $(Build.SourcesDirectory) - node .\common\scripts\generate-doc.js --dgOp "dg" $(additionalArgs) + node .\eng\tools\generate-doc\index.js --dgOp "dg" $(additionalArgs) Copy-Item -Path $(Build.SourcesDirectory)/docGen/* -Destination $(Build.ArtifactStagingDirectory) -Recurse -Force displayName: "Generate Typedoc Docs" diff --git a/eng/pipelines/templates/jobs/archetype-sdk-client.yml b/eng/pipelines/templates/jobs/archetype-sdk-client.yml index bea071f86908..85bef349f5f4 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-client.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-client.yml @@ -1,5 +1,7 @@ parameters: RunUnitTests: true + Artifacts: [] + ServiceDirectory: not-specified Matrix: Linux_Node8: OSVmImage: "ubuntu-16.04" @@ -49,6 +51,13 @@ jobs: node common/scripts/install-run-rush.js install displayName: "Install dependencies" + - template: eng/pipelines/templates/scripts/replace-relative-links.yml@azure-sdk-tools + parameters: + TargetFolder: $(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}} + RootFolder: $(Build.SourcesDirectory) + BuildSHA: $(Build.SourceVersion) + RepoId: "Azure/azure-sdk-for-js" + - script: | node eng/tools/rush-runner.js build "${{parameters.ServiceDirectory}}" --verbose displayName: "Build libraries" @@ -76,6 +85,32 @@ jobs: artifactName: packages path: $(Build.ArtifactStagingDirectory) + - script: | + npm i -g typedoc + displayName: "Install typedoc" + + - script: | + npm install + workingDirectory: $(System.DefaultWorkingDirectory)/eng/tools/generate-doc + displayName: "Install tool dependencies" + + - pwsh: | + $docDirectory = "${{parameters.ServiceDirectory}}" + if ($docDirectory -eq '*') { $docDirectory = "core" } + node $(Build.SourcesDirectory)/eng/tools/generate-doc/index.js --dgOp "dg" -i "inc" --inc "$docDirectory" + displayName: "Run Typedoc Docs" + + - pwsh: | + $(Build.SourcesDirectory)/eng/tools/compress-subfolders.ps1 "$(Build.SourcesDirectory)/docGen" "$(Build.ArtifactStagingDirectory)/Documentation" + displayName: "Generate Typedoc Docs" + + - task: PublishPipelineArtifact@1 + condition: succeededOrFailed() + displayName: "Publish artifacts" + inputs: + artifactName: documentation + path: $(Build.ArtifactStagingDirectory)/Documentation + - job: "Analyze" dependsOn: "Build" variables: @@ -107,7 +142,8 @@ jobs: targetPath: "$(Agent.TempDirectory)/packagesMaster" displayName: "Download Latest Master (PipelineTask) artifacts" - - pwsh: eng/tools/compare-packages.ps1 "$(Agent.TempDirectory)/packagesMaster" "$(Agent.TempDirectory)/packagesCurrent" "$(Build.BuildNumber)" "$(System.ArtifactsDirectory)" + - pwsh: | + eng/tools/compare-packages.ps1 "$(Agent.TempDirectory)/packagesMaster" "$(Agent.TempDirectory)/packagesCurrent" "$(Build.BuildNumber)" "$(System.ArtifactsDirectory)" displayName: "Diff Generated Packages" errorActionPreference: "continue" diff --git a/eng/tools/compress-subfolders.ps1 b/eng/tools/compress-subfolders.ps1 new file mode 100644 index 000000000000..4417024d9fe9 --- /dev/null +++ b/eng/tools/compress-subfolders.ps1 @@ -0,0 +1,19 @@ +param ( + $pathToDir, + $pathToDest +) +if((-Not (Test-Path $pathToDir))){ + mkdir $pathToDir +} +$source = Get-ChildItem -Path $pathToDir -Directory +Write-Host "source = $source" + +if((-Not (Test-Path $pathToDest))){ + mkdir $pathToDest +} + +Foreach ($s in $source){ + $destination = Join-path -path $pathToDest -ChildPath "$($s.name).zip" + Write-Host "destination = $destination" + Compress-Archive -Path $s.fullname -DestinationPath $destination +} diff --git a/eng/tools/generate-doc/index.js b/eng/tools/generate-doc/index.js new file mode 100644 index 000000000000..5ccf404e6a03 --- /dev/null +++ b/eng/tools/generate-doc/index.js @@ -0,0 +1,339 @@ +const fs = require("fs-extra"); +const path = require("path"); +const util = require("util"); +const childProcess = require("child_process"); +const nunjucks = require("nunjucks"); +const readFile = util.promisify(fs.readFile); +const readDir = util.promisify(fs.readdir); +const statFile = util.promisify(fs.stat); +const pLimit = require('p-limit'); + +nunjucks.configure("documentation/templateDocGen", { autoescape: true }); + +/* Traversing the directory */ +const walk = async (dir, checks) => { + checks = await walkRecurse(dir, checks, 0); + return checks; +}; + +const walkRecurse = async (dir, checks, depth) => { + if (depth > 0) return checks; + var list = await readDir(dir); + for (const fileName of list) { + const filePath = path.join(dir, fileName); + if (fileName == "node_modules") { + continue; + } + if (fileName == "src") { + checks.srcPresent = true; + } + if (fileName == "package.json") { + let data = await readFile(filePath, "utf8"); + let settings = JSON.parse(data); + if (settings["private"] === true) { + checks.isPrivate = true; + } + if (settings["sdk-type"] === "client") { + checks.isClient = true; + } + checks.version = settings["version"]; + } + if (fileName == "typedoc.json") { + checks.typedocPresent = true; + } + const stat = await statFile(filePath); + if (stat && stat.isDirectory()) { + checks = await walkRecurse(filePath, checks, depth + 1); + } + } + return checks; +}; + +//Old Method Index +const generateOldIndex = serviceList => { + console.log("generateIndexWithTemplate=" + generateIndexWithTemplate); + if (generateIndexWithTemplate) { + var renderedIndex = nunjucks.render("template.html", { + serviceList: serviceList + }); + + var dest = process.cwd() + "/docGen/index.html"; + fs.writeFile(dest, renderedIndex, function(err, result) { + if (err) + console.log( + "error in writing the generated html to docGen/index.html", + err + ); + console.log("Generated html written to docGen/index.html"); + }); + + console.log("serviceList length = " + serviceList.length); + if (serviceList.length > 0) { + /* Copy from pathToAssets to docGen/assets */ + pathToAssets = + process.cwd() + "/docGen/" + serviceList[0].packageList[0] + "/assets"; + var assetsDest = process.cwd() + "/docGen/assets/"; + fs.copy(pathToAssets, assetsDest, err => { + if (err) + return console.error( + "error copying the assets folder to docGen/assets/", + err + ); + console.log("assets folder copied to docGen!"); + }); + } + } +}; + +const executeTypedoc = async ( + exclusionList, + inclusionList, + generateIndexWithTemplate +) => { + console.log("inside executeTypedoc"); + let docOutputFolder = "--out ./dist/docs ./src"; + console.log("process.cwd = " + process.cwd()); + let workingDir = path.join(process.cwd(), "sdk"); + let pathToAssets = ""; + const serviceFolders = await readDir(workingDir); + + /* Initializing package list for template index generation */ + let serviceList = []; + let promises = []; + let commandList = []; + for (const eachService of serviceFolders) { + if ( + (argv.includeMode === "inc" && inclusionList.includes(eachService)) || + (argv.includeMode === "exc" && !exclusionList.includes(eachService)) || + (argv.includeMode === "inc" && argv.include[0] === "*") + ) { + const eachServicePath = path.join(workingDir, eachService); + const stat = await statFile(eachServicePath); + + if (stat && stat.isDirectory()) { + var packageList = await readDir(eachServicePath); + + /* Initializing package list for template index generation */ + let indexPackageList = []; + for (var eachPackage of packageList) { + let checks = { + isPrivate: false, + srcPresent: false, + typedocPresent: false, + isClient: false, + version: "0" + }; + eachPackagePath = path.join(eachServicePath, eachPackage); + pathToAssets = eachPackagePath + "/assets"; + const packageStat = await statFile(eachPackagePath); + if (packageStat && packageStat.isDirectory()) { + checks = await walk(eachPackagePath, checks); + + console.log( + "checks after walk: checks.isPrivate = " + + checks.isPrivate + + ", checks.srcPresent = " + + checks.srcPresent + + ", typedocPresent = " + + checks.typedocPresent + + ", isClient = " + + checks.isClient + + ", version = " + + checks.version + ); + console.log("Path: " + eachPackagePath); + if (!checks.isPrivate) { + if ((argv.clientOnly && checks.isClient) || !argv.clientOnly) { + if (checks.srcPresent) { + if (argv.docGenOutput === "dg") { + docOutputFolder = + "--out ../../../docGen/" + + eachPackage + + "/" + + checks.version + + " ./src"; + } + + let typedocProcess; + let commandRun = []; + commandRun.push("typedoc"); + commandRun.push({ + cwd: eachPackagePath, + shell: true + }); + if (checks.typedocPresent) { + commandRun.push([ + docOutputFolder, + '--theme "../../../eng/tools/generate-doc/theme/default"', + "--ignoreCompilerErrors" + ]); + } else { + commandRun.push([ + '--theme "../../../eng/tools/generate-doc/theme/default"', + "--excludePrivate", + "--excludeNotExported", + '--exclude "node_modules/**/*"', + "--ignoreCompilerErrors", + "--mode file", + docOutputFolder + ]); + } + commandList.push(commandRun); + if (generateIndexWithTemplate) { + /* Adding package to packageList for the template index generation */ + indexPackageList.push(eachPackage); + } + } else { + console.log( + "...SKIPPING Since src folder could not be found....." + ); + } + } else { + //console.log("...SKIPPING Since package is either not sdkType client"); + } + } else { + console.log("...SKIPPING Since package marked as private..."); + } + } + } //end-for each-package + + /* Adding service entry for the template index generation */ + serviceList.push({ name: eachService, packageList: indexPackageList }); + } + } else { + //console.log("...SKIPPING Since service doesn't satisfy one of the 3 condition checks..."); + } + } // end-for ServiceFolders + + + var plimitPromises = []; + const limit = pLimit(20); + for (const commandRun of commandList) { + const promise = limit(()=> new Promise(async (res, rej) => { + + let typedocProcess = childProcess.spawn(commandRun[0], commandRun[2], commandRun[1]); + let stdOut = ""; + let stdErr = ""; + typedocProcess.on("close", code => { + res({ code, stdOut, stdErr }) + }); + + typedocProcess.stdout.on("data", data => (stdOut = stdOut + data.toString())); + typedocProcess.stderr.on("data", data => (stdErr = stdErr + data.toString())); + + })); + plimitPromises.push(promise); + } + try { + const results = await Promise.all(plimitPromises); + for (let item of results) { + console.log(item.stdOut); + if (item.stdErr) { + console.error(item.stdErr); + } + if (item.code !== 0) { + console.error("Process Failed"); + process.exitCode = 1; + } + } + } catch (ex) { + console.log("ERROR", ex); + } + + console.log("All done!"); + if (argv.oldIndex) generateOldIndex(serviceList); +}; + +/* Input arguments to the script */ +var argv = require("yargs") + .options({ + docGenOutput: { + alias: "dgOp", + type: "string", + choices: ["dg", "local"], + describe: + "If value = dg, generate the docs in root/docGen folder, else generated under dist/docs/ of local package", + demandOption: true + }, + includeMode: { + alias: "i", + type: "string", + describe: + "select whether there is inclusion mode, exclusion mode or neither", + choices: ["inc", "exc", "none"], + demandOption: true + }, + include: { + alias: "inc", + type: "array", + describe: + "inclusion list of packages for which the docs should be generated. The index template html is not created in this mode." + }, + exclude: { + alias: "exc", + type: "array", + describe: + "exclusion list for packages for which the docs should be NOT generated.These packages will be added to index template html generated." + }, + clientOnly: { + type: "boolean", + default: false, + demandOption: true + }, + oldIndex: { + type: "boolean", + default: false, + demandOption: true + } + }) + .demandOption( + ["docGenOutput", "includeMode", "clientOnly"], + "Please provide both docGen, includeMode and clientOnly arguments to work with this tool" + ) + .help().argv; + +console.log("Argv.clientOnly = " + argv.clientOnly); +/* Variables for inclusion or exclusion package lists */ +let exclusionList = []; +let inclusionList = []; +let generateIndexWithTemplate = true; /* Generate index html from the template by default */ + +if (argv.dgOp === "local" && argv.includeMode !== "none") { + console.error( + `--includeMode "inc" or "exc" is supported only when the documentGenoutput is set to "dg" instead of "local"!!` + ); + process.exit(1); +} +console.log("arv.include = " + argv.include); + +if (argv.includeMode === "inc") { + if (argv.oldIndex) { + generateIndexWithTemplate = false; + } + if (argv.include !== undefined) { + inclusionList = argv.include; + if (inclusionList.includes("not-specified")) { + console.error( + `One or more value to the input package list is "not-specified"` + ); + process.exit(1); + } + } else { + console.error( + `--includeMode "inc" requires the inclusion list --inc to be passed as an argument!!` + ); + process.exit(1); + } +} else if (argv.includeMode === "exc") { + if (argv.exclude !== undefined) { + exclusionList = argv.exclude; + } else { + console.error( + `--excludeMode "exc" requires the exclusion list --exc to be passed as an argument!!` + ); + process.exit(1); + } +} else if (argv.includeMode === "none") { + generateIndexWithTemplate = false; +} +executeTypedoc(exclusionList, inclusionList, generateIndexWithTemplate); diff --git a/eng/tools/generate-doc/package.json b/eng/tools/generate-doc/package.json new file mode 100644 index 000000000000..95165da55e54 --- /dev/null +++ b/eng/tools/generate-doc/package.json @@ -0,0 +1,19 @@ +{ + "name": "generate-doc", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "generate-doc": "node index.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "fs-extra": "^8.1.0", + "js-yaml": "^3.13.1", + "nunjucks": "^3.2.0", + "typedoc": "^0.15.0", + "yargs": "^11.1.0", + "p-limit": "^2.2.1" + } +} diff --git a/eng/tools/generate-doc/theme/default/assets/css/custom.css b/eng/tools/generate-doc/theme/default/assets/css/custom.css new file mode 100644 index 000000000000..a4ad4c12346a --- /dev/null +++ b/eng/tools/generate-doc/theme/default/assets/css/custom.css @@ -0,0 +1 @@ +h1 { line-height: normal; } diff --git a/eng/tools/generate-doc/theme/default/assets/js/get_options.js b/eng/tools/generate-doc/theme/default/assets/js/get_options.js new file mode 100644 index 000000000000..90013faef080 --- /dev/null +++ b/eng/tools/generate-doc/theme/default/assets/js/get_options.js @@ -0,0 +1,144 @@ +WINDOW_CONTENTS = window.location.href.split("/"); + +function currentVersion() { + if (WINDOW_CONTENTS.includes("$web") && WINDOW_CONTENTS.length > 5) { + return WINDOW_CONTENTS[6]; + } else { + return ""; + } +} + +function currentPackage() { + if (WINDOW_CONTENTS.includes("$web") && WINDOW_CONTENTS.length > 5) { + return WINDOW_CONTENTS[5]; + } else { + return ""; + } +} + +function httpGetAsync(targetUrl, callback) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState == 4 && xmlHttp.status == 200) + callback(xmlHttp.responseText); + }; + xmlHttp.open("GET", targetUrl, true); // true for asynchronous + xmlHttp.send(null); +} + +function showSelectors(selectors) { + selectors.forEach(function(item, index) { + $(item).show(); + }); +} + +function hideSelectors(selectors) { + selectors.forEach(function(item, index) { + $(item).hide(); + }); +} + +function populateOptions(optionSelector, otherSelectors) { + if (currentPackage()) { + var versionRequestUrl = + "https://azuresdkdocs.blob.core.windows.net/$web?restype=container&comp=list&prefix=" + + SELECTED_LANGUAGE + + "/" + + currentPackage() + + "/versions/"; + + httpGetAsync(versionRequestUrl, function(responseText) { + if (responseText) { + data_stored = responseText; + + parser = new DOMParser(); + xmlDoc = parser.parseFromString(responseText, "text/xml"); + + nameElements = Array.from(xmlDoc.getElementsByTagName("Name")); + options = []; + + for (var i in nameElements) { + options.push(nameElements[i].textContent.split("/")[3]); + } + + populateVersionDropDown(optionSelector, options); + showSelectors(otherSelectors); + + $(optionSelector).change(function() { + targetVersion = $(this).val(); + + url = WINDOW_CONTENTS.slice(); + url[6] = targetVersion; + window.location.href = url.join("/"); + }); + } + }); + } +} + +function populateVersionDropDown(selector, values) { + var select = $(selector); + + $("option", select).remove(); + + $.each(values, function(index, text) { + $("