-
-
Notifications
You must be signed in to change notification settings - Fork 243
/
Copy pathindex.ts
228 lines (198 loc) · 7.78 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import type { Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { chain, schematic } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import {
createRootESLintConfig,
createStringifiedRootESLintConfig,
getTargetsConfigFromProject,
readJsonInTree,
shouldUseFlatConfig,
sortObjectByKeys,
updateJsonInTree,
updateSchematicCollections,
} from '../utils';
export const FIXED_ESLINT_V8_VERSION = '8.57.0';
export const FIXED_TYPESCRIPT_ESLINT_V7_VERSION = '7.11.0';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJSON = require('../../package.json');
function addAngularESLintPackages(
json: Record<string, any>,
useFlatConfig: boolean,
) {
return (host: Tree, context: SchematicContext) => {
if (!host.exists('package.json')) {
throw new Error(
'Could not find a `package.json` file at the root of your workspace',
);
}
if (host.exists('tsconfig.base.json')) {
throw new Error(
'\nError: Angular CLI v10.1.0 and later (and no `tsconfig.base.json`) is required in order to run this schematic. Please update your workspace and try again.\n',
);
}
json.scripts = json.scripts || {};
json.scripts['lint'] = json.scripts['lint'] || 'ng lint';
if (useFlatConfig) {
applyDevDependenciesForFlatConfig(json);
// Check if yarn PnP is used https://yarnpkg.com/advanced/pnpapi#processversionspnp and install extra explicit packages to make it happy
if (process.versions.pnp) {
// An explicit reference to the builder is needed for running `ng lint` in PnP
json.devDependencies['@angular-eslint/builder'] = packageJSON.version;
// The linting cannot complete without these explicitly in the root package.json in PnP
json.devDependencies['@eslint/js'] =
`^${packageJSON.devDependencies['eslint']}`;
const typescriptESLintVersion =
packageJSON.devDependencies['@typescript-eslint/utils'];
json.devDependencies['@typescript-eslint/types'] =
typescriptESLintVersion;
json.devDependencies['@typescript-eslint/utils'] =
typescriptESLintVersion;
}
} else {
applyDevDependenciesForESLintRC(json);
}
json.devDependencies = sortObjectByKeys(json.devDependencies);
host.overwrite('package.json', JSON.stringify(json, null, 2));
context.addTask(new NodePackageInstallTask());
context.logger.info(`
All angular-eslint dependencies have been successfully installed 🎉
Please see https://github.com/angular-eslint/angular-eslint for how to add ESLint configuration to your project.
`);
return host;
};
}
function applyDevDependenciesForESLintRC(
json: Record<'devDependencies', Record<string, string>>,
) {
json.devDependencies['eslint'] = FIXED_ESLINT_V8_VERSION;
/**
* @angular-eslint packages
*/
json.devDependencies['@angular-eslint/builder'] = packageJSON.version;
json.devDependencies['@angular-eslint/eslint-plugin'] = packageJSON.version;
json.devDependencies['@angular-eslint/eslint-plugin-template'] =
packageJSON.version;
json.devDependencies['@angular-eslint/schematics'] = packageJSON.version;
json.devDependencies['@angular-eslint/template-parser'] = packageJSON.version;
/**
* @typescript-eslint packages
*/
json.devDependencies['@typescript-eslint/eslint-plugin'] =
FIXED_TYPESCRIPT_ESLINT_V7_VERSION;
json.devDependencies['@typescript-eslint/parser'] =
FIXED_TYPESCRIPT_ESLINT_V7_VERSION;
}
function applyDevDependenciesForFlatConfig(
json: Record<'devDependencies', Record<string, string>>,
) {
json.devDependencies['eslint'] = `^${packageJSON.devDependencies['eslint']}`;
/**
* angular-eslint packages
*/
json.devDependencies['angular-eslint'] = packageJSON.version;
// Clean up individual packages from devDependencies
delete json.devDependencies['@angular-eslint/builder'];
delete json.devDependencies['@angular-eslint/eslint-plugin'];
delete json.devDependencies['@angular-eslint/eslint-plugin-template'];
delete json.devDependencies['@angular-eslint/schematics'];
delete json.devDependencies['@angular-eslint/template-parser'];
/**
* typescript-eslint
*/
const typescriptESLintVersion =
packageJSON.devDependencies['@typescript-eslint/utils'];
json.devDependencies['typescript-eslint'] = typescriptESLintVersion;
// Clean up individual packages from devDependencies
delete json.devDependencies['@typescript-eslint/parser'];
delete json.devDependencies['@typescript-eslint/eslint-plugin'];
delete json.devDependencies['@typescript-eslint/utils'];
}
function applyESLintConfigIfSingleProjectWithNoExistingTSLint(
useFlatConfig: boolean,
) {
return (host: Tree, context: SchematicContext) => {
const angularJson = readJsonInTree(host, 'angular.json');
if (!angularJson || !angularJson.projects) {
return;
}
/**
* If the workspace was created by passing `--create-application=false` to `ng new`
* then there will be an angular.json file with a projects object, but no projects
* within it.
*
* In this case we should still configure the root eslint config and set the
* schematicCollections to use in angular.json.
*/
const projectNames = Object.keys(angularJson.projects);
if (projectNames.length === 0) {
return chain([
useFlatConfig
? (host) => {
// If the root package.json uses type: module, generate ESM content
const packageJson = readJsonInTree(host, 'package.json');
const isESM = packageJson.type === 'module';
host.create(
'eslint.config.js',
createStringifiedRootESLintConfig(null, isESM),
);
return host;
}
: updateJsonInTree('.eslintrc.json', () =>
createRootESLintConfig(null),
),
updateJsonInTree('angular.json', (json) =>
updateSchematicCollections(
json,
useFlatConfig ? 'angular-eslint' : '@angular-eslint/schematics',
),
),
]);
}
/**
* The only other use-case we can reliably support for automatic configuration
* is the default case of having a single project in the workspace, so for anything
* else we bail at this point.
*/
if (projectNames.length !== 1) {
return;
}
const singleProject = angularJson.projects[projectNames[0]];
const targetsConfig = getTargetsConfigFromProject(singleProject);
// Only possible if malformed, safer to finish here
if (!targetsConfig) {
return;
}
// The project already has a lint builder setup, finish here as there is nothing more we can do automatically
if (targetsConfig.lint) {
return;
}
context.logger.info(
`
We detected that you have a single project in your workspace and no existing linter wired up, so we are configuring ESLint for you automatically.
Please see https://github.com/angular-eslint/angular-eslint for more information.
`.trimStart(),
);
return chain([
schematic('add-eslint-to-project', {}),
updateJsonInTree('angular.json', (json) =>
updateSchematicCollections(
json,
useFlatConfig ? 'angular-eslint' : '@angular-eslint/schematics',
),
),
]);
};
}
export default function (): Rule {
return (host: Tree, context: SchematicContext) => {
const workspacePackageJSON = (host.read('package.json') as Buffer).toString(
'utf-8',
);
const json = JSON.parse(workspacePackageJSON);
const useFlatConfig = shouldUseFlatConfig(host, json);
return chain([
addAngularESLintPackages(json, useFlatConfig),
applyESLintConfigIfSingleProjectWithNoExistingTSLint(useFlatConfig),
])(host, context);
};
}