@j-ulrich/release-it-regex-bumper 3.0.0 → 4.0.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.
- package/LICENSE +1 -1
- package/README.md +41 -16
- package/index.js +231 -170
- package/package.json +19 -10
- package/.github/workflows/CI.yml +0 -64
- package/.gitignore +0 -6
- package/.release-it.json +0 -29
- package/CHANGELOG.md +0 -152
- package/test.js +0 -670
package/index.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const readFile =
|
|
16
|
-
const writeFile =
|
|
17
|
-
|
|
18
|
-
const semanticVersionRegex = XRegExp(
|
|
19
|
-
|
|
1
|
+
|
|
2
|
+
import { readFile as fsReadFile, writeFile as fsWriteFile } from 'fs';
|
|
3
|
+
import { strict as assert } from 'assert';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import glob from 'fast-glob';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import _ from 'lodash';
|
|
8
|
+
import XRegExp from 'xregexp';
|
|
9
|
+
import semver from 'semver';
|
|
10
|
+
import { Plugin } from 'release-it';
|
|
11
|
+
import dateFormat from 'date-fns/format/index.js';
|
|
12
|
+
import dateFormatIso from 'date-fns/formatISO/index.js';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const readFile = promisify( fsReadFile );
|
|
16
|
+
const writeFile = promisify( fsWriteFile );
|
|
17
|
+
|
|
18
|
+
const semanticVersionRegex = XRegExp(
|
|
19
|
+
// eslint-disable-next-line security/detect-unsafe-regex
|
|
20
|
+
/(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?/
|
|
21
|
+
);
|
|
22
|
+
const placeholderRegex = XRegExp( '\\{\\{(?<placeholder>(?:[a-z][a-z0-9_]*|\\{))(?::(?<format>.*))?\\}\\}', 'ig' );
|
|
20
23
|
|
|
21
24
|
const prereleasePrefix = '-';
|
|
22
25
|
const buildPrefix = '+';
|
|
@@ -28,38 +31,51 @@ const defaultReplace = '{{version}}';
|
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
|
|
31
|
-
class RegExBumper extends Plugin {
|
|
34
|
+
export default class RegExBumper extends Plugin {
|
|
32
35
|
|
|
33
|
-
constructor(...args) {
|
|
34
|
-
super(...args);
|
|
35
|
-
this.setContext({executionTime: new Date()});
|
|
36
|
+
constructor( ...args ) {
|
|
37
|
+
super( ...args );
|
|
38
|
+
this.setContext( { executionTime: new Date() } );
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
async getLatestVersion() {
|
|
39
42
|
|
|
40
43
|
const { in: inOptions, search: globalSearchOptions, encoding: globalEncoding } = this.options;
|
|
41
44
|
if ( _.isNil( inOptions ) ) {
|
|
42
|
-
return;
|
|
45
|
+
return undefined;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
const context = Object.assign( {}, this.getContext(), this.config.contextOptions );
|
|
46
49
|
|
|
47
|
-
const {
|
|
48
|
-
|
|
50
|
+
const {
|
|
51
|
+
searchRegex: globalSearchRegex,
|
|
52
|
+
flags: globalSearchFlags,
|
|
53
|
+
versionCaptureGroup: globalVersionCaptureGroup,
|
|
54
|
+
} = parseSearchOptions( globalSearchOptions );
|
|
55
|
+
const { file, encoding, searchRegex, flags: searchFlags, versionCaptureGroup } = parseInOptions( inOptions );
|
|
49
56
|
|
|
50
57
|
const effectiveEncoding = firstNotNil( encoding, globalEncoding, defaultEncoding );
|
|
51
58
|
const fileContent = await readFile( file, { encoding: effectiveEncoding } );
|
|
52
|
-
const effectiveSearchRegex = mergeSearchRegExes(
|
|
59
|
+
const effectiveSearchRegex = mergeSearchRegExes(
|
|
60
|
+
[ searchRegex, globalSearchRegex, defaultSearchRegex ],
|
|
61
|
+
[ searchFlags, globalSearchFlags ]
|
|
62
|
+
);
|
|
53
63
|
const replacedSearchRegex = prepareSearch( effectiveSearchRegex, context );
|
|
54
|
-
const version = await extractVersion
|
|
55
|
-
|
|
64
|
+
const version = await extractVersion(
|
|
65
|
+
fileContent,
|
|
66
|
+
replacedSearchRegex,
|
|
67
|
+
firstNotNil( versionCaptureGroup, globalVersionCaptureGroup, defaultVersionCaptureGroup )
|
|
68
|
+
);
|
|
56
69
|
return version;
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
|
|
60
72
|
async bump( version ) {
|
|
61
|
-
|
|
62
|
-
|
|
73
|
+
const {
|
|
74
|
+
out: outOptions,
|
|
75
|
+
search: globalSearchOptions,
|
|
76
|
+
replace: globalReplace,
|
|
77
|
+
encoding: globalEncoding,
|
|
78
|
+
} = this.options;
|
|
63
79
|
const { isDryRun } = this.config;
|
|
64
80
|
if ( _.isNil( outOptions ) ) {
|
|
65
81
|
return;
|
|
@@ -69,15 +85,21 @@ class RegExBumper extends Plugin {
|
|
|
69
85
|
context.version = version;
|
|
70
86
|
}
|
|
71
87
|
|
|
72
|
-
const { searchRegex: globalSearchRegex, flags: globalSearchFlags } = parseSearchOptions.call(
|
|
88
|
+
const { searchRegex: globalSearchRegex, flags: globalSearchFlags } = parseSearchOptions.call(
|
|
89
|
+
this,
|
|
90
|
+
globalSearchOptions
|
|
91
|
+
);
|
|
73
92
|
const expandedOutOptions = await expandOutOptionFiles.call( this, parseOutOptions.call( this, outOptions ) );
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const { files, encoding, searchRegex, flags: searchFlags, replace } =
|
|
94
|
+
/* eslint-disable no-await-in-loop */
|
|
95
|
+
for ( const outOption of expandedOutOptions ) {
|
|
96
|
+
const { files, encoding, searchRegex, flags: searchFlags, replace } = outOption;
|
|
78
97
|
|
|
79
98
|
const effectiveEncoding = firstNotNil( encoding, globalEncoding, defaultEncoding );
|
|
80
|
-
const effectiveSearchRegex = mergeSearchRegExes(
|
|
99
|
+
const effectiveSearchRegex = mergeSearchRegExes(
|
|
100
|
+
[ searchRegex, globalSearchRegex, defaultSearchRegex ],
|
|
101
|
+
[ searchFlags, globalSearchFlags ]
|
|
102
|
+
);
|
|
81
103
|
const effectiveReplacement = firstNotNil( replace, globalReplace, defaultReplace );
|
|
82
104
|
|
|
83
105
|
const replacedSearchRegex = prepareSearch( effectiveSearchRegex, context );
|
|
@@ -89,26 +111,105 @@ class RegExBumper extends Plugin {
|
|
|
89
111
|
const fileContent = await readFile( file, { encoding: effectiveEncoding } );
|
|
90
112
|
|
|
91
113
|
if ( isDryRun ) {
|
|
92
|
-
loadDiff
|
|
114
|
+
await this.loadDiff();
|
|
93
115
|
if ( this.diff ) {
|
|
94
|
-
const processedFileContent = replaceVersion
|
|
95
|
-
|
|
116
|
+
const processedFileContent = replaceVersion( fileContent, replacedSearchRegex,
|
|
117
|
+
effectiveReplacement, context );
|
|
118
|
+
await this.diffAndReport( fileContent, processedFileContent, file );
|
|
96
119
|
continue;
|
|
97
120
|
}
|
|
98
|
-
await searchAndReport
|
|
121
|
+
await this.searchAndReport( fileContent, replacedSearchRegex, file );
|
|
99
122
|
continue;
|
|
100
123
|
}
|
|
101
124
|
|
|
102
|
-
const processedFileContent = replaceVersion
|
|
125
|
+
const processedFileContent = replaceVersion(
|
|
126
|
+
fileContent,
|
|
127
|
+
replacedSearchRegex,
|
|
128
|
+
effectiveReplacement,
|
|
129
|
+
context
|
|
130
|
+
);
|
|
103
131
|
|
|
104
|
-
if ( processedFileContent
|
|
105
|
-
warnNoFileChange
|
|
132
|
+
if ( processedFileContent === fileContent ) {
|
|
133
|
+
this.warnNoFileChange( file );
|
|
106
134
|
}
|
|
107
135
|
else {
|
|
108
136
|
await writeFile( file, processedFileContent, effectiveEncoding );
|
|
109
137
|
}
|
|
110
138
|
}
|
|
111
139
|
}
|
|
140
|
+
/* eslint-enable no-await-in-loop */
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async loadDiff() {
|
|
144
|
+
if ( this.diff ) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const diff = await import( 'diff' );
|
|
149
|
+
if ( !diff || !diff.structuredPatch ) {
|
|
150
|
+
throw new Error( 'diff module no available' );
|
|
151
|
+
}
|
|
152
|
+
this.diff = diff;
|
|
153
|
+
}
|
|
154
|
+
catch ( e ) {
|
|
155
|
+
this.log.info( 'Optional "diff" package not available' );
|
|
156
|
+
this.log.verbose( 'Exception was:', e );
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
diffAndReport( oldContent, newContent, filePath ) {
|
|
161
|
+
const { isDryRun } = this.config;
|
|
162
|
+
const diffResult = this.diff.structuredPatch( filePath, filePath, oldContent, newContent, undefined, undefined,
|
|
163
|
+
{
|
|
164
|
+
context: 0,
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if ( _.isEmpty( diffResult.hunks ) ) {
|
|
169
|
+
this.warnNoFileChange( filePath );
|
|
170
|
+
}
|
|
171
|
+
diffResult.hunks.forEach( ( hunk ) => {
|
|
172
|
+
this.log.exec(
|
|
173
|
+
`Replacing at line ${hunk.oldStart}:\n` +
|
|
174
|
+
hunk.lines
|
|
175
|
+
.map( ( line ) => {
|
|
176
|
+
const lineText = '\t' + line;
|
|
177
|
+
if ( line.startsWith( '-' ) ) {
|
|
178
|
+
return chalk.red( lineText );
|
|
179
|
+
}
|
|
180
|
+
if ( line.startsWith( '+' ) ) {
|
|
181
|
+
return chalk.green( lineText );
|
|
182
|
+
}
|
|
183
|
+
return lineText;
|
|
184
|
+
} )
|
|
185
|
+
.join( '\n' ),
|
|
186
|
+
{ isDryRun }
|
|
187
|
+
);
|
|
188
|
+
} );
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
searchAndReport( content, searchRegex, filePath ) {
|
|
192
|
+
const { isDryRun } = this.config;
|
|
193
|
+
let foundMatch = false;
|
|
194
|
+
const lineCounter = new LineCounter( content );
|
|
195
|
+
XRegExp.forEach( content, searchRegex, ( match ) => {
|
|
196
|
+
const matchText = match[0];
|
|
197
|
+
const matchIndex = searchRegex.lastIndex - matchText.length;
|
|
198
|
+
this.log.exec(
|
|
199
|
+
`Replacing match at line ${lineCounter.lineOfIndex( matchIndex )}, ` +
|
|
200
|
+
`column ${lineCounter.columnOfIndex( matchIndex )}:\n\t` +
|
|
201
|
+
matchText,
|
|
202
|
+
{ isDryRun }
|
|
203
|
+
);
|
|
204
|
+
foundMatch = true;
|
|
205
|
+
} );
|
|
206
|
+
if ( !foundMatch ) {
|
|
207
|
+
this.warnNoFileChange( filePath );
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
warnNoFileChange( filePath ) {
|
|
212
|
+
this.log.warn( `File "${filePath}" did not change!` );
|
|
112
213
|
}
|
|
113
214
|
|
|
114
215
|
}
|
|
@@ -124,7 +225,7 @@ function parseInOptions( options ) {
|
|
|
124
225
|
const { file, encoding } = options;
|
|
125
226
|
const { searchRegex, flags, versionCaptureGroup } = parseSearchOptions( options.search );
|
|
126
227
|
if ( !file ) {
|
|
127
|
-
throw new Error(
|
|
228
|
+
throw new Error( "Missing 'file' property in 'in' options" );
|
|
128
229
|
}
|
|
129
230
|
return { file, encoding, searchRegex, flags, versionCaptureGroup };
|
|
130
231
|
}
|
|
@@ -139,7 +240,7 @@ function parseSearchOptions( options ) {
|
|
|
139
240
|
return { searchRegex, versionCaptureGroup };
|
|
140
241
|
}
|
|
141
242
|
const { pattern, flags, versionCaptureGroup } = options;
|
|
142
|
-
const searchRegex = _.isNil(pattern) ? pattern : XRegExp( pattern, _.isNull( flags ) ? undefined : flags );
|
|
243
|
+
const searchRegex = _.isNil( pattern ) ? pattern : XRegExp( pattern, _.isNull( flags ) ? undefined : flags );
|
|
143
244
|
return { searchRegex, flags, versionCaptureGroup };
|
|
144
245
|
}
|
|
145
246
|
|
|
@@ -149,37 +250,43 @@ function extractVersion( content, versionRegex, versionCaptureGroup ) {
|
|
|
149
250
|
throw new Error( 'Could not extract version from file' );
|
|
150
251
|
}
|
|
151
252
|
if ( !_.isNil( versionCaptureGroup ) ) {
|
|
152
|
-
if(
|
|
153
|
-
|
|
253
|
+
if ( hasOwnProperty( match, versionCaptureGroup ) ) {
|
|
254
|
+
// object injection mitigated by checking hasOwnProperty()
|
|
255
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
256
|
+
return match[versionCaptureGroup];
|
|
154
257
|
}
|
|
155
258
|
}
|
|
156
259
|
else {
|
|
157
|
-
if (
|
|
158
|
-
return match[
|
|
260
|
+
if ( hasOwnProperty( match, 'version' ) ) {
|
|
261
|
+
return match['version'];
|
|
159
262
|
}
|
|
160
|
-
if (
|
|
161
|
-
return match[
|
|
263
|
+
if ( hasOwnProperty( match, 1 ) ) {
|
|
264
|
+
return match[1];
|
|
162
265
|
}
|
|
163
266
|
}
|
|
164
267
|
|
|
165
|
-
return match[
|
|
268
|
+
return match[0];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function hasOwnProperty( obj, prop ) {
|
|
272
|
+
return Object.prototype.hasOwnProperty.call( obj, prop );
|
|
166
273
|
}
|
|
167
274
|
|
|
168
275
|
function parseOutOptions( options ) {
|
|
169
|
-
return _.castArray( options ).map(
|
|
170
|
-
if ( _.isString(
|
|
276
|
+
return _.castArray( options ).map( ( option ) => {
|
|
277
|
+
if ( _.isString( option ) ) {
|
|
171
278
|
return {
|
|
172
|
-
files: [
|
|
279
|
+
files: [ option ],
|
|
173
280
|
encoding: null,
|
|
174
281
|
searchRegex: null,
|
|
175
282
|
replace: null,
|
|
176
283
|
};
|
|
177
284
|
}
|
|
178
|
-
const { encoding, replace } =
|
|
179
|
-
const { searchRegex, flags } = parseSearchOptions(
|
|
180
|
-
const files =
|
|
181
|
-
if(
|
|
182
|
-
files.unshift(
|
|
285
|
+
const { encoding, replace } = option;
|
|
286
|
+
const { searchRegex, flags } = parseSearchOptions( option.search );
|
|
287
|
+
const files = option.files ? _.castArray( option.files ) : [];
|
|
288
|
+
if ( option.file ) {
|
|
289
|
+
files.unshift( option.file );
|
|
183
290
|
}
|
|
184
291
|
return { files, encoding, searchRegex, flags, replace };
|
|
185
292
|
} );
|
|
@@ -202,55 +309,6 @@ async function expandOutOptionFiles( options ) {
|
|
|
202
309
|
return options;
|
|
203
310
|
}
|
|
204
311
|
|
|
205
|
-
function loadDiff() {
|
|
206
|
-
if ( !diff || diff instanceof Error ) {
|
|
207
|
-
this.log.info( 'Optional "diff" package not available' );
|
|
208
|
-
this.log.verbose( 'Exception was:', diff );
|
|
209
|
-
}
|
|
210
|
-
this.diff = diff;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function diffAndReport( oldContent, newContent, filePath ) {
|
|
214
|
-
const { isDryRun } = this.config;
|
|
215
|
-
const diffResult = this.diff.structuredPatch( filePath, filePath, oldContent, newContent, undefined, undefined, { context: 0 } );
|
|
216
|
-
|
|
217
|
-
if ( _.isEmpty( diffResult.hunks ) ) {
|
|
218
|
-
warnNoFileChange.call( this, filePath );
|
|
219
|
-
}
|
|
220
|
-
diffResult.hunks.forEach( hunk => {
|
|
221
|
-
this.log.exec( `Replacing at line ${hunk.oldStart}:\n` + hunk.lines.map( line => {
|
|
222
|
-
const lineText = '\t' + line;
|
|
223
|
-
if ( line.startsWith( '-' ) ) {
|
|
224
|
-
return chalk.red( lineText );
|
|
225
|
-
}
|
|
226
|
-
if ( line.startsWith( '+' ) ) {
|
|
227
|
-
return chalk.green( lineText );
|
|
228
|
-
}
|
|
229
|
-
return lineText;
|
|
230
|
-
} ).join( '\n' ), { isDryRun } );
|
|
231
|
-
} );
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function searchAndReport( content, searchRegex, filePath ) {
|
|
236
|
-
const { isDryRun } = this.config;
|
|
237
|
-
let foundMatch = false;
|
|
238
|
-
const lineCounter = new LineCounter( content );
|
|
239
|
-
XRegExp.forEach( content, searchRegex, ( match ) => {
|
|
240
|
-
const matchText = match[ 0 ];
|
|
241
|
-
const matchIndex = searchRegex.lastIndex - matchText.length;
|
|
242
|
-
this.log.exec( `Replacing match at line ${lineCounter.lineOfIndex( matchIndex )}, column ${lineCounter.columnOfIndex( matchIndex )}:\n\t` + matchText, { isDryRun } );
|
|
243
|
-
foundMatch = true;
|
|
244
|
-
} );
|
|
245
|
-
if ( !foundMatch ) {
|
|
246
|
-
warnNoFileChange.call( this, filePath );
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function warnNoFileChange( filePath ) {
|
|
251
|
-
this.log.warn( `File "${filePath}" did not change!` );
|
|
252
|
-
}
|
|
253
|
-
|
|
254
312
|
function mergeSearchRegExes( regExCandidates, flagCandidates ) {
|
|
255
313
|
const searchRegEx = firstNotNil( ...regExCandidates );
|
|
256
314
|
const flags = firstNotNil( ...flagCandidates );
|
|
@@ -260,58 +318,69 @@ function mergeSearchRegExes( regExCandidates, flagCandidates ) {
|
|
|
260
318
|
function prepareSearch( searchRegEx, context ) {
|
|
261
319
|
const pattern = searchRegEx.xregexp.source;
|
|
262
320
|
const placeholderMap = {
|
|
263
|
-
|
|
264
|
-
if( _.isNil( format ) ) {
|
|
265
|
-
throw new Error(
|
|
321
|
+
now: ( format ) => {
|
|
322
|
+
if ( _.isNil( format ) ) {
|
|
323
|
+
throw new Error( "Missing required parameter 'format' for placeholder {{now}}" );
|
|
266
324
|
}
|
|
267
325
|
return XRegExp.escape( dateFormat( context.executionTime, format ) );
|
|
268
326
|
},
|
|
269
|
-
|
|
327
|
+
semver: `${semanticVersionRegex.xregexp.source || semanticVersionRegex.source}`,
|
|
270
328
|
};
|
|
271
329
|
const parsedVer = semver.parse( context.latestVersion );
|
|
272
|
-
if( parsedVer ) {
|
|
330
|
+
if ( parsedVer ) {
|
|
273
331
|
Object.assign( placeholderMap, {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
332
|
+
version: XRegExp.escape( parsedVer.raw ),
|
|
333
|
+
major: parsedVer.major,
|
|
334
|
+
minor: parsedVer.minor,
|
|
335
|
+
patch: parsedVer.patch,
|
|
336
|
+
prerelease: XRegExp.escape( parsedVer.prerelease.join( '.' ) ),
|
|
337
|
+
prefixedPrerelease:
|
|
338
|
+
parsedVer.prerelease.length > 0
|
|
339
|
+
? XRegExp.escape( prereleasePrefix + parsedVer.prerelease.join( '.' ) )
|
|
340
|
+
: '',
|
|
341
|
+
build: XRegExp.escape( parsedVer.build.join( '.' ) ),
|
|
342
|
+
prefixedBuild:
|
|
343
|
+
parsedVer.build.length > 0 ? XRegExp.escape( buildPrefix + parsedVer.build.join( '.' ) ) : '',
|
|
344
|
+
versionWithoutBuild: XRegExp.escape( parsedVer.version ),
|
|
345
|
+
versionWithoutPrerelease: XRegExp.escape( `${parsedVer.major}.${parsedVer.minor}.${parsedVer.patch}` ),
|
|
285
346
|
} );
|
|
286
347
|
}
|
|
287
348
|
if ( context.latestTag ) {
|
|
288
|
-
Object.assign( placeholderMap, {
|
|
349
|
+
Object.assign( placeholderMap, { tag: XRegExp.escape( context.latestTag ) } );
|
|
289
350
|
}
|
|
290
351
|
|
|
291
352
|
if ( context.version ) {
|
|
292
|
-
Object.assign( placeholderMap, {
|
|
353
|
+
Object.assign( placeholderMap, { newVersion: XRegExp.escape( context.version ) } );
|
|
293
354
|
}
|
|
294
355
|
|
|
295
|
-
|
|
356
|
+
wrapSearchPatternPlaceholders( placeholderMap );
|
|
357
|
+
const replacedPattern = replacePlaceholders( pattern, placeholderMap );
|
|
358
|
+
return XRegExp( replacedPattern, searchRegEx.xregexp.flags || undefined );
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function wrapSearchPatternPlaceholders( placeholderMap ) {
|
|
362
|
+
for ( const placeholder of Object.keys( placeholderMap ) ) {
|
|
363
|
+
// object injection mitigated for placeholderMap since placeholder is from Object.keys( placeholderMap )
|
|
364
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
296
365
|
const replacement = placeholderMap[ placeholder ];
|
|
297
|
-
if( _.isFunction( replacement ) ) {
|
|
366
|
+
if ( _.isFunction( replacement ) ) {
|
|
367
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
298
368
|
placeholderMap[ placeholder ] = ( ...args ) => {
|
|
299
369
|
return wrapInRegexGroup( replacement( ...args ) );
|
|
300
370
|
};
|
|
301
371
|
continue;
|
|
302
372
|
}
|
|
373
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
303
374
|
placeholderMap[ placeholder ] = wrapInRegexGroup( replacement );
|
|
304
375
|
}
|
|
305
|
-
const replacedPattern = replacePlaceholders( pattern, placeholderMap );
|
|
306
|
-
return XRegExp( replacedPattern, searchRegEx.xregexp.flags || undefined );
|
|
307
376
|
}
|
|
308
377
|
|
|
309
378
|
function wrapInRegexGroup( pattern ) {
|
|
310
|
-
return
|
|
379
|
+
return `(?:${pattern})`;
|
|
311
380
|
}
|
|
312
381
|
|
|
313
382
|
function replaceVersion( content, searchRegex, replace, context ) {
|
|
314
|
-
const processedReplace = prepareReplacement
|
|
383
|
+
const processedReplace = prepareReplacement( replace, context );
|
|
315
384
|
const processedContent = XRegExp.replace( content, searchRegex, processedReplace );
|
|
316
385
|
return processedContent;
|
|
317
386
|
}
|
|
@@ -319,24 +388,25 @@ function replaceVersion( content, searchRegex, replace, context ) {
|
|
|
319
388
|
function prepareReplacement( replace, context ) {
|
|
320
389
|
const parsedVer = semver.parse( context.version );
|
|
321
390
|
const placeholderMap = {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
'
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
391
|
+
version: parsedVer.raw,
|
|
392
|
+
major: parsedVer.major,
|
|
393
|
+
minor: parsedVer.minor,
|
|
394
|
+
patch: parsedVer.patch,
|
|
395
|
+
prerelease: parsedVer.prerelease.join( '.' ),
|
|
396
|
+
prefixedPrerelease:
|
|
397
|
+
parsedVer.prerelease.length > 0 ? prereleasePrefix + parsedVer.prerelease.join( '.' ) : '',
|
|
398
|
+
build: parsedVer.build.join( '.' ),
|
|
399
|
+
prefixedBuild: parsedVer.build.length > 0 ? buildPrefix + parsedVer.build.join( '.' ) : '',
|
|
400
|
+
versionWithoutBuild: parsedVer.version,
|
|
401
|
+
versionWithoutPrerelease: `${parsedVer.major}.${parsedVer.minor}.${parsedVer.patch}`,
|
|
402
|
+
latestVersion: context.latestVersion || '',
|
|
403
|
+
latestTag: context.latestTag || '',
|
|
404
|
+
now: ( format ) => {
|
|
405
|
+
if ( _.isNil( format ) ) {
|
|
336
406
|
return dateFormatIso( context.executionTime );
|
|
337
407
|
}
|
|
338
408
|
return dateFormat( context.executionTime, format );
|
|
339
|
-
}
|
|
409
|
+
},
|
|
340
410
|
};
|
|
341
411
|
return replacePlaceholders( replace, placeholderMap );
|
|
342
412
|
}
|
|
@@ -344,9 +414,11 @@ function prepareReplacement( replace, context ) {
|
|
|
344
414
|
function replacePlaceholders( template, placeholderMap ) {
|
|
345
415
|
placeholderMap = Object.assign( {}, placeholderMap, { '{': '{' } );
|
|
346
416
|
return XRegExp.replace( template, placeholderRegex, ( match, placeholder, format ) => {
|
|
347
|
-
if ( !
|
|
417
|
+
if ( !hasOwnProperty( placeholderMap, placeholder ) ) {
|
|
348
418
|
throw new Error( `Unknown placeholder encountered: {{${placeholder}}}` );
|
|
349
419
|
}
|
|
420
|
+
// object injection mitigated by checking hasOwnProperty()
|
|
421
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
350
422
|
const replacement = placeholderMap[ placeholder ];
|
|
351
423
|
if ( _.isFunction( replacement ) ) {
|
|
352
424
|
if ( _.isString( format ) ) {
|
|
@@ -358,46 +430,35 @@ function replacePlaceholders( template, placeholderMap ) {
|
|
|
358
430
|
} );
|
|
359
431
|
}
|
|
360
432
|
|
|
361
|
-
|
|
362
|
-
function optionalRequire( packageName ) {
|
|
363
|
-
try {
|
|
364
|
-
return require( packageName );
|
|
365
|
-
}
|
|
366
|
-
/* c8 ignore next 4 */
|
|
367
|
-
/* rewiremock can't simulate the absence of a package so we can't test this case */
|
|
368
|
-
catch ( ex ) {
|
|
369
|
-
return ex;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
433
|
function firstNotNil( ...args ) {
|
|
374
|
-
return args.find( ele => !_.isNil( ele ) );
|
|
434
|
+
return args.find( ( ele ) => !_.isNil( ele ) );
|
|
375
435
|
}
|
|
376
436
|
|
|
377
437
|
class LineCounter {
|
|
378
438
|
constructor( text ) {
|
|
379
439
|
let index = 0;
|
|
380
|
-
this.lineEndIndex = text.split( '\n' ).map( line => {
|
|
440
|
+
this.lineEndIndex = text.split( '\n' ).map( ( line ) => {
|
|
381
441
|
index += line.length + 1;
|
|
382
442
|
return index;
|
|
383
443
|
} );
|
|
384
444
|
}
|
|
385
445
|
|
|
386
446
|
lineOfIndex( index ) {
|
|
387
|
-
|
|
447
|
+
// eslint-disable-next-line no-extra-parens
|
|
448
|
+
const arrayIndex = this.lineEndIndex.findIndex( ( lineEndIndex ) => index <= lineEndIndex );
|
|
388
449
|
assert( arrayIndex >= 0 );
|
|
389
450
|
return arrayIndex + 1;
|
|
390
451
|
}
|
|
391
452
|
|
|
392
453
|
columnOfIndex( index ) {
|
|
393
|
-
const
|
|
394
|
-
assert(
|
|
395
|
-
if (
|
|
454
|
+
const lineNumber = this.lineOfIndex( index );
|
|
455
|
+
assert( lineNumber >= 1 );
|
|
456
|
+
if ( lineNumber === 1 ) {
|
|
396
457
|
return index;
|
|
397
458
|
}
|
|
398
|
-
const
|
|
459
|
+
const previousLineNumber = lineNumber - 1;
|
|
460
|
+
const previousLineEndIndex = this.lineEndIndex[ previousLineNumber - 1 ];
|
|
399
461
|
return index - previousLineEndIndex;
|
|
400
462
|
}
|
|
401
463
|
}
|
|
402
464
|
|
|
403
|
-
module.exports = RegExBumper;
|
package/package.json
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j-ulrich/release-it-regex-bumper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Regular expression based version read/write plugin for release-it",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js"
|
|
9
|
+
],
|
|
6
10
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
-
"test+coverage": "npx c8
|
|
11
|
+
"test": "node --experimental-loader=testdouble ./node_modules/ava/entrypoints/cli.mjs --serial",
|
|
12
|
+
"test+coverage": "npx c8 node --experimental-loader=testdouble ./node_modules/ava/entrypoints/cli.mjs --serial",
|
|
13
|
+
"lint": "npx eslint .",
|
|
9
14
|
"release": "npx release-it"
|
|
10
15
|
},
|
|
11
16
|
"keywords": [
|
|
@@ -42,20 +47,24 @@
|
|
|
42
47
|
"xregexp": "^4.3.0"
|
|
43
48
|
},
|
|
44
49
|
"peerDependencies": {
|
|
45
|
-
"release-it": "
|
|
50
|
+
"release-it": "15 - 16"
|
|
46
51
|
},
|
|
47
52
|
"optionalDependencies": {
|
|
48
53
|
"diff": "^3.0.0 || ^4.0.0"
|
|
49
54
|
},
|
|
50
55
|
"devDependencies": {
|
|
51
|
-
"
|
|
56
|
+
"@j-ulrich/eslint-config": "^1.0.0",
|
|
57
|
+
"@release-it/keep-a-changelog": "^3.0.0",
|
|
58
|
+
"ava": "^4.0.1",
|
|
52
59
|
"c8": "^7.3.0",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"sinon": "^
|
|
60
|
+
"eslint": "^8.6.0",
|
|
61
|
+
"eslint-plugin-security": "^1.4.0",
|
|
62
|
+
"release-it": "^15.0.0",
|
|
63
|
+
"sinon": "^12.0.1",
|
|
64
|
+
"temp": "^0.9.4",
|
|
65
|
+
"testdouble": "^3.16.4"
|
|
57
66
|
},
|
|
58
67
|
"engines": {
|
|
59
|
-
"node": ">=
|
|
68
|
+
"node": ">=14.9"
|
|
60
69
|
}
|
|
61
70
|
}
|