feat(webpack): add createNodesV2 for plugin (#26588)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> There is no implementation for `createNodesV2`. ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> There should be an implementation for `createNodesV2`. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
df3c7522ea
commit
95a6e6864d
@ -1 +1,5 @@
|
||||
export { createNodes } from './src/plugins/plugin';
|
||||
export {
|
||||
createNodes,
|
||||
createNodesV2,
|
||||
type WebpackPluginOptions,
|
||||
} from './src/plugins/plugin';
|
||||
|
||||
@ -1,63 +1,69 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`@nx/webpack/plugin should create nodes 1`] = `
|
||||
{
|
||||
"projects": {
|
||||
"my-app": {
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build-something": {
|
||||
"cache": true,
|
||||
"command": "webpack-cli build",
|
||||
"dependsOn": [
|
||||
"^build-something",
|
||||
],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"webpack-cli",
|
||||
[
|
||||
[
|
||||
"my-app/webpack.config.js",
|
||||
{
|
||||
"projects": {
|
||||
"my-app": {
|
||||
"metadata": {},
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build-something": {
|
||||
"cache": true,
|
||||
"command": "webpack-cli build",
|
||||
"dependsOn": [
|
||||
"^build-something",
|
||||
],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
"webpack-cli",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=production",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
"outputs": [
|
||||
"{projectRoot}/dist/foo",
|
||||
],
|
||||
},
|
||||
],
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=production",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
"outputs": [
|
||||
"{projectRoot}/dist/foo",
|
||||
],
|
||||
},
|
||||
"my-serve": {
|
||||
"command": "webpack-cli serve",
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=development",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
},
|
||||
"preview-site": {
|
||||
"command": "webpack-cli serve",
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=production",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
},
|
||||
"serve-static": {
|
||||
"executor": "@nx/web:file-server",
|
||||
"options": {
|
||||
"buildTarget": "build-something",
|
||||
"spa": true,
|
||||
"my-serve": {
|
||||
"command": "webpack-cli serve",
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=development",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
},
|
||||
"preview-site": {
|
||||
"command": "webpack-cli serve",
|
||||
"options": {
|
||||
"args": [
|
||||
"--node-env=production",
|
||||
],
|
||||
"cwd": "my-app",
|
||||
},
|
||||
},
|
||||
"serve-static": {
|
||||
"executor": "@nx/web:file-server",
|
||||
"options": {
|
||||
"buildTarget": "build-something",
|
||||
"spa": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { CreateNodesContext } from '@nx/devkit';
|
||||
import { createNodes } from './plugin';
|
||||
import { createNodesV2 } from './plugin';
|
||||
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('@nx/webpack/plugin', () => {
|
||||
let createNodesFunction = createNodes[1];
|
||||
let createNodesFunction = createNodesV2[1];
|
||||
let context: CreateNodesContext;
|
||||
let tempFs: TempFs;
|
||||
|
||||
@ -40,7 +40,7 @@ describe('@nx/webpack/plugin', () => {
|
||||
},
|
||||
});
|
||||
const nodes = await createNodesFunction(
|
||||
'my-app/webpack.config.js',
|
||||
['my-app/webpack.config.js'],
|
||||
{
|
||||
buildTargetName: 'build-something',
|
||||
serveTargetName: 'my-serve',
|
||||
|
||||
@ -2,22 +2,26 @@ import {
|
||||
CreateDependencies,
|
||||
CreateNodes,
|
||||
CreateNodesContext,
|
||||
createNodesFromFiles,
|
||||
CreateNodesResult,
|
||||
CreateNodesV2,
|
||||
detectPackageManager,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
readJsonFile,
|
||||
TargetConfiguration,
|
||||
workspaceRoot,
|
||||
writeJsonFile,
|
||||
} from '@nx/devkit';
|
||||
import { dirname, isAbsolute, join, relative, resolve } from 'path';
|
||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||
import { WebpackExecutorOptions } from '../executors/webpack/schema';
|
||||
import { WebDevServerOptions } from '../executors/dev-server/schema';
|
||||
import { getLockFileName, getRootTsConfigPath } from '@nx/js';
|
||||
import { existsSync, readdirSync } from 'fs';
|
||||
import { hashObject } from 'nx/src/hasher/file-hasher';
|
||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||
import { dirname, isAbsolute, join, relative, resolve } from 'path';
|
||||
import { readWebpackOptions } from '../utils/webpack/read-webpack-options';
|
||||
import { resolveUserDefinedWebpackConfig } from '../utils/webpack/resolve-user-defined-webpack-config';
|
||||
import { getLockFileName, getRootTsConfigPath } from '@nx/js';
|
||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||
|
||||
export interface WebpackPluginOptions {
|
||||
buildTargetName?: string;
|
||||
@ -26,86 +30,113 @@ export interface WebpackPluginOptions {
|
||||
previewTargetName?: string;
|
||||
}
|
||||
|
||||
const cachePath = join(workspaceDataDirectory, 'webpack.hash');
|
||||
const targetsCache = readTargetsCache();
|
||||
type WebpackTargets = Pick<ProjectConfiguration, 'targets' | 'metadata'>;
|
||||
|
||||
function readTargetsCache(): Record<
|
||||
string,
|
||||
Record<string, TargetConfiguration>
|
||||
> {
|
||||
function readTargetsCache(cachePath: string): Record<string, WebpackTargets> {
|
||||
return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
||||
}
|
||||
|
||||
function writeTargetsToCache() {
|
||||
const oldCache = readTargetsCache();
|
||||
writeJsonFile(cachePath, {
|
||||
...oldCache,
|
||||
...targetsCache,
|
||||
});
|
||||
function writeTargetsToCache(
|
||||
cachePath: string,
|
||||
results?: Record<string, WebpackTargets>
|
||||
) {
|
||||
writeJsonFile(cachePath, results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
|
||||
*/
|
||||
export const createDependencies: CreateDependencies = () => {
|
||||
writeTargetsToCache();
|
||||
return [];
|
||||
};
|
||||
|
||||
export const createNodes: CreateNodes<WebpackPluginOptions> = [
|
||||
'**/webpack.config.{js,ts,mjs,cjs}',
|
||||
async (configFilePath, options, context) => {
|
||||
options ??= {};
|
||||
options.buildTargetName ??= 'build';
|
||||
options.serveTargetName ??= 'serve';
|
||||
options.serveStaticTargetName ??= 'serve-static';
|
||||
options.previewTargetName ??= 'preview';
|
||||
const webpackConfigGlob = '**/webpack.config.{js,ts,mjs,cjs}';
|
||||
|
||||
const projectRoot = dirname(configFilePath);
|
||||
|
||||
// Do not create a project if package.json and project.json isn't there.
|
||||
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
|
||||
if (
|
||||
!siblingFiles.includes('package.json') &&
|
||||
!siblingFiles.includes('project.json')
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
export const createNodesV2: CreateNodesV2<WebpackPluginOptions> = [
|
||||
webpackConfigGlob,
|
||||
async (configFilePaths, options, context) => {
|
||||
const optionsHash = hashObject(options);
|
||||
const cachePath = join(
|
||||
workspaceDataDirectory,
|
||||
`webpack-${optionsHash}.hash`
|
||||
);
|
||||
const targets = targetsCache[hash]
|
||||
? targetsCache[hash]
|
||||
: await createWebpackTargets(
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
options,
|
||||
context
|
||||
);
|
||||
|
||||
return {
|
||||
projects: {
|
||||
[projectRoot]: {
|
||||
projectType: 'application',
|
||||
targets,
|
||||
},
|
||||
},
|
||||
};
|
||||
const targetsCache = readTargetsCache(cachePath);
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
try {
|
||||
return await createNodesFromFiles(
|
||||
(configFile, options, context) =>
|
||||
createNodesInternal(configFile, options, context, targetsCache),
|
||||
configFilePaths,
|
||||
normalizedOptions,
|
||||
context
|
||||
);
|
||||
} finally {
|
||||
writeTargetsToCache(cachePath, targetsCache);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
export const createNodes: CreateNodes<WebpackPluginOptions> = [
|
||||
webpackConfigGlob,
|
||||
async (configFilePath, options, context) => {
|
||||
logger.warn(
|
||||
'`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'
|
||||
);
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
return createNodesInternal(configFilePath, normalizedOptions, context, {});
|
||||
},
|
||||
];
|
||||
|
||||
async function createNodesInternal(
|
||||
configFilePath: string,
|
||||
options: Required<WebpackPluginOptions>,
|
||||
context: CreateNodesContext,
|
||||
targetsCache: Record<string, WebpackTargets>
|
||||
): Promise<CreateNodesResult> {
|
||||
const projectRoot = dirname(configFilePath);
|
||||
|
||||
// Do not create a project if package.json and project.json isn't there.
|
||||
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
|
||||
if (
|
||||
!siblingFiles.includes('package.json') &&
|
||||
!siblingFiles.includes('project.json')
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= await createWebpackTargets(
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
options,
|
||||
context
|
||||
);
|
||||
|
||||
const { targets, metadata } = targetsCache[hash];
|
||||
|
||||
return {
|
||||
projects: {
|
||||
[projectRoot]: {
|
||||
projectType: 'application',
|
||||
targets,
|
||||
metadata,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function createWebpackTargets(
|
||||
configFilePath: string,
|
||||
projectRoot: string,
|
||||
options: WebpackPluginOptions,
|
||||
options: Required<WebpackPluginOptions>,
|
||||
context: CreateNodesContext
|
||||
): Promise<
|
||||
Record<
|
||||
string,
|
||||
TargetConfiguration<WebpackExecutorOptions | WebDevServerOptions>
|
||||
>
|
||||
> {
|
||||
): Promise<WebpackTargets> {
|
||||
const namedInputs = getNamedInputs(projectRoot, context);
|
||||
|
||||
const webpackConfig = resolveUserDefinedWebpackConfig(
|
||||
@ -121,7 +152,7 @@ async function createWebpackTargets(
|
||||
projectRoot
|
||||
);
|
||||
|
||||
const targets = {};
|
||||
const targets: Record<string, TargetConfiguration> = {};
|
||||
|
||||
targets[options.buildTargetName] = {
|
||||
command: `webpack-cli build`,
|
||||
@ -171,7 +202,7 @@ async function createWebpackTargets(
|
||||
},
|
||||
};
|
||||
|
||||
return targets;
|
||||
return { targets, metadata: {} };
|
||||
}
|
||||
|
||||
function normalizeOutputPath(
|
||||
@ -204,3 +235,14 @@ function normalizeOutputPath(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeOptions(
|
||||
options: WebpackPluginOptions
|
||||
): Required<WebpackPluginOptions> {
|
||||
return {
|
||||
buildTargetName: options.buildTargetName ?? 'build',
|
||||
serveTargetName: options.serveTargetName ?? 'serve',
|
||||
serveStaticTargetName: options.serveStaticTargetName ?? 'serve-static',
|
||||
previewTargetName: options.previewTargetName ?? 'preview',
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user