<commit_before>// Copyright 2022 Google LLC
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { readFile, mkdir, copyFile, stat, readdir } from 'fs/promises';
import { basename, dirname, extname, join } from 'path';
import type { Header, Rewrite, Redirect } from 'next/dist/lib/load-custom-routes.js';
import type { NextConfig } from 'next';
import { copy } from 'fs-extra';
import { pathToFileURL } from 'url';

import { Commands, DeployConfig, PathFactory, spawn } from '../../utils.js';

export const build = async (config: DeployConfig | Required<DeployConfig>, getProjectPath: PathFactory) => {

    const { default: { default: nextBuild } } = await import('next/dist/build/index.js');

    let nextConfig: NextConfig;
    try {
        const { default: { default: loadConfig } } = await import('next/dist/server/config.js');
        const { PHASE_PRODUCTION_BUILD } = await import('next/constants.js');
        nextConfig = await loadConfig(PHASE_PRODUCTION_BUILD, getProjectPath(), null);
    } catch(e) {
        // Must be Next 11, just import it
        nextConfig = await import(pathToFileURL(getProjectPath('next.config.js')).toString());
    }


    // SEMVER these defaults are only needed for Next 11
    const { distDir='.next', basePath='' } = nextConfig;

    const deployPath = (...args: string[]) => config.dist ? join(config.dist, ...args) : getProjectPath('.deploy', ...args);
    const getHostingPath = (...args: string[]) => deployPath('hosting', ...basePath.split('/'), ...args);

    await nextBuild(getProjectPath(), null, false, false, true);

    try {
        // Using spawn here, rather than their programatic API because I can't silence it
        // Failures with Next export are expected, we're just trying to do it if we can
        await spawn(Commands.next, ['export', '-o', getHostingPath()], { cwd: getProjectPath() });
    } catch(e) { }

    let usingCloudFunctions = !!config.function;
    const asyncSteps: Array<Promise<any>> = [];

    const exportDetailJson = await readFile(getProjectPath(distDir, 'export-detail.json')).then(it => JSON.parse(it.toString()), () => { success: false });
    if (exportDetailJson.success) {<commit_msg>Better error on next build failure<commit_after>// Copyright 2022 Google LLC
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { readFile, mkdir, copyFile, stat } from 'fs/promises';
import { dirname, extname, join } from 'path';
import type { Header, Rewrite, Redirect } from 'next/dist/lib/load-custom-routes.js';
import type { NextConfig } from 'next';
import { copy } from 'fs-extra';
import { pathToFileURL } from 'url';

import { Commands, DeployConfig, PathFactory, spawn } from '../../utils.js';
import { existsSync } from 'fs';

export const build = async (config: DeployConfig | Required<DeployConfig>, getProjectPath: PathFactory) => {

    const { default: { default: nextBuild } } = await import('next/dist/build/index.js');

    let nextConfig: NextConfig|undefined = undefined;

    if (existsSync(getProjectPath('next.config.js'))) {

        try {
            const { default: { default: loadConfig } } = await import('next/dist/server/config.js');
            const { PHASE_PRODUCTION_BUILD } = await import('next/constants.js');
            nextConfig = await loadConfig(PHASE_PRODUCTION_BUILD, getProjectPath(), null);
        } catch(e) { }

        // Try just importing it, incase of Next 11
        try {
            nextConfig = await import(pathToFileURL(getProjectPath('next.config.js')).toString());
        } catch(e) { }

        if (!nextConfig) throw new Error('Unable to load next.config.js.');

    } else {

        nextConfig = {};

    }

    // SEMVER these defaults are only needed for Next 11
    const { distDir='.next', basePath='' } = nextConfig;

    const deployPath = (...args: string[]) => config.dist ? join(config.dist, ...args) : getProjectPath('.deploy', ...args);
    const getHostingPath = (...args: string[]) => deployPath('hosting', ...basePath.split('/'), ...args);

    await nextBuild(getProjectPath(), null, false, false, true).catch(e => {
        console.error(e.message);
        throw e;
    });

    try {
        // Using spawn here, rather than their programatic API because I can't silence it
        // Failures with Next export are expected, we're just trying to do it if we can
        await spawn(Commands.next, ['export', '-o', getHostingPath()], { cwd: getProjectPath() });
    } catch(e) { }

    let usingCloudFunctions = !!config.function;
    const asyncSteps: Array<Promise<any>> = [];

    const exportDetailJson = await readFile(getProjectPath(distDir, 'export-detail.json')).then(it => JSON.parse(it.toString()), () => { success: false });
    if (exportDetailJson.success) {
{'fraction_non_alphanumeric': 0.13768617942500866, 'fraction_numerical': 0.004156563907170073, 'mean_word_length': 3.378316906747536, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4716234', 'n_tokens_mistral': 1650, 'n_tokens_neox': 1611, 'n_words': 542}