forked from antfu-collective/bumpp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathupdate-files.ts
105 lines (87 loc) · 3.05 KB
/
update-files.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import * as path from 'node:path'
import { readJsonFile, readTextFile, writeJsonFile, writeTextFile } from './fs'
import { isManifest } from './manifest'
import type { Operation } from './operation'
import { ProgressEvent } from './types/version-bump-progress'
/**
* Updates the version number in the specified files.
*/
export async function updateFiles(operation: Operation): Promise<Operation> {
const { files } = operation.options
for (const relPath of files) {
const modified = await updateFile(relPath, operation)
if (modified) {
operation.update({
event: ProgressEvent.FileUpdated,
updatedFiles: operation.state.updatedFiles.concat(relPath),
})
}
else {
operation.update({
event: ProgressEvent.FileSkipped,
skippedFiles: operation.state.skippedFiles.concat(relPath),
})
}
}
return operation
}
/**
* Updates the version number in the specified file.
*
* @returns - `true` if the file was actually modified
*/
async function updateFile(relPath: string, operation: Operation): Promise<boolean> {
const name = path.basename(relPath).trim().toLowerCase()
switch (name) {
case 'package.json':
case 'package-lock.json':
case 'bower.json':
case 'component.json':
return updateManifestFile(relPath, operation)
default:
return updateTextFile(relPath, operation)
}
}
/**
* Updates the version number in the specified JSON manifest file.
*
* NOTE: Unlike text files, this is NOT a global find-and-replace. It _specifically_ sets
* the top-level `version` property.
*
* @returns - `true` if the file was actually modified
*/
async function updateManifestFile(relPath: string, operation: Operation): Promise<boolean> {
const { cwd } = operation.options
const { newVersion } = operation.state
let modified = false
const file = await readJsonFile(relPath, cwd)
if (isManifest(file.data) && file.data.version !== newVersion) {
file.data.version = newVersion
await writeJsonFile(file)
modified = true
}
return modified
}
/**
* Updates all occurrences of the version number in the specified text file.
*
* @returns - `true` if the file was actually modified
*/
async function updateTextFile(relPath: string, operation: Operation): Promise<boolean> {
const { cwd } = operation.options
const { oldVersion, newVersion } = operation.state
const modified = false
const file = await readTextFile(relPath, cwd)
// Only update the file if it contains at least one occurrence of the old version
if (file.data.includes(oldVersion)) {
// Escape all non-alphanumeric characters in the version
const sanitizedVersion = oldVersion.replace(/(\W)/g, '\\$1')
// Replace occurrences of the old version number that are surrounded by word boundaries.
// This ensures that it matches "1.23.456" or "v1.23.456", but not "321.23.456".
const replacePattern = new RegExp(`(\\b|v)${sanitizedVersion}\\b`, 'g')
file.data = file.data.replace(replacePattern, `$1${newVersion}`)
await writeTextFile(file)
return true
}
return modified
}