"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ssrBuild = exports.build = exports.createBaseRollupPlugins = exports.onRollupWarning = void 0;
const path_1 = __importDefault(require("path"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const chalk_1 = __importDefault(require("chalk"));
const p_map_series_1 = __importDefault(require("p-map-series"));
const json_1 = require("klona/json");
const utils_1 = require("../utils");
const resolver_1 = require("../resolver");
const buildPluginResolve_1 = require("./buildPluginResolve");
const buildPluginHtml_1 = require("./buildPluginHtml");
const buildPluginCss_1 = require("./buildPluginCss");
const buildPluginAsset_1 = require("./buildPluginAsset");
const buildPluginEsbuild_1 = require("./buildPluginEsbuild");
const buildPluginReplace_1 = require("./buildPluginReplace");
const esbuildService_1 = require("../esbuildService");
const config_1 = require("../config");
const transform_1 = require("../transform");
const hash_sum_1 = __importDefault(require("hash-sum"));
const cssUtils_1 = require("../utils/cssUtils");
const buildPluginWasm_1 = require("./buildPluginWasm");
const buildPluginManifest_1 = require("./buildPluginManifest");
const writeColors = {
    [0 /* JS */]: chalk_1.default.cyan,
    [1 /* CSS */]: chalk_1.default.magenta,
    [2 /* ASSET */]: chalk_1.default.green,
    [3 /* HTML */]: chalk_1.default.blue,
    [4 /* SOURCE_MAP */]: chalk_1.default.gray
};
const warningIgnoreList = [`CIRCULAR_DEPENDENCY`, `THIS_IS_UNDEFINED`];
const dynamicImportWarningIgnoreList = [
    `Unsupported expression`,
    `statically analyzed`
];
const isBuiltin = require('isbuiltin');
function onRollupWarning(spinner, options) {
    return (warning, warn) => {
        if (warning.code === 'UNRESOLVED_IMPORT') {
            let message;
            const id = warning.source;
            const importer = warning.importer;
            if (isBuiltin(id)) {
                let importingDep;
                if (importer) {
                    const pkg = JSON.parse(utils_1.lookupFile(importer, ['package.json']) || `{}`);
                    if (pkg.name) {
                        importingDep = pkg.name;
                    }
                }
                const allowList = options.allowNodeBuiltins;
                if (importingDep && allowList && allowList.includes(importingDep)) {
                    return;
                }
                const dep = importingDep
                    ? `Dependency ${chalk_1.default.yellow(importingDep)}`
                    : `A dependency`;
                message =
                    `${dep} is attempting to import Node built-in module ${chalk_1.default.yellow(id)}.\n` +
                        `This will not work in a browser environment.\n` +
                        `Imported by: ${chalk_1.default.gray(importer)}`;
            }
            else {
                message =
                    `[vite]: Rollup failed to resolve import "${warning.source}" from "${warning.importer}".\n` +
                        `This is most likely unintended because it can break your application at runtime.\n` +
                        `If you do want to externalize this module explicitly add it to\n` +
                        `\`rollupInputOptions.external\``;
            }
            if (spinner) {
                spinner.stop();
            }
            throw new Error(message);
        }
        if (warning.plugin === 'rollup-plugin-dynamic-import-variables' &&
            dynamicImportWarningIgnoreList.some((msg) => warning.message.includes(msg))) {
            return;
        }
        if (!warningIgnoreList.includes(warning.code)) {
            // ora would swallow the console.warn if we let it keep running
            // https://github.com/sindresorhus/ora/issues/90
            if (spinner) {
                spinner.stop();
            }
            warn(warning);
            if (spinner) {
                spinner.start();
            }
        }
    };
}
exports.onRollupWarning = onRollupWarning;
/**
 * Creates non-application specific plugins that are shared between the main
 * app and the dependencies. This is used by the `optimize` command to
 * pre-bundle dependencies.
 */
async function createBaseRollupPlugins(root, resolver, options) {
    const { transforms = [], vueCustomBlockTransforms = {}, enableEsbuild = true, enableRollupPluginVue = true } = options;
    const { nodeResolve } = require('@rollup/plugin-node-resolve');
    const dynamicImport = require('rollup-plugin-dynamic-import-variables');
    return [
        // vite:resolve
        buildPluginResolve_1.createBuildResolvePlugin(root, resolver),
        // vite:esbuild
        enableEsbuild ? await buildPluginEsbuild_1.createEsbuildPlugin(options.jsx) : null,
        // vue
        enableRollupPluginVue ? await createVuePlugin(root, options) : null,
        require('@rollup/plugin-json')({
            preferConst: true,
            indent: '  ',
            compact: false,
            namedExports: true
        }),
        // user transforms
        ...(transforms.length || Object.keys(vueCustomBlockTransforms).length
            ? [transform_1.createBuildJsTransformPlugin(transforms, vueCustomBlockTransforms)]
            : []),
        nodeResolve({
            rootDir: root,
            extensions: resolver_1.supportedExts,
            preferBuiltins: false,
            dedupe: options.rollupDedupe || [],
            mainFields: resolver_1.mainFields
        }),
        require('@rollup/plugin-commonjs')({
            extensions: ['.js', '.cjs']
        }),
        dynamicImport({
            warnOnError: true,
            include: [/\.js$/],
            exclude: [/node_modules/]
        })
    ].filter(Boolean);
}
exports.createBaseRollupPlugins = createBaseRollupPlugins;
async function createVuePlugin(root, { vueCustomBlockTransforms = {}, rollupPluginVueOptions, cssPreprocessOptions, cssModuleOptions, vueCompilerOptions, vueTransformAssetUrls = {}, vueTemplatePreprocessOptions = {} }) {
    const { options: postcssOptions, plugins: postcssPlugins } = await cssUtils_1.resolvePostcssOptions(root, true);
    if (typeof vueTransformAssetUrls === 'object') {
        vueTransformAssetUrls = {
            includeAbsolute: true,
            ...vueTransformAssetUrls
        };
    }
    return require('rollup-plugin-vue')({
        ...rollupPluginVueOptions,
        templatePreprocessOptions: {
            ...vueTemplatePreprocessOptions,
            pug: {
                doctype: 'html',
                ...(vueTemplatePreprocessOptions && vueTemplatePreprocessOptions.pug)
            }
        },
        transformAssetUrls: vueTransformAssetUrls,
        postcssOptions,
        postcssPlugins,
        preprocessStyles: true,
        preprocessOptions: cssPreprocessOptions,
        preprocessCustomRequire: (id) => require(utils_1.resolveFrom(root, id)),
        compilerOptions: vueCompilerOptions,
        cssModulesOptions: {
            localsConvention: 'camelCase',
            generateScopedName: (local, filename) => `${local}_${hash_sum_1.default(filename)}`,
            ...cssModuleOptions,
            ...(rollupPluginVueOptions && rollupPluginVueOptions.cssModulesOptions)
        },
        customBlocks: Object.keys(vueCustomBlockTransforms)
    });
}
/**
 * Clone the given config object and fill it with default values.
 */
function prepareConfig(config) {
    const { alias = {}, assetsDir = '_assets', assetsInclude = utils_1.isStaticAsset, assetsInlineLimit = 4096, base = '/', cssCodeSplit = true, cssModuleOptions = {}, cssPreprocessOptions = {}, define = {}, emitAssets = true, emitIndex = true, enableEsbuild = true, enableRollupPluginVue = true, entry = 'index.html', env = {}, esbuildTarget = 'es2020', indexHtmlTransforms = [], jsx = 'vue', minify = true, mode = 'production', optimizeDeps = {}, outDir = 'dist', resolvers = [], rollupDedupe = [], rollupInputOptions = {}, rollupOutputOptions = {}, rollupPluginVueOptions = {}, root = process.cwd(), shouldPreload = null, silent = false, sourcemap = false, terserOptions = {}, transforms = [], vueCompilerOptions = {}, vueCustomBlockTransforms = {}, vueTransformAssetUrls = {}, vueTemplatePreprocessOptions = {}, write = true } = json_1.klona(config);
    return {
        ...config,
        alias,
        assetsDir,
        assetsInclude,
        assetsInlineLimit,
        base,
        cssCodeSplit,
        cssModuleOptions,
        cssPreprocessOptions,
        define,
        emitAssets,
        emitIndex,
        enableEsbuild,
        enableRollupPluginVue,
        entry,
        env,
        esbuildTarget,
        indexHtmlTransforms,
        jsx,
        minify,
        mode,
        optimizeDeps,
        outDir,
        resolvers,
        rollupDedupe,
        rollupInputOptions,
        rollupOutputOptions,
        rollupPluginVueOptions,
        root,
        shouldPreload,
        silent,
        sourcemap,
        terserOptions,
        transforms,
        vueCompilerOptions,
        vueCustomBlockTransforms,
        vueTransformAssetUrls,
        vueTemplatePreprocessOptions,
        write
    };
}
/**
 * Bundles the app for production.
 * Returns a Promise containing the build result.
 */
async function build(options) {
    const builds = [];
    const config = prepareConfig(options);
    const postBuildHooks = utils_1.toArray(config.configureBuild)
        .map((configureBuild) => configureBuild(config, builds))
        .filter(Boolean);
    const { root, assetsDir, assetsInlineLimit, emitAssets, minify, silent, sourcemap, shouldPreload, env, mode: configMode, define: userDefineReplacements, write } = config;
    const isTest = process.env.NODE_ENV === 'test';
    const resolvedMode = process.env.VITE_ENV || configMode;
    const start = Date.now();
    let spinner;
    const msg = `Building ${configMode} bundle...`;
    if (!silent) {
        if (process.env.DEBUG || isTest) {
            console.log(msg);
        }
        else {
            spinner = require('ora')(msg + '\n').start();
        }
    }
    const outDir = path_1.default.resolve(root, config.outDir);
    const indexPath = path_1.default.resolve(root, 'index.html');
    const publicDir = path_1.default.join(root, 'public');
    const publicBasePath = config.base.replace(/([^/])$/, '$1/'); // ensure ending slash
    const resolvedAssetsPath = path_1.default.join(outDir, assetsDir);
    const resolver = resolver_1.createResolver(root, config.resolvers, config.alias, config.assetsInclude);
    const { htmlPlugin, renderIndex } = await buildPluginHtml_1.createBuildHtmlPlugin(root, indexPath, publicBasePath, assetsDir, assetsInlineLimit, resolver, shouldPreload, options);
    const basePlugins = await createBaseRollupPlugins(root, resolver, config);
    // https://github.com/darionco/rollup-plugin-web-worker-loader
    // configured to support `import Worker from './my-worker?worker'`
    // this plugin relies on resolveId and must be placed before node-resolve
    // since the latter somehow swallows ids with query strings since 8.x
    basePlugins.splice(basePlugins.findIndex((p) => p.name.includes('node-resolve')), 0, require('rollup-plugin-web-worker-loader')({
        targetPlatform: 'browser',
        pattern: /(.+)\?worker$/,
        extensions: resolver_1.supportedExts,
        sourcemap: false // it's inlined so it bloats the bundle
    }));
    // user env variables loaded from .env files.
    // only those prefixed with VITE_ are exposed.
    const userClientEnv = {};
    const userEnvReplacements = {};
    Object.keys(env).forEach((key) => {
        if (key.startsWith(`VITE_`)) {
            userEnvReplacements[`import.meta.env.${key}`] = JSON.stringify(env[key]);
            userClientEnv[key] = env[key];
        }
    });
    const builtInClientEnv = {
        BASE_URL: publicBasePath,
        MODE: configMode,
        DEV: resolvedMode !== 'production',
        PROD: resolvedMode === 'production'
    };
    const builtInEnvReplacements = {};
    Object.keys(builtInClientEnv).forEach((key) => {
        builtInEnvReplacements[`import.meta.env.${key}`] = JSON.stringify(builtInClientEnv[key]);
    });
    Object.keys(userDefineReplacements).forEach((key) => {
        userDefineReplacements[key] = JSON.stringify(userDefineReplacements[key]);
    });
    const { pluginsPreBuild = [], plugins = [], pluginsPostBuild = [], pluginsOptimizer, ...rollupInputOptions } = config.rollupInputOptions;
    builds.unshift({
        input: config.entry,
        preserveEntrySignatures: false,
        treeshake: { moduleSideEffects: 'no-external' },
        ...rollupInputOptions,
        output: config.rollupOutputOptions,
        plugins: [
            ...plugins,
            ...pluginsPreBuild,
            ...basePlugins,
            // vite:html
            htmlPlugin,
            // we use a custom replacement plugin because @rollup/plugin-replace
            // performs replacements twice, once at transform and once at renderChunk
            // - which makes it impossible to exclude Vue templates from it since
            // Vue templates are compiled into js and included in chunks.
            buildPluginReplace_1.createReplacePlugin((id) => !/\?vue&type=template/.test(id) &&
                // also exclude css and static assets for performance
                !cssUtils_1.isCSSRequest(id) &&
                !resolver.isAssetRequest(id), {
                ...config_1.defaultDefines,
                ...userDefineReplacements,
                ...userEnvReplacements,
                ...builtInEnvReplacements,
                'import.meta.env.': `({}).`,
                'import.meta.env': JSON.stringify({
                    ...userClientEnv,
                    ...builtInClientEnv
                }),
                'process.env.NODE_ENV': JSON.stringify(resolvedMode),
                'process.env.': `({}).`,
                'process.env': JSON.stringify({ NODE_ENV: resolvedMode }),
                'import.meta.hot': `false`
            }, !!sourcemap),
            // vite:css
            buildPluginCss_1.createBuildCssPlugin({
                root,
                publicBase: publicBasePath,
                assetsDir,
                minify,
                inlineLimit: assetsInlineLimit,
                cssCodeSplit: config.cssCodeSplit,
                preprocessOptions: config.cssPreprocessOptions,
                modulesOptions: config.cssModuleOptions
            }),
            // vite:wasm
            buildPluginWasm_1.createBuildWasmPlugin(root, publicBasePath, assetsDir, assetsInlineLimit),
            // vite:asset
            buildPluginAsset_1.createBuildAssetPlugin(root, resolver, publicBasePath, assetsDir, assetsInlineLimit),
            config.enableEsbuild &&
                buildPluginEsbuild_1.createEsbuildRenderChunkPlugin(config.esbuildTarget, minify === 'esbuild'),
            // minify with terser
            // this is the default which has better compression, but slow
            // the user can opt-in to use esbuild which is much faster but results
            // in ~8-10% larger file size.
            minify && minify !== 'esbuild'
                ? require('rollup-plugin-terser').terser(config.terserOptions)
                : undefined,
            // #728 user plugins should apply after `@rollup/plugin-commonjs`
            // #471#issuecomment-683318951 user plugin after internal plugin
            ...pluginsPostBuild,
            // vite:manifest
            config.emitManifest ? buildPluginManifest_1.createBuildManifestPlugin() : undefined
        ].filter(Boolean)
    });
    // lazy require rollup so that we don't load it when only using the dev server
    // importing it just for the types
    const rollup = require('rollup').rollup;
    // multiple builds are processed sequentially, in case a build
    // depends on the output of a preceding build.
    const results = await p_map_series_1.default(builds, async (build, i) => {
        const { output: outputOptions, onResult, ...inputOptions } = build;
        let result;
        let indexHtml;
        let indexHtmlPath = getIndexHtmlOutputPath(build);
        const emitIndex = config.emitIndex && indexHtmlPath !== null;
        try {
            const bundle = await rollup({
                onwarn: onRollupWarning(spinner, config.optimizeDeps),
                ...inputOptions,
                plugins: [
                    ...(inputOptions.plugins || []).filter(
                    // remove vite:emit in case this build copied another build's plugins
                    (plugin) => plugin.name !== 'vite:emit'),
                    // vite:emit
                    createEmitPlugin(emitAssets, async (assets) => {
                        indexHtml = emitIndex ? await renderIndex(assets) : '';
                        result = { build, assets, html: indexHtml };
                        if (onResult) {
                            await onResult(result);
                        }
                        // run post-build hooks sequentially
                        await postBuildHooks.reduce((queue, hook) => queue.then(() => hook(result)), Promise.resolve());
                        if (write) {
                            if (i === 0) {
                                await fs_extra_1.default.emptyDir(outDir);
                            }
                            if (emitIndex) {
                                indexHtmlPath = path_1.default.join(outDir, indexHtmlPath);
                                await fs_extra_1.default.writeFile(indexHtmlPath, indexHtml);
                            }
                        }
                    })
                ]
            });
            await bundle[write ? 'write' : 'generate']({
                dir: resolvedAssetsPath,
                format: 'es',
                sourcemap,
                entryFileNames: `[name].[hash].js`,
                chunkFileNames: `[name].[hash].js`,
                assetFileNames: `[name].[hash].[ext]`,
                ...outputOptions
            });
        }
        finally {
            spinner && spinner.stop();
        }
        if (write && !silent) {
            if (emitIndex) {
                printFileInfo(indexHtmlPath, indexHtml, 3 /* HTML */);
            }
            for (const chunk of result.assets) {
                if (chunk.type === 'chunk') {
                    const filePath = path_1.default.join(resolvedAssetsPath, chunk.fileName);
                    printFileInfo(filePath, chunk.code, 0 /* JS */);
                    if (chunk.map) {
                        printFileInfo(filePath + '.map', chunk.map.toString(), 4 /* SOURCE_MAP */);
                    }
                }
                else if (emitAssets && chunk.source)
                    printFileInfo(path_1.default.join(resolvedAssetsPath, chunk.fileName), chunk.source, chunk.fileName.endsWith('.css') ? 1 /* CSS */ : 2 /* ASSET */);
            }
        }
        spinner && spinner.start();
        return result;
    });
    // copy over /public if it exists
    if (write && emitAssets && fs_extra_1.default.existsSync(publicDir)) {
        for (const file of await fs_extra_1.default.readdir(publicDir)) {
            await fs_extra_1.default.copy(path_1.default.join(publicDir, file), path_1.default.resolve(outDir, file));
        }
    }
    spinner && spinner.stop();
    if (!silent) {
        console.log(`Build completed in ${((Date.now() - start) / 1000).toFixed(2)}s.\n`);
    }
    // stop the esbuild service after each build
    await esbuildService_1.stopService();
    return results;
}
exports.build = build;
/**
 * Bundles the app in SSR mode.
 * - All Vue dependencies are automatically externalized
 * - Imports to dependencies are compiled into require() calls
 * - Templates are compiled with SSR specific optimizations.
 */
async function ssrBuild(options) {
    const { rollupInputOptions, rollupOutputOptions, rollupPluginVueOptions } = options;
    return build({
        outDir: 'dist-ssr',
        ...options,
        rollupPluginVueOptions: {
            ...rollupPluginVueOptions,
            target: 'node'
        },
        rollupInputOptions: {
            ...rollupInputOptions,
            external: resolveExternal(rollupInputOptions && rollupInputOptions.external)
        },
        rollupOutputOptions: {
            ...rollupOutputOptions,
            format: 'cjs',
            exports: 'named',
            entryFileNames: '[name].js',
            // 764 add `Symbol.toStringTag` when build es module into cjs chunk
            namespaceToStringTag: true
        },
        emitIndex: false,
        emitAssets: false,
        cssCodeSplit: false,
        minify: false
    });
}
exports.ssrBuild = ssrBuild;
function createEmitPlugin(emitAssets, emit) {
    return {
        name: 'vite:emit',
        async generateBundle(_, output) {
            // assume the first asset in `output` is an entry chunk
            const assets = Object.values(output);
            // process the output before writing
            await emit(assets);
            // write any assets injected by post-build hooks
            for (const asset of assets) {
                output[asset.fileName] = asset;
            }
            // remove assets from bundle if emitAssets is false
            if (!emitAssets) {
                for (const name in output) {
                    if (output[name].type === 'asset') {
                        delete output[name];
                    }
                }
            }
        }
    };
}
/**
 * Resolve the output path of `index.html` for the given build (relative to
 * `outDir` in Vite config).
 */
function getIndexHtmlOutputPath(build) {
    const { input, output } = build;
    return input === 'index.html' ? output.file || input : null;
}
function resolveExternal(userExternal) {
    const required = ['vue', /^@vue\//];
    if (!userExternal) {
        return required;
    }
    if (Array.isArray(userExternal)) {
        return [...required, ...userExternal];
    }
    else if (typeof userExternal === 'function') {
        return (src, importer, isResolved) => {
            if (src === 'vue' || /^@vue\//.test(src)) {
                return true;
            }
            return userExternal(src, importer, isResolved);
        };
    }
    else {
        return [...required, userExternal];
    }
}
function printFileInfo(filePath, content, type) {
    const needCompression = type === 0 /* JS */ || type === 1 /* CSS */ || type === 3 /* HTML */;
    const compressed = needCompression
        ? `, brotli: ${(require('brotli-size').sync(content) / 1024).toFixed(2)}kb`
        : ``;
    console.log(`${chalk_1.default.gray(`[write]`)} ${writeColors[type](path_1.default.relative(process.cwd(), filePath))} ${(content.length / 1024).toFixed(2)}kb${compressed}`);
}
//# sourceMappingURL=index.js.map