@nx/angular 19.7.4 → 19.8.0-beta.1
Sign up to get free protection for your applications and to get access to all the features.
- package/package.json +11 -10
- package/plugin.d.ts +1 -0
- package/plugin.js +5 -0
- package/src/generators/add-linting/add-linting.js +88 -33
- package/src/generators/add-linting/lib/add-angular-eslint-dependencies.js +10 -5
- package/src/generators/init/init.js +12 -0
- package/src/generators/init/schema.d.ts +3 -0
- package/src/plugins/plugin.d.ts +19 -0
- package/src/plugins/plugin.js +418 -0
- package/src/utils/versions.d.ts +1 -1
- package/src/utils/versions.js +1 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@nx/angular",
|
3
|
-
"version": "19.
|
3
|
+
"version": "19.8.0-beta.1",
|
4
4
|
"private": false,
|
5
5
|
"description": "The Nx Plugin for Angular contains executors, generators, and utilities for managing Angular applications and libraries within an Nx workspace. It provides: \n\n- Integration with libraries such as Storybook, Jest, ESLint, Tailwind CSS, Playwright and Cypress. \n\n- Generators to help scaffold code quickly (like: Micro Frontends, Libraries, both internal to your codebase and publishable to npm) \n\n- Single Component Application Modules (SCAMs) \n\n- NgRx helpers. \n\n- Utilities for automatic workspace refactoring.",
|
6
6
|
"repository": {
|
@@ -22,6 +22,7 @@
|
|
22
22
|
"./executors.json": "./executors.json",
|
23
23
|
"./generators": "./generators.js",
|
24
24
|
"./executors": "./executors.js",
|
25
|
+
"./plugin": "./plugin.js",
|
25
26
|
"./tailwind": "./tailwind.js",
|
26
27
|
"./module-federation": "./module-federation/index.js",
|
27
28
|
"./src/utils": "./src/utils/index.js",
|
@@ -69,7 +70,7 @@
|
|
69
70
|
},
|
70
71
|
"dependencies": {
|
71
72
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
72
|
-
"@typescript-eslint/type-utils": "^
|
73
|
+
"@typescript-eslint/type-utils": "^8.0.0",
|
73
74
|
"chalk": "^4.1.0",
|
74
75
|
"find-cache-dir": "^3.3.2",
|
75
76
|
"magic-string": "~0.30.2",
|
@@ -79,14 +80,14 @@
|
|
79
80
|
"webpack-merge": "^5.8.0",
|
80
81
|
"webpack": "^5.88.0",
|
81
82
|
"@module-federation/enhanced": "~0.6.0",
|
82
|
-
"@nx/devkit": "19.
|
83
|
-
"@nx/js": "19.
|
84
|
-
"@nx/eslint": "19.
|
85
|
-
"@nx/webpack": "19.
|
86
|
-
"@nx/web": "19.
|
87
|
-
"@nx/workspace": "19.
|
83
|
+
"@nx/devkit": "19.8.0-beta.1",
|
84
|
+
"@nx/js": "19.8.0-beta.1",
|
85
|
+
"@nx/eslint": "19.8.0-beta.1",
|
86
|
+
"@nx/webpack": "19.8.0-beta.1",
|
87
|
+
"@nx/web": "19.8.0-beta.1",
|
88
|
+
"@nx/workspace": "19.8.0-beta.1",
|
88
89
|
"piscina": "^4.4.0",
|
89
|
-
"@nrwl/angular": "19.
|
90
|
+
"@nrwl/angular": "19.8.0-beta.1"
|
90
91
|
},
|
91
92
|
"peerDependencies": {
|
92
93
|
"@angular-devkit/build-angular": ">= 16.0.0 < 19.0.0",
|
@@ -101,4 +102,4 @@
|
|
101
102
|
"module": "fesm2022/nx-angular.mjs",
|
102
103
|
"typings": "index.d.ts",
|
103
104
|
"sideEffects": false
|
104
|
-
}
|
105
|
+
}
|
package/plugin.d.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export { createNodesV2 } from './src/plugins/plugin';
|
package/plugin.js
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.createNodesV2 = void 0;
|
4
|
+
var plugin_1 = require("./src/plugins/plugin");
|
5
|
+
Object.defineProperty(exports, "createNodesV2", { enumerable: true, get: function () { return plugin_1.createNodesV2; } });
|
@@ -8,6 +8,7 @@ const global_eslint_config_1 = require("@nx/eslint/src/generators/init/global-es
|
|
8
8
|
const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
|
9
9
|
const add_angular_eslint_dependencies_1 = require("./lib/add-angular-eslint-dependencies");
|
10
10
|
const buildable_project_1 = require("./lib/buildable-project");
|
11
|
+
const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
|
11
12
|
async function addLintingGenerator(tree, options) {
|
12
13
|
const tasks = [];
|
13
14
|
const rootProject = options.projectRoot === '.' || options.projectRoot === '';
|
@@ -32,21 +33,11 @@ async function addLintingGenerator(tree, options) {
|
|
32
33
|
const hasParserOptions = tree
|
33
34
|
.read((0, devkit_1.joinPathFragments)(options.projectRoot, eslintFile), 'utf8')
|
34
35
|
.includes(`${options.projectRoot}/tsconfig.*?.json`);
|
35
|
-
(0,
|
36
|
-
|
37
|
-
|
36
|
+
if ((0, flat_config_1.useFlatConfig)(tree)) {
|
37
|
+
(0, eslint_file_1.addPredefinedConfigToFlatLintConfig)(tree, options.projectRoot, 'flat/angular');
|
38
|
+
(0, eslint_file_1.addPredefinedConfigToFlatLintConfig)(tree, options.projectRoot, 'flat/angular-template');
|
39
|
+
(0, eslint_file_1.addOverrideToLintConfig)(tree, options.projectRoot, {
|
38
40
|
files: ['*.ts'],
|
39
|
-
...(hasParserOptions
|
40
|
-
? {
|
41
|
-
parserOptions: {
|
42
|
-
project: [`${options.projectRoot}/tsconfig.*?.json`],
|
43
|
-
},
|
44
|
-
}
|
45
|
-
: {}),
|
46
|
-
extends: [
|
47
|
-
'plugin:@nx/angular',
|
48
|
-
'plugin:@angular-eslint/template/process-inline-templates',
|
49
|
-
],
|
50
41
|
rules: {
|
51
42
|
'@angular-eslint/directive-selector': [
|
52
43
|
'error',
|
@@ -65,28 +56,92 @@ async function addLintingGenerator(tree, options) {
|
|
65
56
|
},
|
66
57
|
],
|
67
58
|
},
|
68
|
-
}
|
69
|
-
{
|
59
|
+
});
|
60
|
+
(0, eslint_file_1.addOverrideToLintConfig)(tree, options.projectRoot, {
|
70
61
|
files: ['*.html'],
|
71
|
-
extends: ['plugin:@nx/angular-template'],
|
72
|
-
/**
|
73
|
-
* Having an empty rules object present makes it more obvious to the user where they would
|
74
|
-
* extend things from if they needed to
|
75
|
-
*/
|
76
62
|
rules: {},
|
77
|
-
}
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
'
|
85
|
-
|
63
|
+
});
|
64
|
+
if ((0, buildable_project_1.isBuildableLibraryProject)(tree, options.projectName)) {
|
65
|
+
(0, eslint_file_1.addOverrideToLintConfig)(tree, '', {
|
66
|
+
files: ['*.json'],
|
67
|
+
parser: 'jsonc-eslint-parser',
|
68
|
+
rules: {
|
69
|
+
'@nx/dependency-checks': [
|
70
|
+
'error',
|
71
|
+
{
|
72
|
+
// With flat configs, we don't want to include imports in the eslint js/cjs/mjs files to be checked
|
73
|
+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
|
74
|
+
},
|
75
|
+
],
|
76
|
+
},
|
77
|
+
});
|
78
|
+
}
|
79
|
+
}
|
80
|
+
else {
|
81
|
+
(0, eslint_file_1.replaceOverridesInLintConfig)(tree, options.projectRoot, [
|
82
|
+
...(rootProject ? [global_eslint_config_1.typeScriptOverride, global_eslint_config_1.javaScriptOverride] : []),
|
83
|
+
{
|
84
|
+
files: ['*.ts'],
|
85
|
+
...(hasParserOptions
|
86
|
+
? {
|
87
|
+
parserOptions: {
|
88
|
+
project: [`${options.projectRoot}/tsconfig.*?.json`],
|
89
|
+
},
|
90
|
+
}
|
91
|
+
: {}),
|
92
|
+
extends: [
|
93
|
+
'plugin:@nx/angular',
|
94
|
+
'plugin:@angular-eslint/template/process-inline-templates',
|
95
|
+
],
|
96
|
+
rules: {
|
97
|
+
'@angular-eslint/directive-selector': [
|
98
|
+
'error',
|
99
|
+
{
|
100
|
+
type: 'attribute',
|
101
|
+
prefix: (0, string_utils_1.camelize)(options.prefix),
|
102
|
+
style: 'camelCase',
|
103
|
+
},
|
104
|
+
],
|
105
|
+
'@angular-eslint/component-selector': [
|
106
|
+
'error',
|
107
|
+
{
|
108
|
+
type: 'element',
|
109
|
+
prefix: (0, string_utils_1.dasherize)(options.prefix),
|
110
|
+
style: 'kebab-case',
|
111
|
+
},
|
112
|
+
],
|
86
113
|
},
|
87
|
-
|
88
|
-
|
89
|
-
|
114
|
+
},
|
115
|
+
{
|
116
|
+
files: ['*.html'],
|
117
|
+
extends: ['plugin:@nx/angular-template'],
|
118
|
+
/**
|
119
|
+
* Having an empty rules object present makes it more obvious to the user where they would
|
120
|
+
* extend things from if they needed to
|
121
|
+
*/
|
122
|
+
rules: {},
|
123
|
+
},
|
124
|
+
...((0, buildable_project_1.isBuildableLibraryProject)(tree, options.projectName)
|
125
|
+
? [
|
126
|
+
{
|
127
|
+
files: ['*.json'],
|
128
|
+
parser: 'jsonc-eslint-parser',
|
129
|
+
rules: {
|
130
|
+
'@nx/dependency-checks': [
|
131
|
+
'error',
|
132
|
+
{
|
133
|
+
// With flat configs, we don't want to include imports in the eslint js/cjs/mjs files to be checked
|
134
|
+
ignoredFiles: [
|
135
|
+
'{projectRoot}/eslint.config.{js,cjs,mjs}',
|
136
|
+
],
|
137
|
+
},
|
138
|
+
],
|
139
|
+
},
|
140
|
+
},
|
141
|
+
]
|
142
|
+
: []),
|
143
|
+
]);
|
144
|
+
}
|
90
145
|
}
|
91
146
|
if (!options.skipPackageJson) {
|
92
147
|
const installTask = (0, add_angular_eslint_dependencies_1.addAngularEsLintDependencies)(tree, options.projectName);
|
@@ -4,14 +4,19 @@ exports.addAngularEsLintDependencies = addAngularEsLintDependencies;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
5
5
|
const version_utils_1 = require("../../utils/version-utils");
|
6
6
|
const buildable_project_1 = require("./buildable-project");
|
7
|
+
const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
|
7
8
|
function addAngularEsLintDependencies(tree, projectName) {
|
8
9
|
const compatVersions = (0, version_utils_1.versions)(tree);
|
9
10
|
const angularEslintVersionToInstall = compatVersions.angularEslintVersion;
|
10
|
-
const devDependencies =
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
const devDependencies = (0, flat_config_1.useFlatConfig)(tree)
|
12
|
+
? {
|
13
|
+
'angular-eslint': angularEslintVersionToInstall,
|
14
|
+
}
|
15
|
+
: {
|
16
|
+
'@angular-eslint/eslint-plugin': angularEslintVersionToInstall,
|
17
|
+
'@angular-eslint/eslint-plugin-template': angularEslintVersionToInstall,
|
18
|
+
'@angular-eslint/template-parser': angularEslintVersionToInstall,
|
19
|
+
};
|
15
20
|
if ('typescriptEslintVersion' in compatVersions) {
|
16
21
|
devDependencies['@typescript-eslint/utils'] =
|
17
22
|
compatVersions.typescriptEslintVersion;
|
@@ -2,10 +2,22 @@
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
3
|
exports.angularInitGenerator = angularInitGenerator;
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
5
|
+
const add_plugin_1 = require("@nx/devkit/src/utils/add-plugin");
|
5
6
|
const version_utils_1 = require("../utils/version-utils");
|
7
|
+
const plugin_1 = require("../../plugins/plugin");
|
6
8
|
async function angularInitGenerator(tree, options) {
|
7
9
|
ignoreAngularCacheDirectory(tree);
|
8
10
|
const installTask = installAngularDevkitCoreIfMissing(tree, options);
|
11
|
+
// For Angular inference plugin, we only want it during import since our
|
12
|
+
// generators do not use `angular.json`, and `nx init` should split
|
13
|
+
// `angular.json` into multiple `project.json` files -- as this is preferred
|
14
|
+
// by most folks we've talked to.
|
15
|
+
options.addPlugin ??= process.env.NX_RUNNING_NX_IMPORT === 'true';
|
16
|
+
if (options.addPlugin) {
|
17
|
+
await (0, add_plugin_1.addPlugin)(tree, await (0, devkit_1.createProjectGraphAsync)(), '@nx/angular/plugin', plugin_1.createNodesV2, {
|
18
|
+
targetNamePrefix: ['', 'angular:', 'angular-'],
|
19
|
+
}, options.updatePackageScripts);
|
20
|
+
}
|
9
21
|
if (!options.skipFormat) {
|
10
22
|
await (0, devkit_1.formatFiles)(tree);
|
11
23
|
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { type CreateNodesV2 } from '@nx/devkit';
|
2
|
+
export interface AngularPluginOptions {
|
3
|
+
targetNamePrefix?: string;
|
4
|
+
}
|
5
|
+
type AngularTargetConfiguration = {
|
6
|
+
builder: string;
|
7
|
+
options?: Record<string, any>;
|
8
|
+
configurations?: Record<string, any>;
|
9
|
+
defaultConfiguration?: string;
|
10
|
+
};
|
11
|
+
export type AngularProjectConfiguration = {
|
12
|
+
projectType: 'application' | 'library';
|
13
|
+
root: string;
|
14
|
+
sourceRoot?: string;
|
15
|
+
architect?: Record<string, AngularTargetConfiguration>;
|
16
|
+
targets?: Record<string, AngularTargetConfiguration>;
|
17
|
+
};
|
18
|
+
export declare const createNodesV2: CreateNodesV2<AngularPluginOptions>;
|
19
|
+
export {};
|
@@ -0,0 +1,418 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.createNodesV2 = void 0;
|
4
|
+
const tslib_1 = require("tslib");
|
5
|
+
const devkit_1 = require("@nx/devkit");
|
6
|
+
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
7
|
+
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
8
|
+
const js_1 = require("@nx/js");
|
9
|
+
const node_fs_1 = require("node:fs");
|
10
|
+
const node_path_1 = require("node:path");
|
11
|
+
const posix = tslib_1.__importStar(require("node:path/posix"));
|
12
|
+
const devkit_internals_1 = require("nx/src/devkit-internals");
|
13
|
+
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
14
|
+
const knownExecutors = {
|
15
|
+
appShell: new Set(['@angular-devkit/build-angular:app-shell']),
|
16
|
+
build: new Set([
|
17
|
+
'@angular-devkit/build-angular:application',
|
18
|
+
'@angular/build:application',
|
19
|
+
'@angular-devkit/build-angular:browser-esbuild',
|
20
|
+
'@angular-devkit/build-angular:browser',
|
21
|
+
'@angular-devkit/build-angular:ng-packagr',
|
22
|
+
]),
|
23
|
+
devServer: new Set(['@angular-devkit/build-angular:dev-server']),
|
24
|
+
extractI18n: new Set(['@angular-devkit/build-angular:extract-i18n']),
|
25
|
+
prerender: new Set([
|
26
|
+
'@angular-devkit/build-angular:prerender',
|
27
|
+
'@nguniversal/builders:prerender',
|
28
|
+
]),
|
29
|
+
server: new Set(['@angular-devkit/build-angular:server']),
|
30
|
+
serveSsr: new Set([
|
31
|
+
'@angular-devkit/build-angular:ssr-dev-server',
|
32
|
+
'@nguniversal/builders:ssr-dev-server',
|
33
|
+
]),
|
34
|
+
test: new Set(['@angular-devkit/build-angular:karma']),
|
35
|
+
};
|
36
|
+
const pmc = (0, devkit_1.getPackageManagerCommand)();
|
37
|
+
function readProjectsCache(cachePath) {
|
38
|
+
return (0, node_fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : {};
|
39
|
+
}
|
40
|
+
function writeProjectsToCache(cachePath, results) {
|
41
|
+
(0, devkit_1.writeJsonFile)(cachePath, results);
|
42
|
+
}
|
43
|
+
exports.createNodesV2 = [
|
44
|
+
'**/angular.json',
|
45
|
+
async (configFiles, options, context) => {
|
46
|
+
const optionsHash = (0, devkit_internals_1.hashObject)(options);
|
47
|
+
const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `angular-${optionsHash}.hash`);
|
48
|
+
const projectsCache = readProjectsCache(cachePath);
|
49
|
+
try {
|
50
|
+
return await (0, devkit_1.createNodesFromFiles)((configFile, options, context) => createNodesInternal(configFile, options, context, projectsCache), configFiles, options, context);
|
51
|
+
}
|
52
|
+
finally {
|
53
|
+
writeProjectsToCache(cachePath, projectsCache);
|
54
|
+
}
|
55
|
+
},
|
56
|
+
];
|
57
|
+
async function createNodesInternal(configFilePath, options, context, projectsCache) {
|
58
|
+
const angularWorkspaceRoot = (0, node_path_1.dirname)(configFilePath);
|
59
|
+
// Do not create a project if package.json isn't there
|
60
|
+
const siblingFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot));
|
61
|
+
if (!siblingFiles.includes('package.json')) {
|
62
|
+
return {};
|
63
|
+
}
|
64
|
+
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(angularWorkspaceRoot, options, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
|
65
|
+
projectsCache[hash] ??= await buildAngularProjects(configFilePath, options, angularWorkspaceRoot, context);
|
66
|
+
return { projects: projectsCache[hash] };
|
67
|
+
}
|
68
|
+
async function buildAngularProjects(configFilePath, options, angularWorkspaceRoot, context) {
|
69
|
+
const projects = {};
|
70
|
+
const absoluteConfigFilePath = (0, node_path_1.join)(context.workspaceRoot, configFilePath);
|
71
|
+
const angularJson = (0, devkit_1.readJsonFile)(absoluteConfigFilePath);
|
72
|
+
const appShellTargets = [];
|
73
|
+
const prerenderTargets = [];
|
74
|
+
for (const [projectName, project] of Object.entries(angularJson.projects ?? {})) {
|
75
|
+
const targets = {};
|
76
|
+
const projectTargets = getAngularJsonProjectTargets(project);
|
77
|
+
if (!projectTargets) {
|
78
|
+
continue;
|
79
|
+
}
|
80
|
+
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(project.root, context);
|
81
|
+
for (const [angularTargetName, angularTarget] of Object.entries(projectTargets)) {
|
82
|
+
const nxTargetName = options?.targetNamePrefix
|
83
|
+
? `${options.targetNamePrefix}${angularTargetName}`
|
84
|
+
: angularTargetName;
|
85
|
+
const externalDependencies = ['@angular/cli'];
|
86
|
+
targets[nxTargetName] = {
|
87
|
+
command:
|
88
|
+
// For targets that are also Angular CLI commands, infer the simplified form.
|
89
|
+
// Otherwise, use `ng run` to support non-command targets so that they will run.
|
90
|
+
angularTargetName === 'build' ||
|
91
|
+
angularTargetName === 'deploy' ||
|
92
|
+
angularTargetName === 'extract-i18n' ||
|
93
|
+
angularTargetName === 'e2e' ||
|
94
|
+
angularTargetName === 'lint' ||
|
95
|
+
angularTargetName === 'serve' ||
|
96
|
+
angularTargetName === 'test'
|
97
|
+
? `ng ${angularTargetName}`
|
98
|
+
: `ng run ${projectName}:${angularTargetName}`,
|
99
|
+
options: { cwd: angularWorkspaceRoot },
|
100
|
+
metadata: {
|
101
|
+
technologies: ['angular'],
|
102
|
+
description: `Run the "${angularTargetName}" target for "${projectName}".`,
|
103
|
+
help: {
|
104
|
+
command: `${pmc.exec} ng run ${projectName}:${angularTargetName} --help`,
|
105
|
+
example: {},
|
106
|
+
},
|
107
|
+
},
|
108
|
+
};
|
109
|
+
if (knownExecutors.appShell.has(angularTarget.builder)) {
|
110
|
+
appShellTargets.push({ target: nxTargetName, project: projectName });
|
111
|
+
}
|
112
|
+
else if (knownExecutors.build.has(angularTarget.builder)) {
|
113
|
+
await updateBuildTarget(nxTargetName, targets[nxTargetName], angularTarget, context, angularWorkspaceRoot, project.root, namedInputs);
|
114
|
+
}
|
115
|
+
else if (knownExecutors.devServer.has(angularTarget.builder)) {
|
116
|
+
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
117
|
+
}
|
118
|
+
else if (knownExecutors.extractI18n.has(angularTarget.builder)) {
|
119
|
+
targets[nxTargetName].metadata.help.example.options = {
|
120
|
+
format: 'json',
|
121
|
+
};
|
122
|
+
}
|
123
|
+
else if (knownExecutors.test.has(angularTarget.builder)) {
|
124
|
+
updateTestTarget(targets[nxTargetName], angularTarget, context, angularWorkspaceRoot, project.root, namedInputs, externalDependencies);
|
125
|
+
}
|
126
|
+
else if (knownExecutors.server.has(angularTarget.builder)) {
|
127
|
+
updateServerTarget(targets[nxTargetName], angularTarget, context, angularWorkspaceRoot, project.root, namedInputs);
|
128
|
+
}
|
129
|
+
else if (knownExecutors.serveSsr.has(angularTarget.builder)) {
|
130
|
+
targets[nxTargetName].metadata.help.example.options = { port: 4201 };
|
131
|
+
}
|
132
|
+
else if (knownExecutors.prerender.has(angularTarget.builder)) {
|
133
|
+
prerenderTargets.push({ target: nxTargetName, project: projectName });
|
134
|
+
}
|
135
|
+
if (targets[nxTargetName].inputs?.length) {
|
136
|
+
targets[nxTargetName].inputs.push({ externalDependencies });
|
137
|
+
}
|
138
|
+
if (angularTarget.configurations) {
|
139
|
+
for (const configurationName of Object.keys(angularTarget.configurations)) {
|
140
|
+
targets[nxTargetName].configurations = {
|
141
|
+
...targets[nxTargetName].configurations,
|
142
|
+
[configurationName]: {
|
143
|
+
command: `ng run ${projectName}:${angularTargetName}:${configurationName}`,
|
144
|
+
},
|
145
|
+
};
|
146
|
+
}
|
147
|
+
}
|
148
|
+
if (angularTarget.defaultConfiguration) {
|
149
|
+
targets[nxTargetName].defaultConfiguration =
|
150
|
+
angularTarget.defaultConfiguration;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
projects[projectName] = {
|
154
|
+
projectType: project.projectType,
|
155
|
+
root: posix.join(angularWorkspaceRoot, project.root),
|
156
|
+
sourceRoot: project.sourceRoot
|
157
|
+
? posix.join(angularWorkspaceRoot, project.sourceRoot)
|
158
|
+
: undefined,
|
159
|
+
targets,
|
160
|
+
};
|
161
|
+
}
|
162
|
+
for (const { project, target } of appShellTargets) {
|
163
|
+
updateAppShellTarget(project, target, projects, angularJson, angularWorkspaceRoot, context);
|
164
|
+
}
|
165
|
+
for (const { project, target } of prerenderTargets) {
|
166
|
+
updatePrerenderTarget(project, target, projects, angularJson);
|
167
|
+
}
|
168
|
+
return Object.entries(projects).reduce((acc, [projectName, project]) => {
|
169
|
+
acc[project.root] = {
|
170
|
+
projectType: project.projectType,
|
171
|
+
sourceRoot: project.sourceRoot,
|
172
|
+
targets: project.targets,
|
173
|
+
};
|
174
|
+
return acc;
|
175
|
+
}, {});
|
176
|
+
}
|
177
|
+
function updateAppShellTarget(projectName, targetName, projects, angularJson, angularWorkspaceRoot, context) {
|
178
|
+
// it must exist since we collected it when processing it
|
179
|
+
const target = projects[projectName].targets[targetName];
|
180
|
+
target.metadata.help.example.options = { route: '/some/route' };
|
181
|
+
const { inputs, outputs } = getBrowserAndServerTargetInputsAndOutputs(projectName, targetName, projects, angularJson);
|
182
|
+
const outputIndexPath = getAngularJsonProjectTargets(angularJson.projects[projectName])[targetName].options?.outputIndexPath;
|
183
|
+
if (outputIndexPath) {
|
184
|
+
const fullOutputIndexPath = (0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot, outputIndexPath);
|
185
|
+
outputs.push(getOutput(fullOutputIndexPath, context.workspaceRoot, angularWorkspaceRoot, angularJson.projects[projectName].root));
|
186
|
+
}
|
187
|
+
if (!outputs.length) {
|
188
|
+
// no outputs were identified for the build or server target, so we don't
|
189
|
+
// set any Nx cache options
|
190
|
+
return;
|
191
|
+
}
|
192
|
+
target.cache = true;
|
193
|
+
target.inputs = inputs;
|
194
|
+
target.outputs = outputs;
|
195
|
+
}
|
196
|
+
async function updateBuildTarget(targetName, target, angularTarget, context, angularWorkspaceRoot, projectRoot, namedInputs) {
|
197
|
+
target.dependsOn = [`^${targetName}`];
|
198
|
+
if (angularTarget.options?.outputPath) {
|
199
|
+
const fullOutputPath = (0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot, angularTarget.options.outputPath);
|
200
|
+
target.outputs = [
|
201
|
+
getOutput(fullOutputPath, context.workspaceRoot, angularWorkspaceRoot, projectRoot),
|
202
|
+
];
|
203
|
+
}
|
204
|
+
else if (angularTarget.builder === '@angular-devkit/build-angular:ng-packagr') {
|
205
|
+
const outputs = await getNgPackagrOutputs(angularTarget, angularWorkspaceRoot, projectRoot, context);
|
206
|
+
if (outputs.length) {
|
207
|
+
target.outputs = outputs;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
if (target.outputs?.length) {
|
211
|
+
// make it cacheable if we were able to identify outputs
|
212
|
+
target.cache = true;
|
213
|
+
target.inputs =
|
214
|
+
'production' in namedInputs
|
215
|
+
? ['production', '^production']
|
216
|
+
: ['default', '^default'];
|
217
|
+
}
|
218
|
+
if (angularTarget.builder === '@angular-devkit/build-angular:ng-packagr') {
|
219
|
+
target.metadata.help.example.options = { watch: true };
|
220
|
+
}
|
221
|
+
else {
|
222
|
+
target.metadata.help.example.options = { localize: true };
|
223
|
+
}
|
224
|
+
}
|
225
|
+
function updateTestTarget(target, angularTarget, context, angularWorkspaceRoot, projectRoot, namedInputs, externalDependencies) {
|
226
|
+
target.cache = true;
|
227
|
+
target.inputs =
|
228
|
+
'production' in namedInputs
|
229
|
+
? ['default', '^production']
|
230
|
+
: ['default', '^default'];
|
231
|
+
target.outputs = getKarmaTargetOutputs(angularTarget, angularWorkspaceRoot, projectRoot, context);
|
232
|
+
externalDependencies.push('karma');
|
233
|
+
target.metadata.help.example.options = { codeCoverage: true };
|
234
|
+
}
|
235
|
+
function updateServerTarget(target, angularTarget, context, angularWorkspaceRoot, projectRoot, namedInputs) {
|
236
|
+
target.metadata.help.example.options = { localize: true };
|
237
|
+
if (!angularTarget.options?.outputPath) {
|
238
|
+
// only make it cacheable if we were able to identify outputs
|
239
|
+
return;
|
240
|
+
}
|
241
|
+
target.cache = true;
|
242
|
+
target.inputs =
|
243
|
+
'production' in namedInputs
|
244
|
+
? ['production', '^production']
|
245
|
+
: ['default', '^default'];
|
246
|
+
const fullOutputPath = (0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot, angularTarget.options.outputPath);
|
247
|
+
target.outputs = [
|
248
|
+
getOutput(fullOutputPath, context.workspaceRoot, angularWorkspaceRoot, projectRoot),
|
249
|
+
];
|
250
|
+
}
|
251
|
+
function updatePrerenderTarget(projectName, targetName, projects, angularJson) {
|
252
|
+
// it must exist since we collected it when processing it
|
253
|
+
const target = projects[projectName].targets[targetName];
|
254
|
+
target.metadata.help.example.options =
|
255
|
+
getAngularJsonProjectTargets(angularJson.projects[projectName])[targetName]
|
256
|
+
.builder === '@angular-devkit/build-angular:prerender'
|
257
|
+
? { discoverRoutes: false }
|
258
|
+
: { guessRoutes: false };
|
259
|
+
const { inputs, outputs } = getBrowserAndServerTargetInputsAndOutputs(projectName, targetName, projects, angularJson);
|
260
|
+
if (!outputs.length) {
|
261
|
+
// no outputs were identified for the build or server target, so we don't
|
262
|
+
// set any Nx cache options
|
263
|
+
return;
|
264
|
+
}
|
265
|
+
target.cache = true;
|
266
|
+
target.inputs = inputs;
|
267
|
+
target.outputs = outputs;
|
268
|
+
}
|
269
|
+
async function getNgPackagrOutputs(target, angularWorkspaceRoot, projectRoot, context) {
|
270
|
+
let ngPackageJsonPath = (0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot, target.options.project);
|
271
|
+
const readConfig = async (configPath) => {
|
272
|
+
if (!(0, node_fs_1.existsSync)(configPath)) {
|
273
|
+
return undefined;
|
274
|
+
}
|
275
|
+
try {
|
276
|
+
if (configPath.endsWith('.js')) {
|
277
|
+
const result = await import(configPath);
|
278
|
+
return result['default'] ?? result;
|
279
|
+
}
|
280
|
+
return (0, devkit_1.readJsonFile)(configPath);
|
281
|
+
}
|
282
|
+
catch { }
|
283
|
+
return undefined;
|
284
|
+
};
|
285
|
+
let ngPackageJson;
|
286
|
+
let basePath;
|
287
|
+
if ((0, node_fs_1.statSync)(ngPackageJsonPath).isDirectory()) {
|
288
|
+
basePath = ngPackageJsonPath;
|
289
|
+
ngPackageJson = await readConfig((0, node_path_1.join)(ngPackageJsonPath, 'ng-package.json'));
|
290
|
+
if (!ngPackageJson) {
|
291
|
+
ngPackageJson = await readConfig((0, node_path_1.join)(ngPackageJsonPath, 'ng-package.js'));
|
292
|
+
}
|
293
|
+
}
|
294
|
+
else {
|
295
|
+
basePath = (0, node_path_1.dirname)(ngPackageJsonPath);
|
296
|
+
ngPackageJson = await readConfig(ngPackageJsonPath);
|
297
|
+
}
|
298
|
+
if (!ngPackageJson) {
|
299
|
+
return [];
|
300
|
+
}
|
301
|
+
const destination = ngPackageJson.dest
|
302
|
+
? (0, node_path_1.join)(basePath, ngPackageJson.dest)
|
303
|
+
: (0, node_path_1.join)(basePath, 'dist');
|
304
|
+
return [
|
305
|
+
getOutput(destination, context.workspaceRoot, angularWorkspaceRoot, projectRoot),
|
306
|
+
];
|
307
|
+
}
|
308
|
+
function getKarmaTargetOutputs(target, angularWorkspaceRoot, projectRoot, context) {
|
309
|
+
const defaultOutput = posix.join('{workspaceRoot}', angularWorkspaceRoot, 'coverage/{projectName}');
|
310
|
+
if (!target.options?.karmaConfig) {
|
311
|
+
return [defaultOutput];
|
312
|
+
}
|
313
|
+
try {
|
314
|
+
const { parseConfig } = require('karma/lib/config');
|
315
|
+
const karmaConfigPath = (0, node_path_1.join)(context.workspaceRoot, angularWorkspaceRoot, projectRoot, target.options.karmaConfig);
|
316
|
+
const config = parseConfig(karmaConfigPath);
|
317
|
+
if (config.coverageReporter.dir) {
|
318
|
+
return [
|
319
|
+
getOutput(config.coverageReporter.dir, context.workspaceRoot, angularWorkspaceRoot, projectRoot),
|
320
|
+
];
|
321
|
+
}
|
322
|
+
}
|
323
|
+
catch {
|
324
|
+
// we silently ignore any error here and fall back to the default output
|
325
|
+
}
|
326
|
+
return [defaultOutput];
|
327
|
+
}
|
328
|
+
function getBrowserAndServerTargetInputsAndOutputs(projectName, targetName, projects, angularJson) {
|
329
|
+
const { browserTarget, serverTarget } = extractBrowserAndServerTargets(angularJson, projectName, targetName);
|
330
|
+
if (!browserTarget || !serverTarget) {
|
331
|
+
// if any of these are missing, the target is invalid so we return empty values
|
332
|
+
return { inputs: [], outputs: [] };
|
333
|
+
}
|
334
|
+
const browserTargetInputs = projects[browserTarget.project]?.targets?.[browserTarget.target]?.inputs ??
|
335
|
+
[];
|
336
|
+
const serverTargetInputs = projects[serverTarget.project]?.targets?.[serverTarget.target]?.inputs ??
|
337
|
+
[];
|
338
|
+
const browserTargetOutputs = projects[browserTarget.project]?.targets?.[browserTarget.target]?.outputs ??
|
339
|
+
[];
|
340
|
+
const serverTargetOutputs = projects[serverTarget.project]?.targets?.[serverTarget.target]?.outputs ??
|
341
|
+
[];
|
342
|
+
return {
|
343
|
+
inputs: mergeInputs(...browserTargetInputs, ...serverTargetInputs),
|
344
|
+
outputs: Array.from(new Set([...browserTargetOutputs, ...serverTargetOutputs])),
|
345
|
+
};
|
346
|
+
}
|
347
|
+
function extractBrowserAndServerTargets(angularJson, projectName, targetName) {
|
348
|
+
let browserTarget;
|
349
|
+
let serverTarget;
|
350
|
+
try {
|
351
|
+
const targets = getAngularJsonProjectTargets(angularJson.projects[projectName]);
|
352
|
+
const target = targets[targetName];
|
353
|
+
let browserTargetSpecifier = target.options?.browserTarget;
|
354
|
+
if (!browserTargetSpecifier) {
|
355
|
+
const configuration = Object.values(target.configurations ?? {}).find((config) => !!config.browserTarget);
|
356
|
+
browserTargetSpecifier = configuration?.browserTarget;
|
357
|
+
}
|
358
|
+
if (browserTargetSpecifier) {
|
359
|
+
browserTarget = targetFromTargetString(browserTargetSpecifier, projectName, targetName);
|
360
|
+
}
|
361
|
+
let serverTargetSpecifier = target.options?.serverTarget;
|
362
|
+
if (!serverTargetSpecifier) {
|
363
|
+
serverTargetSpecifier = Object.values(target.configurations ?? {}).find((config) => !!config.serverTarget)?.serverTarget;
|
364
|
+
}
|
365
|
+
if (serverTargetSpecifier) {
|
366
|
+
serverTarget = targetFromTargetString(serverTargetSpecifier, projectName, targetName);
|
367
|
+
}
|
368
|
+
}
|
369
|
+
catch { }
|
370
|
+
return { browserTarget: browserTarget, serverTarget };
|
371
|
+
}
|
372
|
+
function mergeInputs(...inputs) {
|
373
|
+
const stringInputs = new Set();
|
374
|
+
const externalDependencies = new Set();
|
375
|
+
for (const input of inputs) {
|
376
|
+
if (typeof input === 'string') {
|
377
|
+
stringInputs.add(input);
|
378
|
+
}
|
379
|
+
else if ('externalDependencies' in input) {
|
380
|
+
// we only infer external dependencies, so we don't need to handle the other input definitions
|
381
|
+
for (const externalDependency of input.externalDependencies) {
|
382
|
+
externalDependencies.add(externalDependency);
|
383
|
+
}
|
384
|
+
}
|
385
|
+
}
|
386
|
+
return [
|
387
|
+
...stringInputs,
|
388
|
+
...(externalDependencies.size
|
389
|
+
? [{ externalDependencies: Array.from(externalDependencies) }]
|
390
|
+
: []),
|
391
|
+
];
|
392
|
+
}
|
393
|
+
// angular support abbreviated target specifiers, this is adapter from:
|
394
|
+
// https://github.com/angular/angular-cli/blob/7d9ce246a33c60ec96eb4bf99520f5475716a910/packages/angular_devkit/architect/src/api.ts#L336
|
395
|
+
function targetFromTargetString(specifier, abbreviatedProjectName, abbreviatedTargetName) {
|
396
|
+
const tuple = specifier.split(':', 3);
|
397
|
+
if (tuple.length < 2) {
|
398
|
+
// invalid target, ignore
|
399
|
+
return undefined;
|
400
|
+
}
|
401
|
+
// we only care about project and target
|
402
|
+
return {
|
403
|
+
project: tuple[0] || abbreviatedProjectName || '',
|
404
|
+
target: tuple[1] || abbreviatedTargetName || '',
|
405
|
+
};
|
406
|
+
}
|
407
|
+
function getOutput(path, workspaceRoot, angularWorkspaceRoot, projectRoot) {
|
408
|
+
const relativePath = (0, node_path_1.relative)((0, node_path_1.join)(workspaceRoot, angularWorkspaceRoot, projectRoot), path);
|
409
|
+
if (relativePath.startsWith('..')) {
|
410
|
+
return posix.join('{workspaceRoot}', (0, node_path_1.join)(angularWorkspaceRoot, projectRoot, relativePath));
|
411
|
+
}
|
412
|
+
else {
|
413
|
+
return posix.join('{projectRoot}', relativePath);
|
414
|
+
}
|
415
|
+
}
|
416
|
+
function getAngularJsonProjectTargets(project) {
|
417
|
+
return project.architect ?? project.targets;
|
418
|
+
}
|
package/src/utils/versions.d.ts
CHANGED
@@ -14,7 +14,7 @@ export declare const typesExpressVersion = "4.17.14";
|
|
14
14
|
export declare const browserSyncVersion = "^3.0.0";
|
15
15
|
export declare const moduleFederationNodeVersion = "~2.5.0";
|
16
16
|
export declare const moduleFederationEnhancedVersion = "~0.6.0";
|
17
|
-
export declare const angularEslintVersion = "^18.0
|
17
|
+
export declare const angularEslintVersion = "^18.3.0";
|
18
18
|
export declare const typescriptEslintVersion = "^7.16.0";
|
19
19
|
export declare const tailwindVersion = "^3.0.2";
|
20
20
|
export declare const postcssVersion = "^8.4.5";
|
package/src/utils/versions.js
CHANGED
@@ -17,7 +17,7 @@ exports.typesExpressVersion = '4.17.14';
|
|
17
17
|
exports.browserSyncVersion = '^3.0.0';
|
18
18
|
exports.moduleFederationNodeVersion = '~2.5.0';
|
19
19
|
exports.moduleFederationEnhancedVersion = '~0.6.0';
|
20
|
-
exports.angularEslintVersion = '^18.0
|
20
|
+
exports.angularEslintVersion = '^18.3.0';
|
21
21
|
exports.typescriptEslintVersion = '^7.16.0';
|
22
22
|
exports.tailwindVersion = '^3.0.2';
|
23
23
|
exports.postcssVersion = '^8.4.5';
|