@nx/next
Version:
177 lines (176 loc) • 7.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNodes = exports.createNodesV2 = exports.createDependencies = void 0;
const devkit_1 = require("@nx/devkit");
const path_1 = require("path");
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
const fs_1 = require("fs");
const cache_directory_1 = require("nx/src/utils/cache-directory");
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
const js_1 = require("@nx/js");
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
const devkit_internals_1 = require("nx/src/devkit-internals");
const util_1 = require("@nx/js/src/plugins/typescript/util");
const pmc = (0, devkit_1.getPackageManagerCommand)();
const nextConfigBlob = '**/next.config.{ts,js,cjs,mjs}';
function readTargetsCache(cachePath) {
return (0, fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : {};
}
function writeTargetsToCache(cachePath, targetsCache) {
const oldCache = readTargetsCache(cachePath);
(0, devkit_1.writeJsonFile)(cachePath, {
...oldCache,
...targetsCache,
});
}
/**
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
*/
const createDependencies = () => {
return [];
};
exports.createDependencies = createDependencies;
exports.createNodesV2 = [
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 = readTargetsCache(cachePath);
try {
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => createNodesInternal(configFile, options, context, targetsCache), configFiles, options, context);
}
finally {
writeTargetsToCache(cachePath, targetsCache);
}
},
];
/**
* @deprecated This is replaced with {@link createNodesV2}. Update your plugin to export its own `createNodesV2` function that wraps this one instead.
* This function will change to the v2 function in Nx 21.
*/
exports.createNodes = [
nextConfigBlob,
async (configFilePath, options, context) => {
devkit_1.logger.warn('`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 21, this will change to the createNodesV2 API.');
const optionsHash = (0, devkit_internals_1.hashObject)(options);
const cachePath = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, `next-${optionsHash}.json`);
const targetsCache = readTargetsCache(cachePath);
const result = await createNodesInternal(configFilePath, options, context, targetsCache);
writeTargetsToCache(cachePath, targetsCache);
return result;
},
];
async function createNodesInternal(configFilePath, options, context, targetsCache) {
const projectRoot = (0, path_1.dirname)(configFilePath);
// Do not create a project if package.json and project.json isn't there.
const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
if (!siblingFiles.includes('package.json') &&
!siblingFiles.includes('project.json')) {
return {};
}
options = normalizeOptions(options);
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
targetsCache[hash] ??= await buildNextTargets(configFilePath, projectRoot, options, context);
return {
projects: {
[projectRoot]: {
root: projectRoot,
targets: targetsCache[hash],
},
},
};
}
async function buildNextTargets(nextConfigPath, projectRoot, options, context) {
const nextConfig = await getNextConfig(nextConfigPath, context);
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
const targets = {};
targets[options.buildTargetName] = await getBuildTargetConfig(namedInputs, projectRoot, nextConfig);
targets[options.devTargetName] = getDevTargetConfig(projectRoot);
const startTarget = getStartTargetConfig(options, projectRoot);
targets[options.startTargetName] = startTarget;
targets[options.serveStaticTargetName] = startTarget;
(0, util_1.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, options, pmc);
return targets;
}
async function getBuildTargetConfig(namedInputs, projectRoot, nextConfig) {
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)`],
};
// TODO(ndcunningham): Update this to be consider different versions of next.js which is running
// This doesn't actually need to be tty, but next.js has a bug, https://github.com/vercel/next.js/issues/62906, where it exits 0 when SIGINT is sent.
targetConfig.options.tty = false;
return targetConfig;
}
function getDevTargetConfig(projectRoot) {
const targetConfig = {
continuous: true,
command: `next dev`,
options: {
cwd: projectRoot,
},
};
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, config_utils_1.loadConfigFile)(resolvedPath);
}
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', '^production']
: ['default', '^default']),
{
externalDependencies: ['next'],
},
];
}