|
1 | 1 | import {
|
| 2 | + createNodesFromFiles, |
2 | 3 | detectPackageManager,
|
3 | 4 | joinPathFragments,
|
| 5 | + logger, |
4 | 6 | normalizePath,
|
5 | 7 | readJsonFile,
|
6 | 8 | writeJsonFile,
|
7 | 9 | type CreateDependencies,
|
8 | 10 | type CreateNodes,
|
9 | 11 | type CreateNodesContext,
|
| 12 | + type CreateNodesResult, |
| 13 | + type CreateNodesV2, |
10 | 14 | type NxJsonConfiguration,
|
| 15 | + type ProjectConfiguration, |
11 | 16 | type TargetConfiguration,
|
12 | 17 | } from '@nx/devkit';
|
13 | 18 | import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
14 | 19 | import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
| 20 | +import { minimatch } from 'minimatch'; |
15 | 21 | import { existsSync, readdirSync, statSync } from 'node:fs';
|
16 | 22 | import { basename, dirname, join, relative } from 'node:path';
|
17 |
| -import { minimatch } from 'minimatch'; |
| 23 | +import { hashObject } from 'nx/src/hasher/file-hasher'; |
18 | 24 | // eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
19 | 25 | import { getLockFileName } from 'nx/src/plugins/js/lock-file/lock-file';
|
20 | 26 | import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
@@ -49,84 +55,118 @@ interface NormalizedPluginOptions {
|
49 | 55 | };
|
50 | 56 | }
|
51 | 57 |
|
52 |
| -const cachePath = join(workspaceDataDirectory, 'tsc.hash'); |
53 |
| -const targetsCache = readTargetsCache(); |
| 58 | +type TscProjectResult = Pick<ProjectConfiguration, 'targets'>; |
54 | 59 |
|
55 |
| -function readTargetsCache(): Record< |
56 |
| - string, |
57 |
| - Record<string, TargetConfiguration<unknown>> |
58 |
| -> { |
| 60 | +function readTargetsCache(cachePath: string): Record<string, TscProjectResult> { |
59 | 61 | return existsSync(cachePath) ? readJsonFile(cachePath) : {};
|
60 | 62 | }
|
61 | 63 |
|
62 |
| -function writeTargetsToCache() { |
63 |
| - const oldCache = readTargetsCache(); |
64 |
| - writeJsonFile(cachePath, { |
65 |
| - ...oldCache, |
66 |
| - ...targetsCache, |
67 |
| - }); |
| 64 | +function writeTargetsToCache( |
| 65 | + cachePath: string, |
| 66 | + results?: Record<string, TscProjectResult> |
| 67 | +) { |
| 68 | + writeJsonFile(cachePath, results); |
68 | 69 | }
|
69 | 70 |
|
| 71 | +/** |
| 72 | + * @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'. |
| 73 | + */ |
70 | 74 | export const createDependencies: CreateDependencies = () => {
|
71 |
| - writeTargetsToCache(); |
72 | 75 | return [];
|
73 | 76 | };
|
74 | 77 |
|
75 | 78 | export const PLUGIN_NAME = '@nx/js/typescript';
|
76 | 79 |
|
| 80 | +const tsConfigGlob = '**/tsconfig*.json'; |
| 81 | + |
| 82 | +export const createNodesV2: CreateNodesV2<TscPluginOptions> = [ |
| 83 | + tsConfigGlob, |
| 84 | + async (configFilePaths, options, context) => { |
| 85 | + const optionsHash = hashObject(options); |
| 86 | + const cachePath = join(workspaceDataDirectory, `tsc-${optionsHash}.hash`); |
| 87 | + const targetsCache = readTargetsCache(cachePath); |
| 88 | + const normalizedOptions = normalizePluginOptions(options); |
| 89 | + try { |
| 90 | + return await createNodesFromFiles( |
| 91 | + (configFile, options, context) => |
| 92 | + createNodesInternal(configFile, options, context, targetsCache), |
| 93 | + configFilePaths, |
| 94 | + normalizedOptions, |
| 95 | + context |
| 96 | + ); |
| 97 | + } finally { |
| 98 | + writeTargetsToCache(cachePath, targetsCache); |
| 99 | + } |
| 100 | + }, |
| 101 | +]; |
| 102 | + |
77 | 103 | export const createNodes: CreateNodes<TscPluginOptions> = [
|
78 |
| - '**/tsconfig*.json', |
| 104 | + tsConfigGlob, |
79 | 105 | async (configFilePath, options, context) => {
|
80 |
| - const pluginOptions = normalizePluginOptions(options); |
81 |
| - const projectRoot = dirname(configFilePath); |
82 |
| - const fullConfigPath = joinPathFragments( |
83 |
| - context.workspaceRoot, |
84 |
| - configFilePath |
| 106 | + logger.warn( |
| 107 | + '`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.' |
85 | 108 | );
|
| 109 | + const normalizedOptions = normalizePluginOptions(options); |
| 110 | + return createNodesInternal(configFilePath, normalizedOptions, context, {}); |
| 111 | + }, |
| 112 | +]; |
86 | 113 |
|
87 |
| - // Do not create a project if package.json and project.json isn't there. |
88 |
| - const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot)); |
89 |
| - if ( |
90 |
| - !siblingFiles.includes('package.json') && |
91 |
| - !siblingFiles.includes('project.json') |
92 |
| - ) { |
93 |
| - return {}; |
94 |
| - } |
| 114 | +async function createNodesInternal( |
| 115 | + configFilePath: string, |
| 116 | + options: NormalizedPluginOptions, |
| 117 | + context: CreateNodesContext, |
| 118 | + targetsCache: Record<string, TscProjectResult> |
| 119 | +): Promise<CreateNodesResult> { |
| 120 | + const projectRoot = dirname(configFilePath); |
| 121 | + const fullConfigPath = joinPathFragments( |
| 122 | + context.workspaceRoot, |
| 123 | + configFilePath |
| 124 | + ); |
| 125 | + |
| 126 | + // Do not create a project if package.json and project.json isn't there. |
| 127 | + const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot)); |
| 128 | + if ( |
| 129 | + !siblingFiles.includes('package.json') && |
| 130 | + !siblingFiles.includes('project.json') |
| 131 | + ) { |
| 132 | + return {}; |
| 133 | + } |
95 | 134 |
|
96 |
| - // Do not create a project if it's not a tsconfig.json and there is no tsconfig.json in the same directory |
97 |
| - if ( |
98 |
| - basename(configFilePath) !== 'tsconfig.json' && |
99 |
| - !siblingFiles.includes('tsconfig.json') |
100 |
| - ) { |
101 |
| - return {}; |
102 |
| - } |
| 135 | + // Do not create a project if it's not a tsconfig.json and there is no tsconfig.json in the same directory |
| 136 | + if ( |
| 137 | + basename(configFilePath) !== 'tsconfig.json' && |
| 138 | + !siblingFiles.includes('tsconfig.json') |
| 139 | + ) { |
| 140 | + return {}; |
| 141 | + } |
103 | 142 |
|
104 |
| - const nodeHash = await calculateHashForCreateNodes( |
105 |
| - projectRoot, |
106 |
| - pluginOptions, |
107 |
| - context, |
108 |
| - [getLockFileName(detectPackageManager(context.workspaceRoot))] |
109 |
| - ); |
110 |
| - // The hash is calculated at the node/project level, so we add the config file path to avoid conflicts when caching |
111 |
| - const cacheKey = `${nodeHash}_${configFilePath}`; |
112 |
| - |
113 |
| - targetsCache[cacheKey] ??= buildTscTargets( |
114 |
| - fullConfigPath, |
115 |
| - projectRoot, |
116 |
| - pluginOptions, |
117 |
| - context |
118 |
| - ); |
| 143 | + const nodeHash = await calculateHashForCreateNodes( |
| 144 | + projectRoot, |
| 145 | + options, |
| 146 | + context, |
| 147 | + [getLockFileName(detectPackageManager(context.workspaceRoot))] |
| 148 | + ); |
| 149 | + // The hash is calculated at the node/project level, so we add the config file path to avoid conflicts when caching |
| 150 | + const cacheKey = `${nodeHash}_${configFilePath}`; |
| 151 | + |
| 152 | + targetsCache[cacheKey] ??= buildTscTargets( |
| 153 | + fullConfigPath, |
| 154 | + projectRoot, |
| 155 | + options, |
| 156 | + context |
| 157 | + ); |
119 | 158 |
|
120 |
| - return { |
121 |
| - projects: { |
122 |
| - [projectRoot]: { |
123 |
| - projectType: 'library', |
124 |
| - targets: targetsCache[cacheKey], |
125 |
| - }, |
| 159 | + const { targets } = targetsCache[cacheKey]; |
| 160 | + |
| 161 | + return { |
| 162 | + projects: { |
| 163 | + [projectRoot]: { |
| 164 | + projectType: 'library', |
| 165 | + targets, |
126 | 166 | },
|
127 |
| - }; |
128 |
| - }, |
129 |
| -]; |
| 167 | + }, |
| 168 | + }; |
| 169 | +} |
130 | 170 |
|
131 | 171 | function buildTscTargets(
|
132 | 172 | configFilePath: string,
|
@@ -220,7 +260,7 @@ function buildTscTargets(
|
220 | 260 | };
|
221 | 261 | }
|
222 | 262 |
|
223 |
| - return targets; |
| 263 | + return { targets }; |
224 | 264 | }
|
225 | 265 |
|
226 | 266 | function getInputs(
|
|
0 commit comments