@j-ulrich/release-it-regex-bumper 2.0.0 → 3.0.2
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 +108 -30
- package/index.js +272 -114
- package/package.json +12 -3
- package/.github/workflows/CI.yml +0 -64
- package/.gitignore +0 -6
- package/.release-it.json +0 -14
- package/CHANGELOG.md +0 -132
- package/test.js +0 -484
package/index.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const fs = require( 'fs' );
|
|
2
4
|
const assert = require( 'assert' ).strict;
|
|
3
5
|
const util = require( 'util' );
|
|
4
6
|
const glob = require( 'fast-glob' );
|
|
5
7
|
const chalk = require( 'chalk' );
|
|
6
|
-
const _ =
|
|
8
|
+
const _ = {
|
|
9
|
+
isEmpty: require( 'lodash/isEmpty' ),
|
|
10
|
+
isNil: require( 'lodash/isNil' ),
|
|
11
|
+
isNull: require( 'lodash/isNull' ),
|
|
12
|
+
isString: require( 'lodash/isString' ),
|
|
13
|
+
isFunction: require( 'lodash/isFunction' ),
|
|
14
|
+
castArray: require( 'lodash/castArray' ),
|
|
15
|
+
};
|
|
7
16
|
const XRegExp = require( 'xregexp' );
|
|
8
17
|
const semver = require( 'semver' );
|
|
9
18
|
const { Plugin } = require( 'release-it' );
|
|
@@ -15,9 +24,17 @@ const diff = optionalRequire( 'diff' );
|
|
|
15
24
|
const readFile = util.promisify( fs.readFile );
|
|
16
25
|
const writeFile = util.promisify( fs.writeFile );
|
|
17
26
|
|
|
27
|
+
const semanticVersionRegex = XRegExp(
|
|
28
|
+
// eslint-disable-next-line security/detect-unsafe-regex
|
|
29
|
+
/(?: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-]+)*)?/
|
|
30
|
+
);
|
|
31
|
+
const placeholderRegex = XRegExp( '\\{\\{(?<placeholder>(?:[a-z][a-z0-9_]*|\\{))(?::(?<format>.*))?\\}\\}', 'ig' );
|
|
32
|
+
|
|
33
|
+
const prereleasePrefix = '-';
|
|
34
|
+
const buildPrefix = '+';
|
|
18
35
|
|
|
19
36
|
const defaultEncoding = 'utf-8';
|
|
20
|
-
const defaultSearchRegex = XRegExp(
|
|
37
|
+
const defaultSearchRegex = XRegExp( '{{semver}}' );
|
|
21
38
|
const defaultVersionCaptureGroup = null;
|
|
22
39
|
const defaultReplace = '{{version}}';
|
|
23
40
|
|
|
@@ -25,47 +42,77 @@ const defaultReplace = '{{version}}';
|
|
|
25
42
|
|
|
26
43
|
class RegExBumper extends Plugin {
|
|
27
44
|
|
|
45
|
+
constructor( ...args ) {
|
|
46
|
+
super( ...args );
|
|
47
|
+
this.setContext( { executionTime: new Date() } );
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
async getLatestVersion() {
|
|
29
51
|
|
|
30
52
|
const { in: inOptions, search: globalSearchOptions, encoding: globalEncoding } = this.options;
|
|
31
53
|
if ( _.isNil( inOptions ) ) {
|
|
32
|
-
return;
|
|
54
|
+
return undefined;
|
|
33
55
|
}
|
|
34
56
|
|
|
35
|
-
const
|
|
36
|
-
|
|
57
|
+
const context = Object.assign( {}, this.getContext(), this.config.contextOptions );
|
|
58
|
+
|
|
59
|
+
const {
|
|
60
|
+
searchRegex: globalSearchRegex,
|
|
61
|
+
flags: globalSearchFlags,
|
|
62
|
+
versionCaptureGroup: globalVersionCaptureGroup,
|
|
63
|
+
} = parseSearchOptions( globalSearchOptions );
|
|
64
|
+
const { file, encoding, searchRegex, flags: searchFlags, versionCaptureGroup } = parseInOptions( inOptions );
|
|
37
65
|
|
|
38
66
|
const effectiveEncoding = firstNotNil( encoding, globalEncoding, defaultEncoding );
|
|
39
67
|
const fileContent = await readFile( file, { encoding: effectiveEncoding } );
|
|
40
|
-
const
|
|
41
|
-
|
|
68
|
+
const effectiveSearchRegex = mergeSearchRegExes(
|
|
69
|
+
[ searchRegex, globalSearchRegex, defaultSearchRegex ],
|
|
70
|
+
[ searchFlags, globalSearchFlags ]
|
|
71
|
+
);
|
|
72
|
+
const replacedSearchRegex = prepareSearch( effectiveSearchRegex, context );
|
|
73
|
+
const version = await extractVersion(
|
|
74
|
+
fileContent,
|
|
75
|
+
replacedSearchRegex,
|
|
76
|
+
firstNotNil( versionCaptureGroup, globalVersionCaptureGroup, defaultVersionCaptureGroup )
|
|
77
|
+
);
|
|
42
78
|
return version;
|
|
43
79
|
}
|
|
44
80
|
|
|
45
|
-
|
|
46
81
|
async bump( version ) {
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
const {
|
|
83
|
+
out: outOptions,
|
|
84
|
+
search: globalSearchOptions,
|
|
85
|
+
replace: globalReplace,
|
|
86
|
+
encoding: globalEncoding,
|
|
87
|
+
} = this.options;
|
|
49
88
|
const { isDryRun } = this.config;
|
|
50
89
|
if ( _.isNil( outOptions ) ) {
|
|
51
90
|
return;
|
|
52
91
|
}
|
|
53
|
-
const context = Object.assign( {}, this.config.contextOptions );
|
|
92
|
+
const context = Object.assign( {}, this.getContext(), this.config.contextOptions );
|
|
54
93
|
if ( !context.version ) {
|
|
55
94
|
context.version = version;
|
|
56
95
|
}
|
|
57
96
|
|
|
58
|
-
const { searchRegex: globalSearchRegex } = parseSearchOptions.call(
|
|
97
|
+
const { searchRegex: globalSearchRegex, flags: globalSearchFlags } = parseSearchOptions.call(
|
|
98
|
+
this,
|
|
99
|
+
globalSearchOptions
|
|
100
|
+
);
|
|
59
101
|
const expandedOutOptions = await expandOutOptionFiles.call( this, parseOutOptions.call( this, outOptions ) );
|
|
60
102
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const { files, encoding, searchRegex, replace } =
|
|
103
|
+
/* eslint-disable no-await-in-loop */
|
|
104
|
+
for ( const outOption of expandedOutOptions ) {
|
|
105
|
+
const { files, encoding, searchRegex, flags: searchFlags, replace } = outOption;
|
|
64
106
|
|
|
65
107
|
const effectiveEncoding = firstNotNil( encoding, globalEncoding, defaultEncoding );
|
|
66
|
-
const effectiveSearchRegex =
|
|
108
|
+
const effectiveSearchRegex = mergeSearchRegExes(
|
|
109
|
+
[ searchRegex, globalSearchRegex, defaultSearchRegex ],
|
|
110
|
+
[ searchFlags, globalSearchFlags ]
|
|
111
|
+
);
|
|
67
112
|
const effectiveReplacement = firstNotNil( replace, globalReplace, defaultReplace );
|
|
68
113
|
|
|
114
|
+
const replacedSearchRegex = prepareSearch( effectiveSearchRegex, context );
|
|
115
|
+
|
|
69
116
|
for ( const file of files ) {
|
|
70
117
|
|
|
71
118
|
this.log.info( `Updating version in ${file}` );
|
|
@@ -73,26 +120,96 @@ class RegExBumper extends Plugin {
|
|
|
73
120
|
const fileContent = await readFile( file, { encoding: effectiveEncoding } );
|
|
74
121
|
|
|
75
122
|
if ( isDryRun ) {
|
|
76
|
-
loadDiff
|
|
123
|
+
this.loadDiff();
|
|
77
124
|
if ( this.diff ) {
|
|
78
|
-
const processedFileContent = replaceVersion
|
|
79
|
-
|
|
125
|
+
const processedFileContent = replaceVersion( fileContent, replacedSearchRegex,
|
|
126
|
+
effectiveReplacement, context );
|
|
127
|
+
await this.diffAndReport( fileContent, processedFileContent, file );
|
|
80
128
|
continue;
|
|
81
129
|
}
|
|
82
|
-
await searchAndReport
|
|
130
|
+
await this.searchAndReport( fileContent, replacedSearchRegex, file );
|
|
83
131
|
continue;
|
|
84
132
|
}
|
|
85
133
|
|
|
86
|
-
const processedFileContent = replaceVersion
|
|
134
|
+
const processedFileContent = replaceVersion(
|
|
135
|
+
fileContent,
|
|
136
|
+
replacedSearchRegex,
|
|
137
|
+
effectiveReplacement,
|
|
138
|
+
context
|
|
139
|
+
);
|
|
87
140
|
|
|
88
|
-
if ( processedFileContent
|
|
89
|
-
warnNoFileChange
|
|
141
|
+
if ( processedFileContent === fileContent ) {
|
|
142
|
+
this.warnNoFileChange( file );
|
|
90
143
|
}
|
|
91
144
|
else {
|
|
92
145
|
await writeFile( file, processedFileContent, effectiveEncoding );
|
|
93
146
|
}
|
|
94
147
|
}
|
|
95
148
|
}
|
|
149
|
+
/* eslint-enable no-await-in-loop */
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
loadDiff() {
|
|
153
|
+
if ( !diff || diff instanceof Error ) {
|
|
154
|
+
this.log.info( 'Optional "diff" package not available' );
|
|
155
|
+
this.log.verbose( 'Exception was:', diff );
|
|
156
|
+
}
|
|
157
|
+
this.diff = diff;
|
|
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!` );
|
|
96
213
|
}
|
|
97
214
|
|
|
98
215
|
}
|
|
@@ -106,11 +223,11 @@ function parseInOptions( options ) {
|
|
|
106
223
|
return { file, encoding, searchRegex, versionCaptureGroup };
|
|
107
224
|
}
|
|
108
225
|
const { file, encoding } = options;
|
|
109
|
-
const { searchRegex, versionCaptureGroup } = parseSearchOptions( options.search );
|
|
226
|
+
const { searchRegex, flags, versionCaptureGroup } = parseSearchOptions( options.search );
|
|
110
227
|
if ( !file ) {
|
|
111
|
-
throw new Error(
|
|
228
|
+
throw new Error( "Missing 'file' property in 'in' options" );
|
|
112
229
|
}
|
|
113
|
-
return { file, encoding, searchRegex, versionCaptureGroup };
|
|
230
|
+
return { file, encoding, searchRegex, flags, versionCaptureGroup };
|
|
114
231
|
}
|
|
115
232
|
|
|
116
233
|
function parseSearchOptions( options ) {
|
|
@@ -123,8 +240,8 @@ function parseSearchOptions( options ) {
|
|
|
123
240
|
return { searchRegex, versionCaptureGroup };
|
|
124
241
|
}
|
|
125
242
|
const { pattern, flags, versionCaptureGroup } = options;
|
|
126
|
-
const searchRegex = XRegExp( pattern, _.isNull( flags ) ? undefined : flags );
|
|
127
|
-
return { searchRegex, versionCaptureGroup };
|
|
243
|
+
const searchRegex = _.isNil( pattern ) ? pattern : XRegExp( pattern, _.isNull( flags ) ? undefined : flags );
|
|
244
|
+
return { searchRegex, flags, versionCaptureGroup };
|
|
128
245
|
}
|
|
129
246
|
|
|
130
247
|
function extractVersion( content, versionRegex, versionCaptureGroup ) {
|
|
@@ -133,39 +250,45 @@ function extractVersion( content, versionRegex, versionCaptureGroup ) {
|
|
|
133
250
|
throw new Error( 'Could not extract version from file' );
|
|
134
251
|
}
|
|
135
252
|
if ( !_.isNil( versionCaptureGroup ) ) {
|
|
136
|
-
if(
|
|
137
|
-
|
|
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];
|
|
138
257
|
}
|
|
139
258
|
}
|
|
140
259
|
else {
|
|
141
|
-
if (
|
|
142
|
-
return match[
|
|
260
|
+
if ( hasOwnProperty( match, 'version' ) ) {
|
|
261
|
+
return match['version'];
|
|
143
262
|
}
|
|
144
|
-
if (
|
|
145
|
-
return match[
|
|
263
|
+
if ( hasOwnProperty( match, 1 ) ) {
|
|
264
|
+
return match[1];
|
|
146
265
|
}
|
|
147
266
|
}
|
|
148
267
|
|
|
149
|
-
return match[
|
|
268
|
+
return match[0];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function hasOwnProperty( obj, prop ) {
|
|
272
|
+
return Object.prototype.hasOwnProperty.call( obj, prop );
|
|
150
273
|
}
|
|
151
274
|
|
|
152
275
|
function parseOutOptions( options ) {
|
|
153
|
-
return _.castArray( options ).map(
|
|
154
|
-
if ( _.isString(
|
|
276
|
+
return _.castArray( options ).map( ( option ) => {
|
|
277
|
+
if ( _.isString( option ) ) {
|
|
155
278
|
return {
|
|
156
|
-
files: [
|
|
279
|
+
files: [ option ],
|
|
157
280
|
encoding: null,
|
|
158
281
|
searchRegex: null,
|
|
159
282
|
replace: null,
|
|
160
283
|
};
|
|
161
284
|
}
|
|
162
|
-
const { encoding, replace } =
|
|
163
|
-
const { searchRegex } = parseSearchOptions(
|
|
164
|
-
const files =
|
|
165
|
-
if(
|
|
166
|
-
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 );
|
|
167
290
|
}
|
|
168
|
-
return { files, encoding, searchRegex, replace };
|
|
291
|
+
return { files, encoding, searchRegex, flags, replace };
|
|
169
292
|
} );
|
|
170
293
|
}
|
|
171
294
|
|
|
@@ -186,132 +309,167 @@ async function expandOutOptionFiles( options ) {
|
|
|
186
309
|
return options;
|
|
187
310
|
}
|
|
188
311
|
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
this.diff = diff;
|
|
312
|
+
function mergeSearchRegExes( regExCandidates, flagCandidates ) {
|
|
313
|
+
const searchRegEx = firstNotNil( ...regExCandidates );
|
|
314
|
+
const flags = firstNotNil( ...flagCandidates );
|
|
315
|
+
return XRegExp( searchRegEx.xregexp.source, flags );
|
|
195
316
|
}
|
|
196
317
|
|
|
197
|
-
function
|
|
198
|
-
const
|
|
199
|
-
const
|
|
318
|
+
function prepareSearch( searchRegEx, context ) {
|
|
319
|
+
const pattern = searchRegEx.xregexp.source;
|
|
320
|
+
const placeholderMap = {
|
|
321
|
+
now: ( format ) => {
|
|
322
|
+
if ( _.isNil( format ) ) {
|
|
323
|
+
throw new Error( "Missing required parameter 'format' for placeholder {{now}}" );
|
|
324
|
+
}
|
|
325
|
+
return XRegExp.escape( dateFormat( context.executionTime, format ) );
|
|
326
|
+
},
|
|
327
|
+
semver: `${semanticVersionRegex.xregexp.source || semanticVersionRegex.source}`,
|
|
328
|
+
};
|
|
329
|
+
const parsedVer = semver.parse( context.latestVersion );
|
|
330
|
+
if ( parsedVer ) {
|
|
331
|
+
Object.assign( placeholderMap, {
|
|
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}` ),
|
|
346
|
+
} );
|
|
347
|
+
}
|
|
348
|
+
if ( context.latestTag ) {
|
|
349
|
+
Object.assign( placeholderMap, { tag: XRegExp.escape( context.latestTag ) } );
|
|
350
|
+
}
|
|
200
351
|
|
|
201
|
-
if (
|
|
202
|
-
|
|
352
|
+
if ( context.version ) {
|
|
353
|
+
Object.assign( placeholderMap, { newVersion: XRegExp.escape( context.version ) } );
|
|
203
354
|
}
|
|
204
|
-
diffResult.hunks.forEach( hunk => {
|
|
205
|
-
this.log.exec( `Replacing at line ${hunk.oldStart}:\n` + hunk.lines.map( line => {
|
|
206
|
-
const lineText = '\t' + line;
|
|
207
|
-
if ( line.startsWith( '-' ) ) {
|
|
208
|
-
return chalk.red( lineText );
|
|
209
|
-
}
|
|
210
|
-
if ( line.startsWith( '+' ) ) {
|
|
211
|
-
return chalk.green( lineText );
|
|
212
|
-
}
|
|
213
|
-
return lineText;
|
|
214
|
-
} ).join( '\n' ), { isDryRun } );
|
|
215
|
-
} );
|
|
216
355
|
|
|
356
|
+
wrapSearchPatternPlaceholders( placeholderMap );
|
|
357
|
+
const replacedPattern = replacePlaceholders( pattern, placeholderMap );
|
|
358
|
+
return XRegExp( replacedPattern, searchRegEx.xregexp.flags || undefined );
|
|
217
359
|
}
|
|
218
360
|
|
|
219
|
-
function
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
365
|
+
const replacement = placeholderMap[ placeholder ];
|
|
366
|
+
if ( _.isFunction( replacement ) ) {
|
|
367
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
368
|
+
placeholderMap[ placeholder ] = ( ...args ) => {
|
|
369
|
+
return wrapInRegexGroup( replacement( ...args ) );
|
|
370
|
+
};
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
374
|
+
placeholderMap[ placeholder ] = wrapInRegexGroup( replacement );
|
|
231
375
|
}
|
|
232
376
|
}
|
|
233
377
|
|
|
234
|
-
function
|
|
235
|
-
|
|
378
|
+
function wrapInRegexGroup( pattern ) {
|
|
379
|
+
return `(?:${pattern})`;
|
|
236
380
|
}
|
|
237
381
|
|
|
238
382
|
function replaceVersion( content, searchRegex, replace, context ) {
|
|
239
|
-
const processedReplace = prepareReplacement
|
|
383
|
+
const processedReplace = prepareReplacement( replace, context );
|
|
240
384
|
const processedContent = XRegExp.replace( content, searchRegex, processedReplace );
|
|
241
385
|
return processedContent;
|
|
242
386
|
}
|
|
243
387
|
|
|
244
388
|
function prepareReplacement( replace, context ) {
|
|
245
|
-
const placeholderRegex = XRegExp( /\{\{(?<placeholder>(?:[a-z][a-z0-9_]*|\{))(?::(?<format>.*))?\}\}/ig );
|
|
246
|
-
const now = new Date();
|
|
247
389
|
const parsedVer = semver.parse( context.version );
|
|
248
390
|
const placeholderMap = {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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 ) ) {
|
|
406
|
+
return dateFormatIso( context.executionTime );
|
|
263
407
|
}
|
|
264
|
-
return dateFormat(
|
|
265
|
-
}
|
|
408
|
+
return dateFormat( context.executionTime, format );
|
|
409
|
+
},
|
|
266
410
|
};
|
|
267
|
-
return
|
|
268
|
-
|
|
269
|
-
|
|
411
|
+
return replacePlaceholders( replace, placeholderMap );
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function replacePlaceholders( template, placeholderMap ) {
|
|
415
|
+
placeholderMap = Object.assign( {}, placeholderMap, { '{': '{' } );
|
|
416
|
+
return XRegExp.replace( template, placeholderRegex, ( match, placeholder, format ) => {
|
|
417
|
+
if ( !hasOwnProperty( placeholderMap, placeholder ) ) {
|
|
418
|
+
throw new Error( `Unknown placeholder encountered: {{${placeholder}}}` );
|
|
419
|
+
}
|
|
420
|
+
// object injection mitigated by checking hasOwnProperty()
|
|
421
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
422
|
+
const replacement = placeholderMap[ placeholder ];
|
|
423
|
+
if ( _.isFunction( replacement ) ) {
|
|
270
424
|
if ( _.isString( format ) ) {
|
|
271
|
-
return
|
|
425
|
+
return replacement( format );
|
|
272
426
|
}
|
|
273
|
-
return
|
|
427
|
+
return replacement();
|
|
274
428
|
}
|
|
275
|
-
return
|
|
429
|
+
return replacement;
|
|
276
430
|
} );
|
|
277
431
|
}
|
|
278
432
|
|
|
279
|
-
|
|
280
433
|
function optionalRequire( packageName ) {
|
|
281
434
|
try {
|
|
435
|
+
// eslint-disable-next-line security/detect-non-literal-require
|
|
282
436
|
return require( packageName );
|
|
283
437
|
}
|
|
438
|
+
/* c8 ignore next 4 */
|
|
439
|
+
/* rewiremock can't simulate the absence of a package so we can't test this case */
|
|
284
440
|
catch ( ex ) {
|
|
285
441
|
return ex;
|
|
286
442
|
}
|
|
287
443
|
}
|
|
288
444
|
|
|
289
445
|
function firstNotNil( ...args ) {
|
|
290
|
-
return args.find( ele => !_.isNil( ele ) );
|
|
446
|
+
return args.find( ( ele ) => !_.isNil( ele ) );
|
|
291
447
|
}
|
|
292
448
|
|
|
293
449
|
class LineCounter {
|
|
294
450
|
constructor( text ) {
|
|
295
451
|
let index = 0;
|
|
296
|
-
this.lineEndIndex = text.split( '\n' ).map( line => {
|
|
452
|
+
this.lineEndIndex = text.split( '\n' ).map( ( line ) => {
|
|
297
453
|
index += line.length + 1;
|
|
298
454
|
return index;
|
|
299
455
|
} );
|
|
300
456
|
}
|
|
301
457
|
|
|
302
458
|
lineOfIndex( index ) {
|
|
303
|
-
|
|
459
|
+
// eslint-disable-next-line no-extra-parens
|
|
460
|
+
const arrayIndex = this.lineEndIndex.findIndex( ( lineEndIndex ) => index <= lineEndIndex );
|
|
304
461
|
assert( arrayIndex >= 0 );
|
|
305
462
|
return arrayIndex + 1;
|
|
306
463
|
}
|
|
307
464
|
|
|
308
465
|
columnOfIndex( index ) {
|
|
309
|
-
const
|
|
310
|
-
assert(
|
|
311
|
-
if (
|
|
466
|
+
const lineNumber = this.lineOfIndex( index );
|
|
467
|
+
assert( lineNumber >= 1 );
|
|
468
|
+
if ( lineNumber === 1 ) {
|
|
312
469
|
return index;
|
|
313
470
|
}
|
|
314
|
-
const
|
|
471
|
+
const previousLineNumber = lineNumber - 1;
|
|
472
|
+
const previousLineEndIndex = this.lineEndIndex[ previousLineNumber - 1 ];
|
|
315
473
|
return index - previousLineEndIndex;
|
|
316
474
|
}
|
|
317
475
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j-ulrich/release-it-regex-bumper",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Regular expression based version read/write plugin for release-it",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"index.js"
|
|
8
|
+
],
|
|
6
9
|
"scripts": {
|
|
7
10
|
"test": "npx bron test.js",
|
|
8
11
|
"test+coverage": "npx c8 npx bron test.js",
|
|
12
|
+
"lint": "npx eslint .",
|
|
9
13
|
"release": "npx release-it"
|
|
10
14
|
},
|
|
11
15
|
"keywords": [
|
|
@@ -48,12 +52,17 @@
|
|
|
48
52
|
"diff": "^3.0.0 || ^4.0.0"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
55
|
+
"@j-ulrich/eslint-config": "^1.0.0",
|
|
56
|
+
"@release-it/keep-a-changelog": "^2.5.0",
|
|
51
57
|
"bron": "^1.1.0",
|
|
52
58
|
"c8": "^7.3.0",
|
|
53
|
-
"
|
|
59
|
+
"eslint": "^8.6.0",
|
|
60
|
+
"eslint-plugin-security": "^1.4.0",
|
|
61
|
+
"mock-fs": "^5.1.1",
|
|
54
62
|
"release-it": "^14.0.0",
|
|
55
63
|
"rewiremock": "^3.14.3",
|
|
56
|
-
"sinon": "^9.0.3"
|
|
64
|
+
"sinon": "^9.0.3",
|
|
65
|
+
"temp": "^0.9.4"
|
|
57
66
|
},
|
|
58
67
|
"engines": {
|
|
59
68
|
"node": ">=10.12.0"
|
package/.github/workflows/CI.yml
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
-
|
|
4
|
-
name: CI
|
|
5
|
-
|
|
6
|
-
on: [ push, pull_request ]
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
compatibility-test-node:
|
|
10
|
-
name: Test Node Version Compatibility
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
|
|
13
|
-
strategy:
|
|
14
|
-
matrix:
|
|
15
|
-
node-version: [ 10.x, 12.x ]
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- uses: actions/checkout@v2
|
|
19
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
-
uses: actions/setup-node@v1
|
|
21
|
-
with:
|
|
22
|
-
node-version: ${{ matrix.node-version }}
|
|
23
|
-
- run: npm ci
|
|
24
|
-
- run: npm install release-it
|
|
25
|
-
- run: npm run build --if-present
|
|
26
|
-
- run: npm run test
|
|
27
|
-
|
|
28
|
-
compatibility-test-release:
|
|
29
|
-
name: Test Release-it Version Compatibility
|
|
30
|
-
runs-on: ubuntu-latest
|
|
31
|
-
|
|
32
|
-
strategy:
|
|
33
|
-
matrix:
|
|
34
|
-
release-it-version: [ 11.x, 12.x, 13.x, 14.x ]
|
|
35
|
-
|
|
36
|
-
steps:
|
|
37
|
-
- uses: actions/checkout@v2
|
|
38
|
-
- uses: actions/setup-node@v1
|
|
39
|
-
with:
|
|
40
|
-
node-version: 14.x
|
|
41
|
-
- run: npm ci
|
|
42
|
-
- run: npm install release-it@${{ matrix.release-it-version }}
|
|
43
|
-
- run: npm run build --if-present
|
|
44
|
-
- run: npm run test
|
|
45
|
-
|
|
46
|
-
coverage:
|
|
47
|
-
name: Coverage
|
|
48
|
-
runs-on: ubuntu-latest
|
|
49
|
-
|
|
50
|
-
steps:
|
|
51
|
-
- uses: actions/checkout@v2
|
|
52
|
-
- uses: actions/setup-node@v1
|
|
53
|
-
with:
|
|
54
|
-
node-version: 14.x
|
|
55
|
-
- run: npm ci
|
|
56
|
-
- run: npm install release-it
|
|
57
|
-
- run: npm run build --if-present
|
|
58
|
-
- run: npm run test+coverage
|
|
59
|
-
- run: npx c8 report --reporter lcov
|
|
60
|
-
- name: Upload coverage to Codacy
|
|
61
|
-
uses: codacy/codacy-coverage-reporter-action@v1
|
|
62
|
-
with:
|
|
63
|
-
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
|
64
|
-
coverage-reports: coverage/lcov.info
|