@nx/angular 19.7.4 → 19.8.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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';
|