@@ -22,6 +22,7 @@ import { convertIgnorePatternToMinimatch } from "@eslint/compat";
22
22
/** @typedef {import("eslint").Linter.ConfigOverride } ConfigOverride */
23
23
/** @typedef {import("recast").types.namedTypes.ObjectExpression } ObjectExpression */
24
24
/** @typedef {import("recast").types.namedTypes.ArrayExpression } ArrayExpression */
25
+ /** @typedef {import("recast").types.namedTypes.CallExpression } CallExpression */
25
26
/** @typedef {import("recast").types.namedTypes.Property } Property */
26
27
/** @typedef {import("recast").types.namedTypes.MemberExpression } MemberExpression */
27
28
/** @typedef {import("recast").types.namedTypes.Program } Program */
@@ -66,6 +67,12 @@ class Migration {
66
67
*/
67
68
messages = [ ] ;
68
69
70
+ /**
71
+ * Whether or not the migration needs the `__dirname` variable defined.
72
+ * @type {boolean }
73
+ */
74
+ needsDirname = false ;
75
+
69
76
/**
70
77
* Any initialization needed in the file.
71
78
* @type {Array<Statement> }
@@ -162,43 +169,53 @@ function getPluginVariableName(pluginName) {
162
169
}
163
170
164
171
/**
165
- * Creates an initialization block for the FlatCompat utility.
166
- * @param {"module"|"commonjs" } sourceType The module type to use.
172
+ * Get the initialization code for `__dirname`.
167
173
* @returns {Array<Statement> } The AST for the initialization block.
168
174
*/
169
- function getFlatCompatInit ( sourceType ) {
170
- let init = `
171
- const compat = new FlatCompat({
172
- baseDirectory: __dirname,
173
- recommendedConfig: js.configs.recommended,
174
- allConfig: js.configs.all
175
- });
176
- ` ;
177
-
175
+ function getDirnameInit ( ) {
178
176
/*
179
- * Need to calculate `__dirname` and `__filename` for ESM. Note that Recast
180
- * doesn't support `import.meta.url`, so using an uppercase "I" to allow for
181
- * parsing. We then need to replace it with the lowercase "i".
177
+ * Recast doesn't support `import.meta.url`, so using an uppercase "I" to
178
+ * allow for parsing. We then need to replace it with the lowercase "i".
182
179
*/
183
- if ( sourceType === "module" ) {
184
- init = `
180
+ const init = `\n
185
181
const __filename = fileURLToPath(Import.meta.url);
186
- const __dirname = path.dirname(__filename);
187
-
188
- ${ init } `;
189
- }
182
+ const __dirname = path.dirname(__filename);` ;
190
183
191
184
const result = recast . parse ( init ) . program . body ;
192
185
193
186
// Replace uppercase "I" with lowercase "i" in "Import.meta.url"
194
- if ( sourceType === "module" ) {
195
- result [ 0 ] . declarations [ 0 ] . init . arguments [ 0 ] . object . object . name =
196
- "import" ;
197
- }
187
+ result [ 0 ] . declarations [ 0 ] . init . arguments [ 0 ] . object . object . name = "import" ;
198
188
199
189
return result ;
200
190
}
201
191
192
+ /**
193
+ * Creates an initialization block for the FlatCompat utility.
194
+ * @returns {Array<Statement> } The AST for the initialization block.
195
+ */
196
+ function getFlatCompatInit ( ) {
197
+ const init = `
198
+ const compat = new FlatCompat({
199
+ baseDirectory: __dirname,
200
+ recommendedConfig: js.configs.recommended,
201
+ allConfig: js.configs.all
202
+ });
203
+ ` ;
204
+ return recast . parse ( init ) . program . body ;
205
+ }
206
+
207
+ /**
208
+ * Creates an initialization block for the gitignore file.
209
+ * @returns {Statement } The AST for the initialization block.
210
+ */
211
+ function getGitignoreInit ( ) {
212
+ const init = `
213
+ const gitignorePath = path.resolve(__dirname, ".gitignore");
214
+ ` ;
215
+
216
+ return recast . parse ( init ) . program . body [ 0 ] ;
217
+ }
218
+
202
219
/**
203
220
* Converts a glob pattern to a format that can be used in a flat config.
204
221
* @param {string } pattern The glob pattern to convert.
@@ -216,6 +233,37 @@ function convertGlobPattern(pattern) {
216
233
return `${ isNegated ? "!" : "" } **/${ patternToTest } ` ;
217
234
}
218
235
236
+ /**
237
+ * Creates the entry for the gitignore inclusion.
238
+ * @param {Migration } migration The migration object.
239
+ * @returns {CallExpression } The AST for the gitignore entry.
240
+ */
241
+ function createGitignoreEntry ( migration ) {
242
+ migration . inits . push ( getGitignoreInit ( ) ) ;
243
+
244
+ if ( ! migration . imports . has ( "@eslint/compat" ) ) {
245
+ migration . imports . set ( "@eslint/compat" , {
246
+ bindings : [ "includeIgnoreFile" ] ,
247
+ added : true ,
248
+ } ) ;
249
+ } else {
250
+ migration . imports
251
+ . get ( "@eslint/compat" )
252
+ . bindings . push ( "includeIgnoreFile" ) ;
253
+ }
254
+
255
+ if ( ! migration . imports . has ( "node:path" ) ) {
256
+ migration . imports . set ( "node:path" , {
257
+ name : "path" ,
258
+ added : true ,
259
+ } ) ;
260
+ }
261
+
262
+ const code = `includeIgnoreFile(gitignorePath)` ;
263
+
264
+ return recast . parse ( code ) . program . body [ 0 ] . expression ;
265
+ }
266
+
219
267
/**
220
268
* Creates the globals object from the config.
221
269
* @param {Config } config The config to create globals from.
@@ -786,18 +834,25 @@ function migrateConfigObject(migration, config) {
786
834
* @param {Config } config The eslintrc config to migrate.
787
835
* @param {Object } [options] Options for the migration.
788
836
* @param {"module"|"commonjs" } [options.sourceType] The module type to use.
837
+ * @param {boolean } [options.gitignore] `true` to include contents of a .gitignore file.
789
838
* @returns {{code:string,messages:Array<string>,imports:Map<string,MigrationImport>} } The migrated config and
790
839
* any messages to display to the user.
791
840
*/
792
- export function migrateConfig ( config , { sourceType = "module" } = { } ) {
841
+ export function migrateConfig (
842
+ config ,
843
+ { sourceType = "module" , gitignore = false } = { } ,
844
+ ) {
793
845
const migration = new Migration ( config ) ;
794
846
const body = [ ] ;
847
+
848
+ /** @type {Array<CallExpression|ObjectExpression|SpreadElement> } */
795
849
const configArrayElements = [
796
850
...migrateConfigObject (
797
851
migration ,
798
852
/** @type {ConfigOverride } */ ( config ) ,
799
853
) ,
800
854
] ;
855
+ const isModule = sourceType === "module" ;
801
856
802
857
// if the base config has no properties, then remove the empty object
803
858
if (
@@ -821,7 +876,7 @@ export function migrateConfig(config, { sourceType = "module" } = {}) {
821
876
config . extends ||
822
877
config . overrides ?. some ( override => override . extends )
823
878
) {
824
- if ( sourceType === "module" ) {
879
+ if ( isModule ) {
825
880
migration . imports . set ( "node:path" , {
826
881
name : "path" ,
827
882
added : true ,
@@ -839,15 +894,29 @@ export function migrateConfig(config, { sourceType = "module" } = {}) {
839
894
bindings : [ "FlatCompat" ] ,
840
895
added : true ,
841
896
} ) ;
842
- migration . inits . push ( ...getFlatCompatInit ( sourceType ) ) ;
897
+ migration . needsDirname ||= isModule ;
898
+ migration . inits . push ( ...getFlatCompatInit ( ) ) ;
899
+ }
900
+
901
+ // add .gitignore if necessary
902
+ if ( gitignore ) {
903
+ migration . needsDirname ||= isModule ;
904
+ configArrayElements . unshift ( createGitignoreEntry ( migration ) ) ;
905
+
906
+ if ( migration . needsDirname && ! migration . imports . has ( "node:url" ) ) {
907
+ migration . imports . set ( "node:url" , {
908
+ bindings : [ "fileURLToPath" ] ,
909
+ added : true ,
910
+ } ) ;
911
+ }
843
912
}
844
913
845
914
if ( config . ignorePatterns ) {
846
915
configArrayElements . unshift ( createGlobalIgnores ( config ) ) ;
847
916
}
848
917
849
918
// add imports to the top of the file
850
- if ( sourceType === "commonjs" ) {
919
+ if ( ! isModule ) {
851
920
migration . imports . forEach ( ( { name, bindings } , path ) => {
852
921
const bindingProperties = bindings ?. map ( binding => {
853
922
const bindingProperty = b . property (
@@ -897,11 +966,16 @@ export function migrateConfig(config, { sourceType = "module" } = {}) {
897
966
} ) ;
898
967
}
899
968
969
+ // add calculation of `__dirname` if needed
970
+ if ( migration . needsDirname ) {
971
+ body . push ( ...getDirnameInit ( ) ) ;
972
+ }
973
+
900
974
// output any inits
901
975
body . push ( ...migration . inits ) ;
902
976
903
977
// output the actual config array to the program
904
- if ( sourceType === "commonjs" ) {
978
+ if ( ! isModule ) {
905
979
body . push (
906
980
b . expressionStatement (
907
981
b . assignmentExpression (
0 commit comments