module.exports = function()

in Gruntfile.js [10:883]


module.exports = function (grunt) {
    const pkgPath = path.resolve('./node_modules/.bin/pkg');
    const tsmPath = path.resolve('./node_modules/.bin/tsm');
    const webpackPath = path.resolve('./node_modules/.bin/webpack');

    const extensionPath = 'extension';

    const packageReportPath = path.join('packages', 'report');
    const packageReportBundlePath = path.join(packageReportPath, 'bundle');
    const packageReportDropPath = path.join(packageReportPath, 'drop');

    const packageUIPath = path.join('packages', 'ui');
    const packageUIBundlePath = path.join(packageUIPath, 'bundle');
    const packageUIDropPath = path.join(packageUIPath, 'drop');

    const mockAdbAppPath = path.resolve('./src/tests/miscellaneous/mock-adb/app');
    const mockAdbBinSrcPath = path.join(mockAdbAppPath, 'bin.js');
    const mockAdbBinFilename = process.platform === 'win32' ? 'adb.exe' : 'adb';
    const mockAdbDropPath = path.join('drop', 'mock-adb');
    const mockAdbBinOutPath = path.join(mockAdbDropPath, mockAdbBinFilename);

    function mustExist(file, reason) {
        const normalizedFile = path.normalize(file);
        if (!grunt.file.exists(normalizedFile)) {
            grunt.fail.fatal(`Missing required file ${normalizedFile}\n${reason}`);
        }
    }

    function getUnifiedVersion() {
        return grunt.option('unified-version');
    }

    grunt.initConfig({
        bom: {
            cwd: path.resolve('./src/**/*.{ts,tsx,js,snap,html,scss,css}'),
        },
        clean: {
            intermediates: ['dist', extensionPath],
            'mock-adb': mockAdbDropPath,
            'package-report': packageReportDropPath,
            'package-ui': packageUIDropPath,
            scss: path.join('src', '**/*.scss.d.ts'),
        },
        concurrent: {
            'webpack-all': [
                'exec:webpack-dev',
                'exec:webpack-dev-mv3',
                'exec:webpack-unified',
                'exec:webpack-prod',
            ],
        },
        copy: {
            code: {
                files: [
                    {
                        cwd: './src',
                        src: ['manifest.json'],
                        dest: extensionPath,
                        expand: true,
                    },
                    {
                        cwd: './src',
                        src: ['./**/*.html', '!./tests/**/*'],
                        dest: extensionPath,
                        expand: true,
                    },
                ],
            },
            images: {
                files: [
                    {
                        cwd: './src',
                        src: ['./**/*.{png,ico,icns}', '!./tests/**/*'],
                        dest: extensionPath,
                        expand: true,
                    },
                ],
            },
            styles: {
                files: [
                    {
                        cwd: './src',
                        src: '**/*.css',
                        dest: extensionPath,
                        expand: true,
                    },
                    {
                        cwd: './dist/src/reports',
                        src: '*.css',
                        dest: path.join(extensionPath, 'reports'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/views',
                        src: '**/*.css',
                        dest: path.join(extensionPath, 'views'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/DetailsView/Styles',
                        src: '*.css',
                        dest: path.join(extensionPath, 'DetailsView/styles/default'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/electron/views',
                        src: '*.css',
                        dest: path.join(extensionPath, 'electron/views'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/injected/styles',
                        src: '*.css',
                        dest: path.join(extensionPath, 'injected/styles/default'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/popup/Styles',
                        src: '*.css',
                        dest: path.join(extensionPath, 'popup/styles/default'),
                        expand: true,
                    },
                    {
                        cwd: './node_modules/office-ui-fabric-react/dist/css',
                        src: 'fabric.min.css',
                        dest: path.join(extensionPath, 'common/styles/'),
                        expand: true,
                    },
                    {
                        cwd: './dist/src/debug-tools',
                        src: '*.css',
                        dest: path.join(extensionPath, 'debug-tools'),
                        expand: true,
                    },
                ],
            },
            'package-report': {
                files: [
                    {
                        cwd: '.',
                        src: path.join(packageReportBundlePath, 'report.bundle.js'),
                        dest: path.join(packageReportDropPath, 'index.js'),
                    },
                    {
                        cwd: '.',
                        src: './src/reports/package/accessibilityInsightsReport.d.ts',
                        dest: path.join(packageReportDropPath, 'index.d.ts'),
                    },
                ],
            },
            'package-ui': {
                files: [
                    {
                        cwd: '.',
                        src: path.join(packageUIBundlePath, 'ui.bundle.js'),
                        dest: path.join(packageUIDropPath, 'index.js'),
                    },
                    {
                        cwd: '.',
                        src: path.join(packageUIBundlePath, 'ui.css'),
                        dest: path.join(packageUIDropPath, 'ui.css'),
                    },
                ],
            },
        },
        exec: {
            'webpack-dev': `"${webpackPath}" --config-name dev`,
            'webpack-dev-mv3': `"${webpackPath}" --config-name dev-mv3`,
            'webpack-prod': `"${webpackPath}" --config-name prod`,
            'webpack-unified': `"${webpackPath}" --config-name unified`,
            'webpack-package-report': `"${webpackPath}" --config-name package-report`,
            'webpack-package-ui': `"${webpackPath}" --config-name package-ui`,
            'generate-scss-typings': `"${tsmPath}" src`,
            'pkg-mock-adb': `"${pkgPath}" "${mockAdbBinSrcPath}" -d --target host --output "${mockAdbBinOutPath}"`,
        },
        sass: {
            options: {
                implementation: sass,
                outputStyle: 'expanded',
            },
            dist: {
                files: [
                    {
                        src: 'src/**/*.scss',
                        dest: 'dist',
                        expand: true,
                        ext: '.css',
                    },
                ],
            },
        },
        'embed-styles': {
            'package-report': {
                cwd: packageReportBundlePath,
                src: '**/*bundle.js',
                dest: packageReportBundlePath,
                expand: true,
                cssPath: path.resolve('extension', 'prodBundle'),
            },
        },
        watch: {
            images: {
                files: ['src/**/*.{png,ico,icns}'],
                tasks: ['copy:images', 'drop:dev', 'drop:dev-mv3', 'drop:unified-dev'],
            },
            'non-webpack-code': {
                files: ['src/**/*.html', 'src/manifest.json'],
                tasks: ['copy:code', 'drop:dev', 'drop:dev-mv3', 'drop:unified-dev'],
            },
            scss: {
                files: ['src/**/*.scss'],
                tasks: ['sass', 'copy:styles', 'drop:dev', 'drop:dev-mv3', 'drop:unified-dev'],
            },
            // We assume webpack --watch is running separately (usually via 'yarn watch')
            'webpack-dev-output': {
                files: ['extension/devBundle/**/*.*'],
                tasks: ['drop:dev'],
            },
            // We assume webpack --watch is running separately (usually via 'yarn watch')
            'webpack-dev-mv3-output': {
                files: ['extension/devMv3Bundle/**/*.*'],
                tasks: ['drop:dev-mv3'],
            },
            'webpack-unified-output': {
                files: ['extension/unifiedBundle/**/*.*'],
                tasks: ['drop:unified-dev'],
            },
        },
    });

    const targetNames = Object.keys(targets);
    const releaseTargets = Object.keys(targets).filter(t => targets[t].release);
    const extensionReleaseTargets = releaseTargets.filter(
        t => targets[t].config.options.productCategory === 'extension',
    );
    const unifiedReleaseTargets = releaseTargets.filter(
        t => targets[t].config.options.productCategory === 'electron',
    );

    unifiedReleaseTargets.forEach(targetName => {
        const { config, appId, publishUrl } = targets[targetName];
        const { electronIconBaseName, fullName, productCategory } = config.options;
        const dropPath = `drop/${productCategory}/${targetName}`;

        grunt.config.merge({
            'configure-electron-builder': {
                [targetName]: {
                    dropPath,
                    electronIconBaseName,
                    fullName,
                    appId,
                    publishUrl,
                },
            },
            'electron-builder-prepare': {
                [targetName]: {
                    dropPath: dropPath,
                },
            },
            'electron-builder-pack': {
                [targetName]: {
                    dropPath: dropPath,
                },
            },
            'unified-release-drop': {
                [targetName]: {
                    // empty on purpose
                },
            },
            'unified-release-pack': {
                [targetName]: {
                    // empty on purpose
                },
            },
            'zip-mac-folder': {
                [targetName]: {
                    dropPath: dropPath,
                },
            },
        });
    });

    targetNames.forEach(targetName => {
        const { config, bundleFolder, telemetryKeyIdentifier } = targets[targetName];

        const { productCategory } = config.options;

        const dropPath = path.join(`drop/${productCategory}`, targetName);
        const dropExtensionPath = path.join(dropPath, 'product');

        const productCategorySpecificCopyFiles = [];
        if (productCategory === 'electron') {
            productCategorySpecificCopyFiles.push(
                {
                    src: 'src/electron/resources/mit_license_en.txt',
                    dest: `${dropExtensionPath}/LICENSE`,
                },
                {
                    src: androidServiceBin.apkPath,
                    // This should be kept in sync with android-service-apk.ts
                    dest: path.join(dropExtensionPath, 'android-service', 'android-service.apk'),
                },
                {
                    src: androidServiceBin.noticePath,
                    dest: path.join(
                        dropExtensionPath,
                        'android-service',
                        path.basename(androidServiceBin.noticePath),
                    ),
                },
            );
        } else {
            productCategorySpecificCopyFiles.push({
                src: 'LICENSE',
                dest: `${dropExtensionPath}/LICENSE`,
            });
        }

        grunt.config.merge({
            drop: {
                [targetName]: {
                    // empty on purpose
                },
            },
            configure: {
                [targetName]: {
                    configJSPath: path.join(dropExtensionPath, 'insights.config.js'),
                    configJSONPath: path.join(dropExtensionPath, 'insights.config.json'),
                    config,
                    telemetryKeyIdentifier,
                },
            },
            manifest: {
                [targetName]: {
                    manifestSrc: path.join('src', 'manifest.json'),
                    manifestDest: path.join(dropExtensionPath, 'manifest.json'),
                    config,
                },
            },
            clean: {
                [targetName]: dropPath,
            },
            'embed-styles': {
                [targetName]: {
                    cwd: path.resolve(extensionPath, bundleFolder),
                    src: '**/*bundle.js',
                    dest: path.resolve(extensionPath, bundleFolder),
                    cssPath: path.resolve(extensionPath, bundleFolder),
                    expand: true,
                },
            },
            copy: {
                [targetName]: {
                    files: [
                        {
                            cwd: path.resolve(extensionPath, bundleFolder),
                            src: ['*.js', '*.js.map', '*.css'],
                            dest: path.resolve(dropExtensionPath, 'bundle'),
                            expand: true,
                        },
                        {
                            cwd: extensionPath,
                            src: ['**/*.{png,icns,ico,css,woff}'],
                            dest: dropExtensionPath,
                            expand: true,
                        },
                        {
                            cwd: 'deploy',
                            src: ['Gruntfile.js', 'package.json'],
                            dest: dropPath,
                            expand: true,
                        },
                        {
                            cwd: extensionPath,
                            src: ['**/*.html'],
                            dest: dropExtensionPath,
                            expand: true,
                        },
                        ...productCategorySpecificCopyFiles,
                    ],
                },
            },
        });
    });

    grunt.loadNpmTasks('grunt-bom-removal');
    grunt.loadNpmTasks('grunt-concurrent');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-exec');
    grunt.loadNpmTasks('grunt-sass');

    grunt.registerMultiTask('embed-styles', function () {
        const { cssPath } = this.data;
        this.files.forEach(file => {
            const {
                src: [src],
                dest,
            } = file;
            grunt.log.writeln(`embedding style in ${src}`);
            const fileOptions = { options: { encoding: 'utf8' } };
            const input = grunt.file.read(src, fileOptions);
            // eslint-disable-next-line no-useless-escape
            const rex = /\<\<CSS:([a-zA-Z\-\.\/]+)\>\>/g;
            const output = input.replace(rex, (_, cssName) => {
                const cssFile = path.resolve(cssPath, cssName);
                grunt.log.writeln(`    embedding from ${cssFile}`);
                const styles = grunt.file.read(cssFile, fileOptions);
                return styles.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\\n');
            });
            grunt.file.write(dest, output, fileOptions);
            grunt.log.writeln(`    written to ${dest}`);
        });
    });

    grunt.registerMultiTask('configure', function () {
        const { config, configJSONPath, configJSPath, telemetryKeyIdentifier } = this.data;
        // We pass this as an option from a build variable not because it is a secret
        // (it can be found easily enough from released builds), but to make it harder
        // to accidentally pollute release telemetry with data from local builds.
        if (telemetryKeyIdentifier && grunt.option(telemetryKeyIdentifier)) {
            config.options.appInsightsInstrumentationKey = grunt.option(telemetryKeyIdentifier);
        }

        // Add unifiedAppVersion value for electron-based products
        if (config.options.productCategory === 'electron') {
            const unifiedAppVersion = getUnifiedVersion();
            if (unifiedAppVersion) {
                config.options.unifiedAppVersion = unifiedAppVersion;
            }
        }
        const configJSON = JSON.stringify(config, undefined, 4);
        grunt.file.write(configJSONPath, configJSON);
        const copyrightHeader =
            '// Copyright (c) Microsoft Corporation. All rights reserved.\n// Licensed under the MIT License.\n';
        const configJS = `${copyrightHeader}window.insights = ${configJSON}`;
        grunt.file.write(configJSPath, configJS);
    });

    grunt.registerMultiTask('manifest', function () {
        const { config, manifestSrc, manifestDest } = this.data;
        const manifestJSON = grunt.file.readJSON(manifestSrc);

        // Build-specific settings that exist in both MV2 and MV3
        merge(manifestJSON, {
            name: config.options.fullName,
            description: config.options.extensionDescription,
            manifest_version: config.options.manifestVersion,
            icons: {
                16: config.options.icon16,
                48: config.options.icon48,
                128: config.options.icon128,
            },
        });

        if (config.options.manifestVersion === 3) {
            // Settings that are specific to MV3
            merge(manifestJSON, {
                action: {
                    default_icon: {
                        20: config.options.icon16,
                        40: config.options.icon48,
                    },
                },
                background: {
                    service_worker: 'bundle/serviceWorker.bundle.js',
                },
                host_permissions: [],
            });
        } else {
            // Settings that are specific to MV2. Note that many of these settings--especially the
            // commands--will eventually be restored to manifest.json. They are here only because
            // we want to vet each settings as we convert from MV2 to MV3.
            merge(manifestJSON, {
                browser_action: {
                    default_popup: 'popup/popup.html',
                    default_icon: {
                        20: config.options.icon16,
                        40: config.options.icon48,
                    },
                },
                background: {
                    page: 'background/background.html',
                    persistent: true,
                },
                web_accessible_resources: [
                    'insights.html',
                    'assessments/*',
                    'injected/*',
                    'background/*',
                    'common/*',
                    'DetailsView/*',
                    'bundle/*',
                    'NOTICE.html',
                ],
                content_security_policy:
                    "script-src 'self' 'unsafe-eval' https://az416426.vo.msecnd.net; object-src 'self'",
                optional_permissions: ['*://*/*'],
                commands: {
                    _execute_browser_action: {
                        suggested_key: {
                            windows: 'Alt+Shift+K',
                            mac: 'Alt+Shift+K',
                            chromeos: 'Alt+Shift+K',
                            linux: 'Alt+Shift+K',
                        },
                        description: 'Activate the extension',
                    },
                    '01_toggle-issues': {
                        suggested_key: {
                            windows: 'Alt+Shift+1',
                            mac: 'Alt+Shift+1',
                            chromeos: 'Alt+Shift+1',
                            linux: 'Alt+Shift+1',
                        },
                        description: 'Toggle Automated checks',
                    },
                    '02_toggle-landmarks': {
                        suggested_key: {
                            windows: 'Alt+Shift+2',
                            mac: 'Alt+Shift+2',
                            chromeos: 'Alt+Shift+2',
                            linux: 'Alt+Shift+2',
                        },
                        description: 'Toggle Landmarks',
                    },
                    '03_toggle-headings': {
                        suggested_key: {
                            windows: 'Alt+Shift+3',
                            mac: 'Alt+Shift+3',
                            chromeos: 'Alt+Shift+3',
                            linux: 'Alt+Shift+3',
                        },
                        description: 'Toggle Headings',
                    },
                    '04_toggle-tabStops': {
                        description: 'Toggle Tab stops',
                    },
                    '05_toggle-color': {
                        description: 'Toggle Color',
                    },
                    '06_toggle-needsReview': {
                        description: 'Toggle Needs review',
                    },
                },
            });
        }

        grunt.file.write(manifestDest, JSON.stringify(manifestJSON, undefined, 2));
    });

    grunt.registerMultiTask('drop', function () {
        const targetName = this.target;
        const { bundleFolder, mustExistFile, config } = targets[targetName];

        const { productCategory } = config.options;

        const dropPath = path.join(`drop/${productCategory}`, targetName);
        const dropExtensionPath = path.join(dropPath, 'product');

        const mustExistPath = path.join(extensionPath, bundleFolder, mustExistFile);

        mustExist(mustExistPath, 'Have you run webpack?');

        grunt.task.run('embed-styles:' + targetName);
        grunt.task.run('clean:' + targetName);
        grunt.task.run('copy:' + targetName);
        grunt.task.run('configure:' + targetName);
        grunt.task.run('manifest:' + targetName);
        console.log(`${targetName} extension is in ${dropExtensionPath}`);
    });

    grunt.registerMultiTask('configure-electron-builder', function () {
        grunt.task.requires('drop:' + this.target);
        const { dropPath, electronIconBaseName, fullName, appId, publishUrl } = this.data;
        const productDir = `${dropPath}/product`;

        const outElectronBuilderConfigFile = path.join(dropPath, 'electron-builder.yml');
        const srcElectronBuilderConfigFile = path.join(
            'src',
            'electron',
            'electron-builder',
            `electron-builder.template.yaml`,
        );

        const version = getUnifiedVersion() || '0.0.0';

        const config = grunt.file.readYAML(srcElectronBuilderConfigFile);
        config.appId = appId;
        config.directories.app = dropPath;
        config.directories.output = `${dropPath}/packed`;
        config.extraMetadata.version = version;
        config.win.icon = `src/${electronIconBaseName}.ico`;
        // electron-builder infers the linux icon from the mac one
        config.mac.icon = `src/${electronIconBaseName}.icns`;
        config.publish.url = publishUrl;
        config.productName = fullName;
        config.extraMetadata.name = fullName;
        // This is necessary for the AppImage to display using our brand icon
        // See electron-userland/electron-builder#3547 and AppImage/AppImageKit#678
        config.linux.artifactName = fullName.replace(/ (- )?/g, '_') + '.${ext}';

        for (const fileset of [...config.extraResources, ...config.extraFiles]) {
            fileset.from = fileset.from.replace(/TARGET_SPECIFIC_PRODUCT_DIR/g, productDir);
        }

        // Manually copying the license files is a workaround for electron-builder #1495.
        // On win/linux builds these are automatically included, but in Mac they are omitted.
        if (process.platform === 'darwin') {
            config.extraFiles.push(
                {
                    from: 'node_modules/electron/dist/LICENSE',
                    to: 'LICENSE.electron.txt',
                },
                {
                    from: 'node_modules/electron/dist/LICENSES.chromium.html',
                    to: 'LICENSES.chromium.html',
                },
            );
        }

        const configFileContent = yaml.dump(config);
        grunt.file.write(outElectronBuilderConfigFile, configFileContent);
        grunt.log.writeln(`generated ${outElectronBuilderConfigFile} from target config`);
    });

    grunt.registerMultiTask('electron-builder-prepare', function () {
        grunt.task.requires('drop:' + this.target);
        grunt.task.requires('configure-electron-builder:' + this.target);

        const { dropPath } = this.data;
        const configFile = path.join(dropPath, 'electron-builder.yml');

        const taskDoneCallback = this.async();

        grunt.util.spawn(
            {
                cmd: 'node',
                args: [
                    'node_modules/electron-builder/out/cli/cli.js',
                    '-p',
                    'never',
                    '-c',
                    configFile,
                ],
            },
            (error, result, code) => {
                if (error) {
                    grunt.fail.fatal(
                        `electron-builder exited with error code ${code}:\n\n${result.stdout}`,
                        code,
                    );
                }

                taskDoneCallback();
            },
        );
    });

    grunt.registerMultiTask('electron-builder-pack', function () {
        const { dropPath } = this.data;
        const configFile = path.join(dropPath, 'electron-builder.yml');

        mustExist(configFile, 'Have you built the product you are trying to pack?');

        let unpackedDirName;

        switch (process.platform) {
            case 'win32':
                unpackedDirName = 'win-unpacked';
                break;
            case 'darwin':
                unpackedDirName = 'mac';
                break;
            case 'linux':
                unpackedDirName = 'linux-unpacked';
                break;
        }

        const unpackedPath = path.join(dropPath, 'packed', unpackedDirName);

        const taskDoneCallback = this.async();

        grunt.util.spawn(
            {
                cmd: 'node',
                args: [
                    'node_modules/electron-builder/out/cli/cli.js',
                    '-p',
                    'never',
                    '-c',
                    configFile,
                    '--pd',
                    unpackedPath,
                ],
            },
            (error, result, code) => {
                if (error) {
                    grunt.fail.fatal(
                        `electron-builder exited with error code ${code}:\n\n${result.stdout}`,
                        code,
                    );
                }

                taskDoneCallback();
            },
        );
    });

    grunt.registerMultiTask('zip-mac-folder', function () {
        grunt.task.requires('electron-builder-pack:' + this.target);

        // We found that the mac update fails unless we produce the
        // zip file ourselves; electron-builder requires a zip file, but
        // the zip file it produces leads to 'couldn't find pkzip signatures'
        // during the eventual update.

        if (process.platform !== 'darwin') {
            grunt.log.writeln(`task not required for this platform (${process.platform})`);
            return true;
        }

        const { dropPath } = this.data;
        const packedPath = `${dropPath}/packed`;

        const taskDoneCallback = this.async();

        grunt.util.spawn(
            {
                cmd: 'node',
                args: ['pipeline/scripts/zip-mac-folder.js', packedPath],
            },
            (error, result, code) => {
                if (error) {
                    grunt.fail.fatal(
                        `zipping mac folder exited with error code ${code}:\n\n${result.stdout}`,
                        code,
                    );
                }

                taskDoneCallback();
            },
        );
    });

    grunt.registerMultiTask('unified-release-drop', function () {
        grunt.task.run(`drop:${this.target}`);
        grunt.task.run(`configure-electron-builder:${this.target}`);
        grunt.task.run(`electron-builder-prepare:${this.target}`);
    });

    grunt.registerMultiTask('unified-release-pack', function () {
        grunt.task.run(`electron-builder-pack:${this.target}`);
        grunt.task.run(`zip-mac-folder:${this.target}`);
    });

    grunt.registerTask('package-report', function () {
        const mustExistPath = path.join(packageReportBundlePath, 'report.bundle.js');

        mustExist(mustExistPath, 'Have you run webpack?');

        grunt.task.run('embed-styles:package-report');
        grunt.task.run('clean:package-report');
        grunt.task.run('copy:package-report');
        console.log(`package is in ${packageReportDropPath}`);
    });

    grunt.registerTask('package-ui', function () {
        const mustExistPath = path.join(packageUIBundlePath, 'ui.bundle.js');

        mustExist(mustExistPath, 'Have you run webpack?');

        grunt.task.run('clean:package-ui');
        grunt.task.run('copy:package-ui');
        console.log(`package is in ${packageUIDropPath}`);
    });

    grunt.registerTask('extension-release-drops', function () {
        extensionReleaseTargets.forEach(targetName => {
            grunt.task.run('drop:' + targetName);
        });
    });

    grunt.registerTask('unified-release-drops', function () {
        unifiedReleaseTargets.forEach(targetName => {
            grunt.task.run('unified-release-drop:' + targetName);
        });
    });

    grunt.registerTask('unified-release-packs', function () {
        unifiedReleaseTargets.forEach(targetName => {
            grunt.task.run('unified-release-pack:' + targetName);
        });
    });

    grunt.registerTask('ada-cat', function () {
        if (process.env.SHOW_ADA !== 'false') {
            console.log(
                'Image of Ada sleeping follows. Set environment variable SHOW_ADA to false to hide.',
            );
            const adaFile = 'docs/art/ada-cat.ansi256.txt';
            const adaArt = grunt.file.read(adaFile);
            console.log(adaArt);
        }
    });

    grunt.registerTask('build-assets', ['sass', 'copy:code', 'copy:styles', 'copy:images']);

    // Main entry points for npm scripts:
    grunt.registerTask('build-dev', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:webpack-dev',
        'build-assets',
        'drop:dev',
    ]);
    grunt.registerTask('build-dev-mv3', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:webpack-dev-mv3',
        'build-assets',
        'drop:dev-mv3',
    ]);
    grunt.registerTask('build-prod', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:webpack-prod',
        'build-assets',
        'drop:production',
    ]);
    grunt.registerTask('build-unified', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:pkg-mock-adb',
        'exec:webpack-unified',
        'build-assets',
        'drop:unified-dev',
    ]);
    grunt.registerTask('build-unified-canary', [
        'build-unified',
        'unified-release-drop:unified-canary',
    ]);
    grunt.registerTask('build-unified-all', ['build-unified', 'unified-release-drops']);
    grunt.registerTask('pack-unified-all', ['unified-release-packs']);
    grunt.registerTask('build-package-report', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:webpack-prod', // required to get the css assets
        'exec:webpack-package-report',
        'build-assets',
        'package-report',
    ]);
    grunt.registerTask('build-package-ui', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:webpack-package-ui',
        'build-assets',
        'package-ui',
    ]);
    grunt.registerTask('build-all', [
        'clean:intermediates',
        'exec:generate-scss-typings',
        'exec:pkg-mock-adb',
        'concurrent:webpack-all',
        'build-assets',
        'drop:dev',
        'drop:dev-mv3',
        'drop:unified-dev',
        'extension-release-drops',
    ]);

    grunt.registerTask('default', ['build-dev']);
};