{"version":3,"sources":["../../../packages/tools/gulp-update-notes-resource/index.ts"],"names":[],"mappings":"","file":"index.d.ts","sourcesContent":["'use strict';\r\n\r\nimport { Buffer } from 'buffer';\r\nimport fs from 'fs';\r\nimport log from 'fancy-log';\r\nimport pluginError from 'plugin-error';\r\nimport through2 from 'through2';\r\nimport Vinyl from 'vinyl';\r\nimport * as util from '../utilities';\r\n\r\nconst PLUGIN_NAME = 'gulp-update-notes-resource';\r\n\r\ninterface Options {\r\n    /**\r\n     * resource name.\r\n     */\r\n    resourceName: string;\r\n\r\n    /**\r\n     * path of update notes file.\r\n     */\r\n    updateNotes: string;\r\n\r\n    /**\r\n     * depth of folder structure from the locale folder. (default is 1)\r\n     */\r\n    localeOffset: number;\r\n}\r\n\r\nfunction gulpUpdateNotesResources(options: Options) {\r\n    function findResourceKey(node: any, results: string[]): void {\r\n        const match = 'resources:strings:';\r\n        const keys = Object.keys(node);\r\n        if (keys && keys.length > 0) {\r\n            keys.forEach(key => {\r\n                const target = node[key];\r\n                if (target) {\r\n                    if (typeof target === 'string') {\r\n                        if (target.indexOf(match) === 0) {\r\n                            const value = target.substring(match.length);\r\n                            if (results.indexOf(value) < 0) {\r\n                                results.push(value);\r\n                            }\r\n                        }\r\n                    } else {\r\n                        findResourceKey(target, results);\r\n                    }\r\n                }\r\n            });\r\n        }\r\n    }\r\n\r\n    // override options settings if not specified.\r\n    options = <Options>Object.assign(\r\n        {\r\n            updateNotes: 'packages/shell/src/assets/updates/update-notes.json',\r\n            localeOffset: 1\r\n        },\r\n        options || {});\r\n\r\n    const resources: any = {};\r\n    const keyToResources: any = {};\r\n\r\n    return through2.obj(\r\n        /**\r\n         * Transform\r\n         */\r\n        function (file, encoding, callback) {\r\n            let locale = 'default';\r\n            if (resources.default) {\r\n                // if default is set already, find current locale from the file path.\r\n                const pathSegments = file.history[0].split('\\\\');\r\n                locale = pathSegments[pathSegments.length - (2 + options.localeOffset)];\r\n            }\r\n\r\n            resources[locale] = {};\r\n            if (resources.default) {\r\n                // if default is set already, pre-fill all resource items from default.\r\n                for (const resourceId in resources.default) {\r\n                    if (resources.default.hasOwnProperty(resourceId)) {\r\n                        resources[locale][resourceId] = resources.default[resourceId];\r\n                    }\r\n                }\r\n            }\r\n\r\n            // remove comments, /* multiline comment */ and // one line comment and \"//\": \"JSON element comment\"\r\n            const content = file.contents.toString('utf8')\r\n                                .replace(/(\\/\\*([^*]|[\\n]|(\\*+([^*/]|[\\n])))*\\*\\/+)|( +\\/\\/.*)|(  +\\\"\\/\\/\\\".*)/g, '');\r\n            const object = JSON.parse(content);\r\n            if (object[options.resourceName] && object[options.resourceName]['App']['UpdateNotes']) {\r\n                // supports <ResourceName>.Manifest.<KeyName> format.\r\n                const items = object[options.resourceName]['App']['UpdateNotes'];\r\n                for (const key in items) {\r\n                    if (items.hasOwnProperty(key)) {\r\n                        // Each key will have two children, title and description\r\n                        // no need to separate in JSON form\r\n                        resources[locale][key] = items[key];\r\n\r\n                        // Used to keep an alternate mapping we use later\r\n                        if (!keyToResources.hasOwnProperty(key)) {\r\n                            keyToResources[key] = {};\r\n                        }\r\n\r\n                        if (keyToResources.hasOwnProperty(key) &&\r\n                            !keyToResources[key].hasOwnProperty(locale)) {\r\n                            keyToResources[key][locale] = {};\r\n                        }\r\n                        keyToResources[key][locale] = items[key];\r\n                    }\r\n                }\r\n            } else {\r\n                // supports <ResourceName>_App_UpdateNotes format.\r\n                const resourceUpdateNotes = options.resourceName + '_App_UpdateNotes_';\r\n                for (const name in object) {\r\n                    if (object.hasOwnProperty(name)) {\r\n                        const index = name.indexOf(resourceUpdateNotes);\r\n                        if (index === 0) {\r\n                            // Check if this resource has the specific substring or not\r\n                            const value = object[name];\r\n\r\n                            // If it contains '_' we split it so we keep only the key\r\n                            const resourceId2 = name.substring(resourceUpdateNotes.length);\r\n\r\n                            // Split to separate <KeyValue>_<type>\r\n                            // Possible types are title or description\r\n                            const splitText = resourceId2.split('_');\r\n\r\n                            const key = splitText[0];\r\n                            const type = splitText[1];\r\n                            if (!resources[locale].hasOwnProperty(key)) {\r\n                                resources[locale][key] = {};\r\n                            }\r\n                            resources[locale][key][type] = value;\r\n\r\n                            // Used to keep a mapping of keys to resources for faster\r\n                            // processing later\r\n                            if (!keyToResources.hasOwnProperty(key)) {\r\n                                keyToResources[key] = {};\r\n                            }\r\n\r\n                            if (keyToResources.hasOwnProperty(key) &&\r\n                                !keyToResources[key].hasOwnProperty(locale)) {\r\n                                keyToResources[key][locale] = {};\r\n                            }\r\n                            keyToResources[key][locale][type] = value;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n\r\n            return callback();\r\n        },\r\n\r\n        /**\r\n         * Flush\r\n         */\r\n        function (callback) {\r\n            try {\r\n                const updateNotesObject = JSON.parse(fs.readFileSync(options.updateNotes, 'utf8'));\r\n\r\n                const keys: string[] = [];\r\n                findResourceKey(updateNotesObject, keys);\r\n                const missingType: string[] = [];\r\n                const missing: string[] = [];\r\n                const unused: string[] = Object.keys(resources.default);\r\n\r\n                keys.forEach(function (key) {\r\n                    const index = unused.indexOf(key);\r\n                    if (index >= 0) {\r\n                        // If the item exists, remove it from the unused set\r\n                        unused.splice(index, 1);\r\n\r\n                        // We have to check each item has a title and description\r\n                        const typeKeys = Object.keys(keyToResources[key].default);\r\n                        const requiredTypesSet = new Set();\r\n                        requiredTypesSet.add('title');\r\n                        requiredTypesSet.add('description');\r\n                        typeKeys.forEach(item => {\r\n                            if (requiredTypesSet.has(item)) {\r\n                                requiredTypesSet.delete(item);\r\n                            }\r\n                        });\r\n\r\n                        // If it did not have we save it throw error later\r\n                        if (requiredTypesSet.size > 0) {\r\n                            missingType.push(key);\r\n                        }\r\n                    } else {\r\n                        missing.push(key);\r\n                    }\r\n                });\r\n\r\n                if (missing.length > 0) {\r\n                    missing.forEach(item => {\r\n                        log.error('Missing resource (update-notes.json): ' + item);\r\n                    });\r\n                    throw new Error('Missing resource found in manifest.json!');\r\n                }\r\n\r\n                if (unused.length > 0) {\r\n                    // allow to use unused resources to share the same strings.json between GWv1 and GWv2.\r\n                    unused.forEach(item => {\r\n                        log.warn('Unused resource (update-notes.json): ' + item);\r\n                    });\r\n                }\r\n\r\n                if (missingType.length > 0) {\r\n                    missingType.forEach(item => {\r\n                        log.error('Missing title or description (update-notes.json): ' + item);\r\n                    });\r\n                    throw new Error('Key without title or description found in strings.resjson!');\r\n                }\r\n\r\n                updateNotesObject.map((item, index) => {\r\n                    if (item.hasOwnProperty('stringJsonKey')) {\r\n                        const key = item.stringJsonKey.replace('resources:strings:', '');\r\n                        // Key to resource maps key -> all locales\r\n                        item.resources = keyToResources[key];\r\n                    }\r\n                    updateNotesObject[index] = item;\r\n                });\r\n\r\n                const updateNotesFile = new Vinyl({\r\n                    cwd: '.',\r\n                    base: '.',\r\n                    path: './update-notes.json',\r\n                    contents: Buffer.from(JSON.stringify(updateNotesObject, null, 2), 'utf8')\r\n                });\r\n\r\n                this.push(updateNotesFile);\r\n            } catch (e) {\r\n                const error = (!e.plugin || (e.plugin !== PLUGIN_NAME)) ?\r\n                    util.extendError(new pluginError({ plugin: PLUGIN_NAME, message: e.message }), e) : e;\r\n                log.error(error);\r\n            }\r\n\r\n            callback();\r\n        });\r\n}\r\n\r\nmodule.exports = gulpUpdateNotesResources;\r\n"]}