UNPKG

@nx/next

Version:

The Next.js plugin for Nx contains executors and generators for managing Next.js applications and libraries within an Nx workspace. It provides: - Scaffolding for creating, building, serving, linting, and testing Next.js applications. - Integration wit

202 lines (201 loc) • 8.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNodesV2 = exports.createNodes = exports.createDependencies = void 0; const internal_1 = require("@nx/devkit/internal"); const devkit_1 = require("@nx/devkit"); const js_1 = require("@nx/js"); const internal_2 = require("@nx/js/internal"); const fs_1 = require("fs"); const devkit_internals_1 = require("nx/src/devkit-internals"); const cache_directory_1 = require("nx/src/utils/cache-directory"); const path_1 = require("path"); const nextConfigBlob = '**/next.config.{ts,js,cjs,mjs}'; /** * @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'. */ const createDependencies = () => { return []; }; exports.createDependencies = createDependencies; exports.createNodes = [ nextConfigBlob, async (configFiles, options, context) => { const optionsHash = (0, devkit_internals_1.hashObject)(options); const cachePath = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `next-${optionsHash}.json`); const targetsCache = new internal_1.PluginCache(cachePath); const isTsSolutionSetup = (0, internal_2.isUsingTsSolutionSetup)(); const packageManager = (0, devkit_1.detectPackageManager)(context.workspaceRoot); const pmc = (0, devkit_1.getPackageManagerCommand)(packageManager); const lockFileName = (0, js_1.getLockFileName)(packageManager); const normalizedOptions = normalizeOptions(options); try { const { entries, preErrors } = await filterNextConfigs(configFiles, context); const projectHashes = await (0, internal_1.calculateHashesForCreateNodes)(entries.map((e) => e.projectRoot), normalizedOptions, context, entries.map(() => [lockFileName])); let results = []; let nodeErrors = []; try { results = await (0, devkit_1.createNodesFromFiles)((configFile, _, ctx, idx) => createNodesInternal(configFile, normalizedOptions, ctx, targetsCache, isTsSolutionSetup, pmc, projectHashes[idx]), entries.map((e) => e.configFile), options, context); } catch (e) { if (e instanceof devkit_1.AggregateCreateNodesError) { results = e.partialResults ?? []; nodeErrors = e.errors; } else { throw e; } } const allErrors = [...preErrors, ...nodeErrors]; if (allErrors.length > 0) { throw new devkit_1.AggregateCreateNodesError(allErrors, results); } return results; } finally { targetsCache.writeToDisk(); } }, ]; exports.createNodesV2 = exports.createNodes; async function createNodesInternal(configFilePath, options, context, targetsCache, isTsSolutionSetup, pmc, hash) { const projectRoot = (0, path_1.dirname)(configFilePath); if (!targetsCache.has(hash)) { targetsCache.set(hash, await buildNextTargets(configFilePath, projectRoot, options, context, isTsSolutionSetup, pmc)); } return { projects: { [projectRoot]: { root: projectRoot, targets: targetsCache.get(hash), }, }, }; } async function buildNextTargets(nextConfigPath, projectRoot, options, context, isTsSolutionSetup, pmc) { const nextConfig = await getNextConfig(nextConfigPath, context); const namedInputs = (0, internal_1.getNamedInputs)(projectRoot, context); const targets = {}; targets[options.buildTargetName] = await getBuildTargetConfig(namedInputs, projectRoot, nextConfig, isTsSolutionSetup); targets[options.devTargetName] = getDevTargetConfig(projectRoot, isTsSolutionSetup); const startTarget = getStartTargetConfig(options, projectRoot); targets[options.startTargetName] = startTarget; targets[options.serveStaticTargetName] = startTarget; (0, internal_2.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, options, pmc); return targets; } async function getBuildTargetConfig(namedInputs, projectRoot, nextConfig, isTsSolutionSetup) { const nextOutputPath = await getOutputs(projectRoot, nextConfig); // Set output path here so that `withNx` can pick it up. const targetConfig = { command: `next build`, options: { cwd: projectRoot, }, dependsOn: ['^build'], cache: true, inputs: getInputs(namedInputs), outputs: [`${nextOutputPath}/!(cache)/**/*`, `${nextOutputPath}/!(cache)`], }; // v14/v15/v16 all emit the same `next build` inferred target shape - no per-major branch needed. // The tty=false is not version-specific: Next.js exits 0 on SIGINT regardless of major // (https://github.com/vercel/next.js/issues/62906). Turbopack-default-on (v16+) and other // bundler differences live at the executor / runtime layer, not here. targetConfig.options.tty = false; if (isTsSolutionSetup) { targetConfig.syncGenerators = ['@nx/js:typescript-sync']; } return targetConfig; } function getDevTargetConfig(projectRoot, isTsSolutionSetup) { const targetConfig = { continuous: true, command: `next dev`, options: { cwd: projectRoot, }, }; if (isTsSolutionSetup) { targetConfig.syncGenerators = ['@nx/js:typescript-sync']; } return targetConfig; } function getStartTargetConfig(options, projectRoot) { const targetConfig = { continuous: true, command: `next start`, options: { cwd: projectRoot, }, dependsOn: [options.buildTargetName], }; return targetConfig; } async function getOutputs(projectRoot, nextConfig) { let dir = '.next'; const { PHASE_PRODUCTION_BUILD } = require('next/constants'); if (typeof nextConfig === 'function') { // Works for both async and sync functions. const configResult = await Promise.resolve(nextConfig(PHASE_PRODUCTION_BUILD, { defaultConfig: {} })); if (configResult?.distDir) { dir = configResult?.distDir; } } else if (typeof nextConfig === 'object' && nextConfig?.distDir) { // If nextConfig is an object, directly use its 'distDir' property. dir = nextConfig.distDir; } if (projectRoot === '.') { return `{projectRoot}/${dir}`; } else { return `{workspaceRoot}/${projectRoot}/${dir}`; } } function getNextConfig(configFilePath, context) { const resolvedPath = (0, path_1.join)(context.workspaceRoot, configFilePath); return (0, internal_1.loadConfigFile)(resolvedPath); } async function filterNextConfigs(configFiles, context) { const preErrors = []; const candidates = await Promise.all(configFiles.map(async (configFile) => { try { const projectRoot = (0, path_1.dirname)(configFile); const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot)); if (!siblingFiles.includes('package.json') && !siblingFiles.includes('project.json')) { return null; } return { configFile, projectRoot }; } catch (e) { preErrors.push([configFile, e]); return null; } })); return { entries: candidates.filter((c) => c !== null), preErrors, }; } function normalizeOptions(options) { options ??= {}; options.buildTargetName ??= 'build'; options.devTargetName ??= 'dev'; options.startTargetName ??= 'start'; options.serveStaticTargetName ??= 'serve-static'; return options; } function getInputs(namedInputs) { return [ ...('production' in namedInputs ? ['default', '^default'] : ['default', '^default']), { externalDependencies: ['next'], }, { dependentTasksOutputFiles: '**/*.d.ts', transitive: true, }, ]; }