forked from microsoft/fluentui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrename-package.ts
268 lines (219 loc) · 9.71 KB
/
rename-package.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
import { spawnSync } from 'child_process';
import * as fs from 'fs-extra';
import { IOptions as GlobOptions, sync as globSync } from 'glob';
import _ from 'lodash';
import * as path from 'path';
import { sync as replaceInFileSync, ReplaceResult } from 'replace-in-file';
import { findGitRoot, PackageInfo, listAllTrackedFiles, stageAndCommit } from 'workspace-tools';
const readConfig: (pth: string) => PackageInfo = require('./read-config').readConfig;
const writeConfig: (pth: string, newValue: any) => void = require('./write-config');
const { runPrettier } = require('./prettier');
const gitRoot = findGitRoot(process.cwd());
/**
* Things that can come before a package name/path: string start, `/`, `!` (loader path),
* space, start of line
*/
const nameStartLookbehind = '(?<=[\'"`/! ]|^)';
/**
* Things that can come after a package name: string end, `/`, space, `@` (certain URLs),
* end of line
*/
const nameEndLookahead = '(?=[\'"`/ @]|$)';
const getPackageNameParts = (packageName: string): [string | undefined, string] =>
packageName[0] === '@' ? (packageName.split('/') as [string, string]) : [, packageName];
const getPackagePath = (unscopedPackageName: string) => {
const packagesPath = path.join(gitRoot, 'packages', unscopedPackageName);
return fs.existsSync(packagesPath) ? packagesPath : path.join(gitRoot, 'apps', unscopedPackageName);
};
const getPackageJson = (unscopedPackageName: string) =>
readConfig(path.join(getPackagePath(unscopedPackageName), 'package.json'));
const getChangedFiles = (results: ReplaceResult[]) => results.filter(res => res.hasChanged).map(res => res.file);
interface RenameInfo {
oldScope: string;
/** Old unscoped name */
oldUnscopedName: string;
newScope: string;
/** New unscoped name */
newUnscopedName: string;
newVersion: string;
packageJson: PackageInfo;
}
function getPackageToRename(): RenameInfo {
const [oldNameArg, newNameArg, versionArg] = process.argv.slice(2);
if (oldNameArg) {
const [oldScope = '@uifabric', oldUnscopedName] = getPackageNameParts(oldNameArg);
const [newScope = '@fluentui', newUnscopedName] = getPackageNameParts(newNameArg || oldUnscopedName);
let packageJson = getPackageJson(oldUnscopedName);
return {
oldUnscopedName,
oldScope,
newUnscopedName,
newScope,
packageJson,
newVersion: versionArg || packageJson.version,
};
} else {
console.log('Usage:\n yarn rename-package <old-name> [<new-name> [<new-version>]]');
process.exit(1);
}
}
function updatePackage(renameInfo: RenameInfo): string[] {
const { oldUnscopedName, oldScope, newUnscopedName, newScope, packageJson, newVersion } = renameInfo;
const oldPath = getPackagePath(oldUnscopedName);
// Replace just the last section so it lands under the correct one of /apps or /packages
const newPath = oldPath.replace(new RegExp(`${oldUnscopedName}$`), newUnscopedName);
const newPackageJsonPath = path.join(newPath, 'package.json');
if (oldPath !== newPath) {
console.log(`\nMoving package from ${oldPath} to ${newPath}`);
fs.renameSync(oldPath, newPath);
const oldExamplesPath = path.join(getPackagePath('react-examples'), 'src', oldUnscopedName);
let newExamplesPath: string | undefined;
if (fs.existsSync(oldExamplesPath)) {
newExamplesPath = path.join(getPackagePath('react-examples'), 'src', newUnscopedName);
console.log(`\nMoving examples from ${oldPath} to ${newPath}`);
fs.renameSync(oldExamplesPath, newExamplesPath);
}
const apiFilePath = path.join(newPath, 'etc', `${oldUnscopedName}.api.md`);
if (fs.existsSync(apiFilePath)) {
fs.renameSync(apiFilePath, apiFilePath.replace(path.basename(apiFilePath), `${newUnscopedName}.api.md`));
}
console.log('\nCommitting the file moves only');
stageAndCommit(
[oldPath, newPath, ...(newExamplesPath ? [oldExamplesPath, newExamplesPath] : [])],
`Rename ${oldScope}/${oldUnscopedName} to ${newScope}/${newUnscopedName}`,
gitRoot,
);
} else {
console.log(`\nPackage does not need to be moved from ${oldPath}`);
}
console.log('\nUpdating name and version in package.json');
packageJson.name = `${newScope}/${newUnscopedName}`;
packageJson.version = newVersion;
writeConfig(newPackageJsonPath, packageJson);
return [newPackageJsonPath];
}
function updateDependents(renameInfo: RenameInfo): string[] {
const { oldUnscopedName, oldScope, newUnscopedName, newScope, newVersion } = renameInfo;
console.log('\nUpdating name and version in other package.json files');
const glob: GlobOptions = {
cwd: gitRoot,
};
const depResults = replaceInFileSync({
files: '{apps,packages,packages/fluentui}/*/package.json',
from: new RegExp(`"${oldScope}/${oldUnscopedName}": "([~^<>= ]*)\\d+\\.\\d+\\.\\d+(-.*)?"`),
to: `"${newScope}/${newUnscopedName}": "$1${newVersion}"`,
glob,
});
const changedPackageJson = getChangedFiles(depResults);
console.log(` ${changedPackageJson.join('\n ')}`);
return changedPackageJson;
}
function updateReferences(renameInfo: RenameInfo): string[] {
console.log('\nReplacing old package name and path in all tracked files (this will take awhile)...');
const files = listAllTrackedFiles([], gitRoot).filter(f => !/CHANGELOG/.test(f));
const { oldUnscopedName, oldScope, newUnscopedName, newScope } = renameInfo;
// Replace name references (@uifabric/utilities) AND path references (packages/utilities).
// To prevent replacing other package names which share substrings, use a fancy regex.
const nameRegex = new RegExp(
`${nameStartLookbehind}(${oldScope}|apps|packages|react-examples/(src|lib))/${oldUnscopedName}${nameEndLookahead}`,
);
let lastUpdatedFile = '';
const results = replaceInFileSync({
files,
from: new RegExp(nameRegex.source, 'gm'),
to: (substr, ...args) => {
const file = args.slice(-1)[0];
if (lastUpdatedFile !== file) {
console.log(` updating ${file}`);
lastUpdatedFile = file;
}
const match = nameRegex.exec(substr);
// This is the scope or the packages or apps section of the path
const firstPart = match[1] === oldScope ? newScope : match[1];
return `${firstPart}/${newUnscopedName}`;
},
});
return getChangedFiles(results);
}
function updateConfigs(renameInfo: RenameInfo): string[] {
console.log('\nUpdating config files...');
const { oldUnscopedName, newUnscopedName } = renameInfo;
// Rename API file if it exists
const oldApiFile = path.join(getPackagePath(newUnscopedName), 'dist', oldUnscopedName + '.api.md');
if (fs.existsSync(oldApiFile)) {
fs.renameSync(oldApiFile, path.join(getPackagePath(newUnscopedName), 'dist', newUnscopedName + '.api.md'));
}
const results: ReplaceResult[] = [
// PR deploy site
...replaceInFileSync({
files: path.join(gitRoot, 'apps/pr-deploy-site/pr-deploy-site.js'),
from: new RegExp(`\\./${oldUnscopedName}/`, 'g'),
to: `./${newUnscopedName}/`,
}),
// API docs config
...replaceInFileSync({
files: path.join(gitRoot, 'packages/api-docs/config/api-docs.js'),
from: `../../${oldUnscopedName}/dist/${oldUnscopedName}`,
to: `../../${newUnscopedName}/dist/${newUnscopedName}`,
}),
];
const newPackagePath = getPackagePath(newUnscopedName);
const bundleFiles = globSync(path.join(newPackagePath, 'webpack*.js'));
if (bundleFiles.length) {
// Assorted special files which are known to reference bundle names
bundleFiles.push(
path.join(gitRoot, 'packages/react-docsite-components/src/components/CodepenComponent/CodepenComponent.tsx'),
path.join(gitRoot, 'packages/react-monaco-editor/src/transpiler/transpileHelpers.test.ts'),
path.join(gitRoot, 'packages/react-monaco-editor/src/utilities/defaultSupportedPackages.ts'),
path.join(gitRoot, 'packages/react-monaco-editor/src/transpiler/__snapshots__/exampleTransform.test.ts.snap'),
);
// Replace the bundle name and the library name in any webpack configs
// (these names aren't the same in all packages)
const oldBundleNameMaybe = 'Fabric' + _.upperFirst(_.camelCase(oldUnscopedName));
const newBundleName = 'FluentUI' + _.upperFirst(_.camelCase(newUnscopedName));
results.push(
...replaceInFileSync({
files: bundleFiles,
from: new RegExp(`${oldBundleNameMaybe}|${oldUnscopedName}`, 'g'),
to: substr => {
return substr === oldBundleNameMaybe ? newBundleName : newUnscopedName;
},
}),
);
}
return getChangedFiles(results);
}
async function runPrettierForFiles(modifiedFiles: string[]) {
console.log('\nRunning prettier on changed files...');
await runPrettier(modifiedFiles, { runAsync: true, logErrorsOnly: true });
}
function runYarn() {
console.log('\nRunning `yarn` to update links...');
const yarnResult = spawnSync('yarn', ['--ignore-scripts'], { cwd: gitRoot, stdio: 'inherit', shell: true });
if (yarnResult.status !== 0) {
console.error('Something went wrong with running yarn. Please check previous logs for details');
process.exit(1);
}
}
async function run() {
const renameInfo = getPackageToRename();
const modifiedFiles = [
...updatePackage(renameInfo),
...updateDependents(renameInfo),
...updateReferences(renameInfo),
...updateConfigs(renameInfo),
];
await runPrettierForFiles(modifiedFiles);
runYarn();
console.log(`
Almost done!
PLEASE VERIFY ALL THE CHANGES ARE CORRECT! (Easy way to view them all: \`git diff -U1\`)
Other follow-up steps:
- Run a search for the old scoped and unscoped package names in case of non-standard references.
This regex might help: ${nameStartLookbehind}${renameInfo.oldUnscopedName}${nameEndLookahead}
- You may need to run a build to ensure API files are properly updated
`);
}
run().catch(err => {
console.error(err);
});