{"version":3,"sources":["../../../packages/tools/gulp-ps-code/index.ts"],"names":[],"mappings":"","file":"index.d.ts","sourcesContent":["/* eslint-disable no-console */\r\n'use strict';\r\n\r\nimport Path from 'path';\r\nimport pluginError from 'plugin-error';\r\nimport through2 from 'through2';\r\nimport Vinyl from 'vinyl';\r\nimport * as util from '../utilities';\r\nimport * as PsCode from './ps-code-convert';\r\nimport { Group, Groups, PsCodeConverter } from './ps-code-convert';\r\n\r\nconst PLUGIN_NAME = 'gulp-ps-code';\r\n\r\n/**\r\n * Plugin level function\r\n */\r\nfunction gulpPsCode(options: PsCode.Options) {\r\n    //\r\n    // (Options):\r\n    //\r\n    // name of PowerShell module. (required)\r\n    //  powerShellModuleName: string\r\n    //\r\n    // name of generated file.\r\n    //   name: string; default is 'powershell-script.ts'\r\n    //\r\n    // base path of powershell files.\r\n    //   basePath: string; default is 'src/resources/scripts'\r\n    //\r\n    // generated path of powershell files.\r\n    //   generatedPath: string; default is 'src/generated/scripts'\r\n    //\r\n    // remove comments at default.\r\n    //   noComments: boolean;\r\n    //\r\n    // validate content of PowerShell script.\r\n    //   - non-ascii code, no comments, unsupported script file name and unofficial verb\r\n    //   validate: boolean;\r\n    //\r\n    // JEA data object format.\r\n    //  jea: boolean\r\n    //\r\n    // name of resource root. the key name of resjson.\r\n    //  resourceName: string\r\n    //\r\n\r\n    // override options settings if not specified.\r\n    options = Object.assign(\r\n        <PsCode.Options>{\r\n            name: 'powershell-scripts.ts',\r\n            basePath: 'src\\\\resources\\\\scripts',\r\n            generatedPath: 'src\\\\generated\\\\scripts',\r\n            noComments: true,\r\n            validate: true,\r\n            jea: true\r\n        },\r\n        options || {});\r\n\r\n    const groups: Groups = {};\r\n    const collection = {};\r\n    let lastFile = null;\r\n\r\n    return through2.obj(\r\n        function (file, enc, cb) {\r\n            let error = null;\r\n            try {\r\n                if (!options.powerShellModuleName) {\r\n                    throw new Error('gulp-ps-code requires powerShellModuleName option.');\r\n                }\r\n\r\n                const path = Path.parse(file.path);\r\n                if (path.ext === '.ps1') {\r\n                    let relPath: string = file.path.substring(file.cwd.length + 1);\r\n                    relPath = relPath.substring(0, relPath.length - path.base.length - 1);\r\n                    let groupName = PsCodeConverter.defaultGroup;\r\n                    if (relPath === options.generatedPath) {\r\n                        groupName = PsCodeConverter.generatedGroup;\r\n                    } else {\r\n                        if (relPath !== options.basePath && relPath.indexOf(options.basePath) === 0) {\r\n                            groupName = relPath.substring(options.basePath.length + 1);\r\n                        }\r\n                    }\r\n\r\n                    let group: Group = groups[groupName];\r\n                    if (!group) {\r\n                        group = {};\r\n                        groups[groupName] = group;\r\n                    }\r\n\r\n                    if (collection[path.base]) {\r\n                        throw new Error('gulp-ps-code requires unique name of ps file, conflicted with ' + path.base);\r\n                    }\r\n\r\n                    collection[path.base] = true;\r\n\r\n                    let lines = file.contents.toString().split('\\n');\r\n                    const newLines = [];\r\n                    for (let i = 0; i < lines.length; i++) {\r\n                        const line = lines[i];\r\n                        if (line.indexOf('# SIG # Begin signature block') >= 0) {\r\n                            break;\r\n                        }\r\n\r\n                        newLines.push(line);\r\n                    }\r\n\r\n                    const data: string = newLines.join('\\n');\r\n                    if (options.validate) {\r\n                        // validate non-ascii code presence.\r\n                        lines = data.split('\\n');\r\n                        let errorCode = false;\r\n                        for (let i = 0; i < lines.length; i++) {\r\n                            const line = lines[i];\r\n                            for (let j = 0; j < line.length; j++) {\r\n                                const code = line.charCodeAt(j);\r\n                                if (code > 0x7f) {\r\n                                    let point = '';\r\n                                    if (j > 0) {\r\n                                        for (let k = 1; k <= j; k++) {\r\n                                            point += ' ';\r\n                                        }\r\n                                    }\r\n\r\n                                    console.error(\r\n                                        `File: ${path.base}, Line: ${i + 1}, Position: ${j + 1}, Char: '${line.charAt(j)}', Code: ${code}`);\r\n                                    console.error(line);\r\n                                    console.error(point + '^----<see>');\r\n                                    errorCode = true;\r\n                                }\r\n                            }\r\n                        }\r\n\r\n                        if (errorCode) {\r\n                            throw new Error('File contained non ascii code: ' + path.base);\r\n                        }\r\n\r\n                        // validate script file name.\r\n                        const segments = path.base.split('-');\r\n                        if (segments.length !== 2) {\r\n                            console.error('File doesn\\'t follow <Verb>-<Name>.ps1 format to support JEA: ' + path.base);\r\n                        } else {\r\n                            // validate script verb.\r\n                            const found = PsCodeConverter.verbs.find(x => x === segments[0]);\r\n                            if (!found) {\r\n                                console.error('File doesn\\'t follow official verb name to support JEA: ' + path.base);\r\n                                console.error('Try Get-Verb PowerShell command to find official verbs.');\r\n                                console.error('https://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx');\r\n                                errorCode = true;\r\n                            } else {\r\n                                // validate comments for JEA.\r\n                                const commentIndex = data.indexOf('{');\r\n                                const comments: string = commentIndex < 0 ? data : data.substring(0, commentIndex);\r\n                                const commentsMinimum = comments.indexOf('<#') >= 0\r\n                                    && comments.indexOf('#>') > 0\r\n                                    && comments.indexOf('.SYNOPSIS') > 0\r\n                                    && comments.indexOf('.DESCRIPTION') > 0\r\n                                    && comments.indexOf('.ROLE') > 0;\r\n                                if (!commentsMinimum) {\r\n                                    console.error(\r\n                                        'The script file doesn\\'t contain formal PowerShell comment section to support JEA: ' + path.base);\r\n                                    console.error(\r\n                                        'Requires .SYNOPSIS, .DESCRIPTION and .ROLE comments.');\r\n                                    errorCode = true;\r\n                                }\r\n\r\n                                let roleLine = false;\r\n                                for (const comment of comments.split('\\n')) {\r\n                                    if (roleLine) {\r\n                                        const role = comment.trim();\r\n                                        if (role === '' || role === '#>') {\r\n                                            break;\r\n                                        }\r\n\r\n                                        if (role !== 'Administrators' && role !== 'Readers' && role !== 'Hyper-V-Administrators') {\r\n                                            console.error(\r\n                                                'The script file contains unsupported JEA ROLE \"' + role + '\": ' + path.base);\r\n                                            errorCode = true;\r\n                                        }\r\n                                    }\r\n\r\n                                    if (comment.indexOf('.ROLE') >= 0) {\r\n                                        roleLine = true;\r\n                                    }\r\n                                }\r\n                            }\r\n                        }\r\n\r\n                        if (errorCode) {\r\n                            throw new Error('File cannot support JEA: ' + path.base);\r\n                        }\r\n                    }\r\n\r\n                    group[path.base] = data;\r\n                    lastFile = file;\r\n                }\r\n            } catch (e) {\r\n                error = (!e.plugin || (e.plugin !== PLUGIN_NAME)) ?\r\n                    util.extendError(new pluginError({ plugin: PLUGIN_NAME, message: e.message }), e) : e;\r\n            }\r\n\r\n            return cb(error);\r\n        },\r\n        function (cb) {\r\n            const converter = new PsCode.PsCodeConverter(options);\r\n            converter.contentReset();\r\n            converter.generate(groups);\r\n            const tsFile = new Vinyl({\r\n                cwd: lastFile.cwd,\r\n                base: lastFile.base,\r\n                path: lastFile.base + '/' + options.name,\r\n                contents: Buffer.from(converter.content, 'utf8')\r\n            });\r\n\r\n            this.push(tsFile);\r\n            cb();\r\n        });\r\n}\r\n\r\nmodule.exports = gulpPsCode;\r\n"]}