@j-ulrich/release-it-regex-bumper 5.3.1 → 5.5.0

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.
Files changed (3) hide show
  1. package/README.md +35 -8
  2. package/index.js +46 -26
  3. package/package.json +9 -9
package/README.md CHANGED
@@ -73,7 +73,7 @@ example, the pattern `\d+` needs to be written as `"\\d+"` inside JSON.
73
73
 
74
74
  ### `in` ###
75
75
 
76
- **Type:** `string|object`
76
+ **Type:** `string | object`
77
77
  **Default:** `null`
78
78
 
79
79
  The `in` option defines where and how to read the current version. If this option is defined and not
@@ -102,7 +102,7 @@ If this option is `null` or not defined, the [global `encoding`](#encoding) opti
102
102
 
103
103
  ### `in.search` ###
104
104
 
105
- **Type:** `string|object`
105
+ **Type:** `string | object`
106
106
  **Default:** `null`
107
107
 
108
108
  Defines the regular expression to find the version inside the `in.file`.
@@ -147,7 +147,7 @@ If this option is `null` or not defined, the [global `search.flags`](#searchflag
147
147
 
148
148
  ### `in.search.versionCaptureGroup` ###
149
149
 
150
- **Type:** `integer|string`
150
+ **Type:** `integer | string`
151
151
  **Default:** `null`
152
152
 
153
153
  Defines the capture group from the `in.search.pattern` which matches the version. If the
@@ -165,7 +165,7 @@ capturing group with index 1 is used if it exists. Else the whole match is used.
165
165
 
166
166
  ### `out` ###
167
167
 
168
- **Type:** `string|object|array<string|object>`
168
+ **Type:** `string | object | array<string | object>`
169
169
  **Default:** `null`
170
170
 
171
171
  The `out` option defines where and how to write the new version. If defined and not `null`, the
@@ -208,7 +208,7 @@ If both, this option and `out.files` are given, both are processed.
208
208
 
209
209
  ### `out.files` ###
210
210
 
211
- **Type:** `string|array<string>`
211
+ **Type:** `string | array<string>`
212
212
  **Default:** `null` but either this option or `out.file` (or both) must contain a value
213
213
  **Since:** 1.1.0
214
214
 
@@ -241,7 +241,7 @@ If this option is `null` or not defined, the [global `encoding`](#encoding) opti
241
241
 
242
242
  ### `out.search` ###
243
243
 
244
- **Type:** `string|object`
244
+ **Type:** `string | object`
245
245
  **Default:** `null`
246
246
 
247
247
  Defines the regular expression to find the text which is replaced with the new version inside
@@ -357,9 +357,27 @@ The template string also supports a set of placeholders:
357
357
  The placeholders are replaced before the template string is used in the search and replace and thus
358
358
  before the capturing group references are replaced.
359
359
 
360
+ ### `out.strict` ###
361
+
362
+ **Type:** `boolean | "off" | "warn" | "errorIfNoMatch" | "errorIfNoChange"`
363
+ **Default:** `null`
364
+
365
+ Defines the behavior if an output file was not changed by this output configuration or if the search pattern did not match in an output file.
366
+
367
+ If this option is `true` or `"warn", a warning is logged if the search pattern was not found in an output file or if a file was not changed by this output configuration.
368
+
369
+ If this options is `"errorIfNoMatch"`, an `Error` is thrown and the release is aborted if there was no match of the search pattern in an output file. If there was a match but the file did not change, a warning is logged.
370
+
371
+ If this options is `"errorIfNoChange"`, an `Error` is thrown and the release is aborted if either an output file was not changed by this output configuration or there was no match of the search pattern in an output file.
372
+
373
+ If this option is `false` or `"off"`, no warning is logged if the search pattern did not match or the file did not change.
374
+
375
+ If this option is `null` or not defined, the [global `strict`](#strict) is used.
376
+
377
+
360
378
  ### `search` ###
361
379
 
362
- **Type:** `string|object`
380
+ **Type:** `string | object`
363
381
  **Default:** An object with the default values as described below.
364
382
 
365
383
  Defines the default regular expression to be used when no `in.search` or `out.search` is given.
@@ -396,7 +414,7 @@ The default flags for the search pattern which are used when `in.search.flags` o
396
414
 
397
415
  ### `search.versionCaptureGroup` ###
398
416
 
399
- **Type:** `integer|string`
417
+ **Type:** `integer | string`
400
418
  **Default:** `null`
401
419
 
402
420
  Defines the default capture group which is used when `in.search.versionCaptureGroup` is `null` or
@@ -425,6 +443,15 @@ encodings are the ones supported by Node's `fs` module.
425
443
 
426
444
  If this option is not defined or set to `null`, the default value is used.
427
445
 
446
+ ### `strict` ###
447
+
448
+ **Type:** `boolean | "off" | "warn" | "errorIfNoMatch" | "errorIfNoChange"`
449
+ **Default:** `true`
450
+
451
+ The default strict mode used when `out.strict` is `null` or not defined.
452
+
453
+ If this option is not defined or set to `null`, the default value is used.
454
+
428
455
  ## Tips ##
429
456
 
430
457
  ### Disable Output via Command-Line Parameter ###
package/index.js CHANGED
@@ -8,8 +8,7 @@ import _ from 'lodash';
8
8
  import XRegExp from 'xregexp';
9
9
  import semver from 'semver';
10
10
  import { Plugin } from 'release-it';
11
- import dateFormat from 'date-fns/format/index.js';
12
- import dateFormatIso from 'date-fns/formatISO/index.js';
11
+ import { format as dateFormat, formatISO as dateFormatISO } from 'date-fns';
13
12
 
14
13
 
15
14
  const readFile = promisify( fsReadFile );
@@ -24,11 +23,18 @@ const placeholderRegex = XRegExp( '\\{\\{(?<placeholder>(?:[a-z][a-z0-9_]*|\\{))
24
23
  const prereleasePrefix = '-';
25
24
  const buildPrefix = '+';
26
25
 
26
+ const StrictMode = {
27
+ Off: 'off',
28
+ Warn: 'warn',
29
+ ErrorIfNoMatch: 'errorIfNoMatch',
30
+ ErrorIfNoChange: 'errorIfNoChange',
31
+ };
32
+
27
33
  const defaultEncoding = 'utf-8';
28
34
  const defaultSearchRegex = XRegExp( '{{semver}}' );
29
35
  const defaultVersionCaptureGroup = null;
30
36
  const defaultReplace = '{{version}}';
31
-
37
+ const defaultStrictMode = true;
32
38
 
33
39
 
34
40
  export default class RegExBumper extends Plugin {
@@ -75,6 +81,7 @@ export default class RegExBumper extends Plugin {
75
81
  search: globalSearchOptions,
76
82
  replace: globalReplace,
77
83
  encoding: globalEncoding,
84
+ strict: globalStrict,
78
85
  } = this.options;
79
86
  const { isDryRun } = this.config;
80
87
  if ( _.isNil( outOptions ) ) {
@@ -93,7 +100,7 @@ export default class RegExBumper extends Plugin {
93
100
 
94
101
  /* eslint-disable no-await-in-loop */
95
102
  for ( const outOption of expandedOutOptions ) {
96
- const { files, encoding, searchRegex, flags: searchFlags, replace } = outOption;
103
+ const { files, encoding, searchRegex, flags: searchFlags, replace, strict } = outOption;
97
104
 
98
105
  const effectiveEncoding = firstNotNil( encoding, globalEncoding, defaultEncoding );
99
106
  const effectiveSearchRegex = mergeSearchRegExes(
@@ -101,6 +108,7 @@ export default class RegExBumper extends Plugin {
101
108
  [ searchFlags, globalSearchFlags ]
102
109
  );
103
110
  const effectiveReplacement = firstNotNil( replace, globalReplace, defaultReplace );
111
+ const effectiveStrictMode = firstNotNil( strict, globalStrict, defaultStrictMode );
104
112
 
105
113
  const replacedSearchRegex = prepareSearch( effectiveSearchRegex, context );
106
114
 
@@ -112,13 +120,15 @@ export default class RegExBumper extends Plugin {
112
120
 
113
121
  if ( isDryRun ) {
114
122
  await this.loadDiff();
123
+ const processedFileContent = replaceVersion( fileContent, replacedSearchRegex,
124
+ effectiveReplacement, context );
125
+ this.evaluateFileChanges( fileContent, processedFileContent,
126
+ replacedSearchRegex, effectiveStrictMode, file );
115
127
  if ( this.diff ) {
116
- const processedFileContent = replaceVersion( fileContent, replacedSearchRegex,
117
- effectiveReplacement, context );
118
- await this.diffAndReport( fileContent, processedFileContent, file );
128
+ await this.diffAndReport( fileContent, processedFileContent, effectiveStrictMode, file );
119
129
  continue;
120
130
  }
121
- await this.searchAndReport( fileContent, replacedSearchRegex, file );
131
+ await this.searchAndReport( fileContent, replacedSearchRegex );
122
132
  continue;
123
133
  }
124
134
 
@@ -129,10 +139,8 @@ export default class RegExBumper extends Plugin {
129
139
  context
130
140
  );
131
141
 
132
- if ( processedFileContent === fileContent ) {
133
- this.warnNoFileChange( file );
134
- }
135
- else {
142
+ if ( this.evaluateFileChanges( fileContent, processedFileContent,
143
+ replacedSearchRegex, effectiveStrictMode, file ) ) {
136
144
  await writeFile( file, processedFileContent, effectiveEncoding );
137
145
  }
138
146
  }
@@ -165,9 +173,6 @@ export default class RegExBumper extends Plugin {
165
173
  }
166
174
  );
167
175
 
168
- if ( _.isEmpty( diffResult.hunks ) ) {
169
- this.warnNoFileChange( filePath );
170
- }
171
176
  diffResult.hunks.forEach( ( hunk ) => {
172
177
  this.log.exec(
173
178
  `Replacing at line ${hunk.oldStart}:\n` +
@@ -188,9 +193,8 @@ export default class RegExBumper extends Plugin {
188
193
  } );
189
194
  }
190
195
 
191
- searchAndReport( content, searchRegex, filePath ) {
196
+ searchAndReport( content, searchRegex ) {
192
197
  const { isDryRun } = this.config;
193
- let foundMatch = false;
194
198
  const lineCounter = new LineCounter( content );
195
199
  XRegExp.forEach( content, searchRegex, ( match ) => {
196
200
  const matchText = match[0];
@@ -201,15 +205,31 @@ export default class RegExBumper extends Plugin {
201
205
  matchText,
202
206
  { isDryRun }
203
207
  );
204
- foundMatch = true;
205
208
  } );
206
- if ( !foundMatch ) {
207
- this.warnNoFileChange( filePath );
208
- }
209
209
  }
210
210
 
211
- warnNoFileChange( filePath ) {
212
- this.log.warn( `File "${filePath}" did not change!` );
211
+ evaluateFileChanges( originalFileContent, processedFileContent, searchRegex, strictMode, filePath ) {
212
+ const fileChanged = originalFileContent !== processedFileContent;
213
+ if ( fileChanged ) {
214
+ return fileChanged;
215
+ }
216
+ if ( strictMode && strictMode !== StrictMode.Off ) {
217
+ const match = XRegExp.match( originalFileContent, searchRegex, 'one' );
218
+ if ( match === null ) {
219
+ const message = `No matches found in file "${filePath}"!`;
220
+ if ( strictMode === StrictMode.ErrorIfNoMatch || strictMode === StrictMode.ErrorIfNoChange ) {
221
+ throw new Error( message );
222
+ }
223
+ this.log.warn( message );
224
+ return fileChanged;
225
+ }
226
+ const message = `File "${filePath}" did not change!`;
227
+ if ( strictMode === StrictMode.ErrorIfNoChange ) {
228
+ throw new Error( message );
229
+ }
230
+ this.log.warn( message );
231
+ }
232
+ return fileChanged;
213
233
  }
214
234
 
215
235
  }
@@ -287,13 +307,13 @@ function parseOutOptions( options ) {
287
307
  replace: null,
288
308
  };
289
309
  }
290
- const { encoding, replace } = option;
310
+ const { encoding, replace, strict } = option;
291
311
  const { searchRegex, flags } = parseSearchOptions( option.search );
292
312
  const files = option.files ? _.castArray( option.files ) : [];
293
313
  if ( option.file ) {
294
314
  files.unshift( option.file );
295
315
  }
296
- return { files, encoding, searchRegex, flags, replace };
316
+ return { files, encoding, searchRegex, flags, replace, strict };
297
317
  } );
298
318
  }
299
319
 
@@ -408,7 +428,7 @@ function prepareReplacement( replace, context ) {
408
428
  latestTag: context.latestTag || '',
409
429
  now: ( format ) => {
410
430
  if ( _.isNil( format ) ) {
411
- return dateFormatIso( context.executionTime );
431
+ return dateFormatISO( context.executionTime );
412
432
  }
413
433
  return dateFormat( context.executionTime, format );
414
434
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j-ulrich/release-it-regex-bumper",
3
- "version": "5.3.1",
3
+ "version": "5.5.0",
4
4
  "description": "Regular expression based version read/write plugin for release-it",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -41,29 +41,29 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "chalk": "^5.0.0",
44
- "date-fns": "^2.8.0",
44
+ "date-fns": "3 || 4",
45
45
  "fast-glob": "^3.2.0",
46
46
  "lodash": "^4.17.20",
47
47
  "semver": "^7.3.0",
48
48
  "xregexp": "^5.0.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "release-it": "16 || 17 || 18 || 19"
51
+ "release-it": "16 || 17 || 18 || 19 || 20"
52
52
  },
53
53
  "optionalDependencies": {
54
- "diff": "3 - 5"
54
+ "diff": "3 - 8"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@j-ulrich/eslint-config": "^1.0.0",
58
58
  "@release-it/keep-a-changelog": "^7.0.0",
59
- "ava": "^5.3.1",
60
- "c8": "^8.0.1",
59
+ "ava": "^7.0.0",
60
+ "c8": "^11.0.0",
61
61
  "eslint": "^8.6.0",
62
62
  "eslint-plugin-security": "^1.4.0",
63
- "release-it": "^19.0.1",
64
- "sinon": "^17 || ^18",
63
+ "release-it": "^20.0.0",
64
+ "sinon": "^21.0.3",
65
65
  "temp": "^0.9.4",
66
- "testdouble": "^3.16.4"
66
+ "testdouble": "^3.20.2"
67
67
  },
68
68
  "engines": {
69
69
  "node": ">=16"