Skip to content

Commit dc2e12c

Browse files
committed
Move patch to new eslint package folder
1 parent ebee584 commit dc2e12c

File tree

3 files changed

+244
-189
lines changed

3 files changed

+244
-189
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
const path = require('path');
5+
const fs = require('fs');
6+
7+
const isModuleResolutionError: (ex: unknown) => boolean = (ex) =>
8+
typeof ex === 'object' && !!ex && 'code' in ex && (ex as { code: unknown }).code === 'MODULE_NOT_FOUND';
9+
10+
// Module path for eslintrc.cjs
11+
// Example: ".../@eslint/eslintrc/dist/eslintrc.cjs"
12+
let eslintrcBundlePath: string | undefined = undefined;
13+
14+
// Module path for config-array-factory.js
15+
// Example: ".../@eslint/eslintrc/lib/config-array-factory"
16+
let configArrayFactoryPath: string | undefined = undefined;
17+
18+
// Module path for naming.js
19+
// Example: ".../@eslint/eslintrc/lib/shared/naming"
20+
let namingPath: string | undefined = undefined;
21+
22+
// Module path for relative-module-resolver.js
23+
// Example: ".../@eslint/eslintrc/lib/shared/relative-module-resolver"
24+
let moduleResolverPath: string | undefined = undefined;
25+
26+
// Folder path where ESLint's package.json can be found
27+
// Example: ".../node_modules/eslint"
28+
let eslintFolder: string | undefined = undefined;
29+
30+
// Probe for the ESLint >=8.0.0 layout:
31+
for (let currentModule = module; ; ) {
32+
if (!eslintrcBundlePath) {
33+
// For ESLint >=8.0.0, all @eslint/eslintrc code is bundled at this path:
34+
// .../@eslint/eslintrc/dist/eslintrc.cjs
35+
try {
36+
const eslintrcFolder = path.dirname(
37+
require.resolve('@eslint/eslintrc/package.json', { paths: [currentModule.path] })
38+
);
39+
40+
// Make sure we actually resolved the module in our call path
41+
// and not some other spurious dependency.
42+
if (path.join(eslintrcFolder, 'dist/eslintrc.cjs') === currentModule.filename) {
43+
eslintrcBundlePath = path.join(eslintrcFolder, 'dist/eslintrc.cjs');
44+
}
45+
} catch (ex: unknown) {
46+
// Module resolution failures are expected, as we're walking
47+
// up our require stack to look for eslint. All other errors
48+
// are rethrown.
49+
if (!isModuleResolutionError(ex)) {
50+
throw ex;
51+
}
52+
}
53+
} else {
54+
// Next look for a file in ESLint's folder
55+
// .../eslint/lib/cli-engine/cli-engine.js
56+
try {
57+
const eslintCandidateFolder = path.dirname(
58+
require.resolve('eslint/package.json', {
59+
paths: [currentModule.path]
60+
})
61+
);
62+
63+
// Make sure we actually resolved the module in our call path
64+
// and not some other spurious dependency.
65+
if (path.join(eslintCandidateFolder, 'lib/cli-engine/cli-engine.js') === currentModule.filename) {
66+
eslintFolder = eslintCandidateFolder;
67+
break;
68+
}
69+
} catch (ex: unknown) {
70+
// Module resolution failures are expected, as we're walking
71+
// up our require stack to look for eslint. All other errors
72+
// are rethrown.
73+
if (!isModuleResolutionError(ex)) {
74+
throw ex;
75+
}
76+
}
77+
}
78+
79+
if (!currentModule.parent) {
80+
break;
81+
}
82+
currentModule = currentModule.parent;
83+
}
84+
85+
if (!eslintFolder) {
86+
// Probe for the ESLint >=7.8.0 layout:
87+
for (let currentModule = module; ; ) {
88+
if (!configArrayFactoryPath) {
89+
// For ESLint >=7.8.0, config-array-factory.js is at this path:
90+
// .../@eslint/eslintrc/lib/config-array-factory.js
91+
try {
92+
const eslintrcFolder = path.dirname(
93+
require.resolve('@eslint/eslintrc/package.json', {
94+
paths: [currentModule.path]
95+
})
96+
);
97+
98+
if (path.join(eslintrcFolder, '/lib/config-array-factory.js') == currentModule.filename) {
99+
configArrayFactoryPath = path.join(eslintrcFolder, 'lib/config-array-factory.js');
100+
moduleResolverPath = path.join(eslintrcFolder, 'lib/shared/relative-module-resolver');
101+
namingPath = path.join(eslintrcFolder, 'lib/shared/naming');
102+
}
103+
} catch (ex: unknown) {
104+
// Module resolution failures are expected, as we're walking
105+
// up our require stack to look for eslint. All other errors
106+
// are rethrown.
107+
if (!isModuleResolutionError(ex)) {
108+
throw ex;
109+
}
110+
}
111+
} else {
112+
// Next look for a file in ESLint's folder
113+
// .../eslint/lib/cli-engine/cli-engine.js
114+
try {
115+
const eslintCandidateFolder = path.dirname(
116+
require.resolve('eslint/package.json', {
117+
paths: [currentModule.path]
118+
})
119+
);
120+
121+
if (path.join(eslintCandidateFolder, 'lib/cli-engine/cli-engine.js') == currentModule.filename) {
122+
eslintFolder = eslintCandidateFolder;
123+
break;
124+
}
125+
} catch (ex: unknown) {
126+
// Module resolution failures are expected, as we're walking
127+
// up our require stack to look for eslint. All other errors
128+
// are rethrown.
129+
if (!isModuleResolutionError(ex)) {
130+
throw ex;
131+
}
132+
}
133+
}
134+
135+
if (!currentModule.parent) {
136+
break;
137+
}
138+
currentModule = currentModule.parent;
139+
}
140+
}
141+
142+
if (!eslintFolder) {
143+
// Probe for the <7.8.0 layout:
144+
for (let currentModule = module; ; ) {
145+
// For ESLint <7.8.0, config-array-factory.js was at this path:
146+
// .../eslint/lib/cli-engine/config-array-factory.js
147+
if (/[\\/]eslint[\\/]lib[\\/]cli-engine[\\/]config-array-factory\.js$/i.test(currentModule.filename)) {
148+
eslintFolder = path.join(path.dirname(currentModule.filename), '../..');
149+
configArrayFactoryPath = path.join(eslintFolder, 'lib/cli-engine/config-array-factory');
150+
moduleResolverPath = path.join(eslintFolder, 'lib/shared/relative-module-resolver');
151+
namingPath = path.join(eslintFolder, 'lib/shared/naming');
152+
break;
153+
}
154+
155+
if (!currentModule.parent) {
156+
// This was tested with ESLint 6.1.0 .. 7.12.1.
157+
throw new Error(
158+
'Failed to patch ESLint because the calling module was not recognized.\n' +
159+
'If you are using a newer ESLint version that may be unsupported, please create a GitHub issue:\n' +
160+
'https://github.com/microsoft/rushstack/issues'
161+
);
162+
}
163+
currentModule = currentModule.parent;
164+
}
165+
}
166+
167+
// Detect the ESLint package version
168+
const eslintPackageJson = fs.readFileSync(path.join(eslintFolder, 'package.json')).toString();
169+
const eslintPackageObject = JSON.parse(eslintPackageJson);
170+
const eslintPackageVersion = eslintPackageObject.version;
171+
const versionMatch = /^([0-9]+)\./.exec(eslintPackageVersion); // parse the SemVer MAJOR part
172+
if (!versionMatch) {
173+
throw new Error('Unable to parse ESLint version: ' + eslintPackageVersion);
174+
}
175+
const eslintMajorVersion = Number(versionMatch[1]);
176+
if (!(eslintMajorVersion >= 6 && eslintMajorVersion <= 8)) {
177+
throw new Error(
178+
'The patch-eslint.js script has only been tested with ESLint version 6.x, 7.x, and 8.x.' +
179+
` (Your version: ${eslintPackageVersion})\n` +
180+
'Consider reporting a GitHub issue:\n' +
181+
'https://github.com/microsoft/rushstack/issues'
182+
);
183+
}
184+
185+
let ConfigArrayFactory: any;
186+
if (eslintMajorVersion === 8) {
187+
ConfigArrayFactory = require(eslintrcBundlePath!).Legacy.ConfigArrayFactory;
188+
} else {
189+
ConfigArrayFactory = require(configArrayFactoryPath!).ConfigArrayFactory;
190+
}
191+
192+
let ModuleResolver: { resolve: any };
193+
let Naming: { normalizePackageName: any };
194+
if (eslintMajorVersion === 8) {
195+
ModuleResolver = require(eslintrcBundlePath!).Legacy.ModuleResolver;
196+
Naming = require(eslintrcBundlePath!).Legacy.naming;
197+
} else {
198+
ModuleResolver = require(moduleResolverPath!);
199+
Naming = require(namingPath!);
200+
}
201+
202+
export { ConfigArrayFactory, ModuleResolver, Naming, eslintMajorVersion as EslintMajorVersion };
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+
// See LICENSE in the project root for license information.
3+
4+
// This is a workaround for ESLint's requirement to consume shareable configurations from package names prefixed
5+
// with "eslint-config".
6+
//
7+
// To remove this requirement, add this line to the top of your project's .eslintrc.js file:
8+
//
9+
// require("@rushstack/eslint-patch/custom-config-package-names");
10+
//
11+
import { ConfigArrayFactory, Naming } from './_patch-base';
12+
13+
if (!ConfigArrayFactory.__loadExtendedSharableConfigPatched) {
14+
ConfigArrayFactory.__loadExtendedSharableConfigPatched = true;
15+
const originalLoadExtendedSharableConfig = ConfigArrayFactory.prototype._loadExtendedSharableConfig;
16+
17+
ConfigArrayFactory.prototype._loadExtendedSharableConfig = function () {
18+
try {
19+
return originalLoadExtendedSharableConfig.apply(this, arguments);
20+
} catch (e) {
21+
// We only care about the case where extended configs are not found.
22+
const error: Error & { messageTemplate?: string } = e as Error & { messageTemplate?: string };
23+
if (!error.messageTemplate || error.messageTemplate !== 'extend-config-missing') {
24+
throw e;
25+
}
26+
}
27+
28+
// Extend config could not be resolved from the original location. Override the normalization
29+
// logic to use the extended config as it was specified.
30+
const originalNormalize = Naming.normalizePackageName;
31+
try {
32+
Naming.normalizePackageName = (name: string, prefix: string) => name;
33+
return originalLoadExtendedSharableConfig.apply(this, arguments);
34+
} finally {
35+
Naming.normalizePackageName = originalNormalize;
36+
}
37+
};
38+
}

0 commit comments

Comments
 (0)