Last active
October 7, 2024 15:35
-
-
Save pivstone/c633d86c7651a48677ed5dbc3675be4c to your computer and use it in GitHub Desktop.
yarn to pnpm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import json | |
import subprocess | |
import re | |
import os | |
import os.path | |
def match_name(text, name): | |
pattern = rf'({re.escape(name)})@(.*)(?!.*@)' | |
match = re.search(pattern, text) | |
if match: | |
version = match.group(2).endswith('"') and match.group(2)[:-1] or match.group(2) | |
return version | |
# find the actual version of a package | |
def yarn_why(name): | |
output = subprocess.run(["yarn", "why", name, '--json'], check=True, capture_output=True, encoding= 'utf-8') | |
for line in output.stdout.split("\n"): | |
try: | |
data = json.loads(line) | |
if data['type'] == 'info' and data['data'].startswith('\r=> Found '): | |
return match_name(data['data'], name) | |
except: | |
pass | |
def extract_package_names(file_path): | |
package_names = set() | |
with open(file_path, 'r', encoding='utf-8') as file: | |
for line in file: | |
# Match import statements | |
match = re.match(r'^\s*import\s+(?:\*\s+as\s+\w+|(\w+)|\{[^}]*\})\s+from\s+[\'"]([^\'"]+)[\'"]', line) | |
if match and not match.group(2).startswith('.'): | |
package_name = match.group(2) | |
package_names.add(package_name) | |
return package_names | |
def scan_src_folder(): | |
all_package_names = set() | |
for root, dirs, files in os.walk('src'): | |
for file in files: | |
if file.endswith('.ts') or file.endswith('.tsx'): | |
file_path = os.path.join(root, file) | |
package_names = extract_package_names(file_path) | |
all_package_names.update(package_names) | |
return all_package_names | |
def text_replace(content): | |
content = content.replace('@main', '@pnpm') | |
content = content.replace('yarn.lock', 'pnpm-lock.yaml') | |
content = content.replace('yarn deploy', 'pnpm run deploy') | |
content = content.replace('yarn global', 'pnpm -g') | |
content = content.replace('yarn test', 'pnpm run test') | |
content = content.replace('yarn run convert-cypress-to-allure', 'pnpm convert-cypress-to-allure') | |
content = content.replace('choco-scripts', 'pnpm choco-scripts') | |
content = content.replace('yarn', 'pnpm') | |
content = content.replace('Yarn', 'Pnpm') | |
return content | |
def update_file(file_path): | |
with open(file_path, 'rt') as file: | |
content = file.read() | |
content = text_replace(content) | |
# Write the file out again | |
with open(file_path, 'wt') as file: | |
file.write(content) | |
def update_github_workflow(): | |
print('====== Update Github Workflow=======') | |
for root, dirs, files in os.walk('.github'): | |
for file in files: | |
if file.endswith('.yml') or file.endswith('.yaml'): | |
file_path = os.path.join(root, file) | |
update_file(file_path) | |
def update_buildspec(): | |
print('======Update Buildspec Yml=======') | |
if not os.path.exists('buildspec.yml'): | |
print('Buildspec Not Found Skip') | |
else: | |
update_file('buildspec.yml') | |
def update_git_hook(): | |
print('======Update .lintstagedrc=======') | |
if not os.path.exists('.lintstagedrc'): | |
print('.lintstagedrc Not Found Skip') | |
else: | |
update_file('.lintstagedrc') | |
def update_readme(): | |
print('======Update readme=======') | |
if not os.path.exists('README.md'): | |
print('README.md Not Found Skip') | |
else: | |
update_file('README.md') | |
def add_common_missing_pkg(package): | |
pkgs = ['url-loader', '@svgr/webpack', 'graphql', 'react-router',] | |
for pkg in pkgs: | |
version = yarn_why(pkg) | |
if version and 'dependencies' in package: | |
print('Add pkg:' + pkg + ' to version:'+ version) | |
package['dependencies'][pkg] = version | |
return package | |
def add_common_missing_dev_pkg(package): | |
dev_pkgs = ['@graphql-codegen/cli', 'webpack-cli', '@babel/runtime', 'process', 'buffer', 'nyc', 'mochawesome', '@typescript-eslint/eslint-plugin', '@types/react-modal', '@types/lodash.debounce', '@types/history', '@types/testing-library__jest-dom', '@types/testing-library__react-hooks', 'webpack', 'crypto-browserify', 'stream-browserify', 'vm-browserify', '@types/lodash', '@types/react-router', ] | |
for pkg in dev_pkgs: | |
version = yarn_why(pkg) | |
if version and 'devDependencies' in package: | |
print('Add pkg:' + pkg + ' to version:'+ version) | |
package['devDependencies'][pkg] = version | |
return package | |
if __name__ == "__main__": | |
with open('package.json', 'rt') as f: | |
package = json.load(f) | |
all_package_names = set() | |
# update version | |
print('========Update Version=========') | |
for type in ['dependencies', 'devDependencies', 'peerDependencies', 'resolutions', ]: | |
if type in package: | |
for name in package[type]: | |
all_package_names.add(name) | |
version = yarn_why(name) | |
print('Update pkg:' + name + ' to version:'+ version) | |
package[type][name] = version | |
add_common_missing_dev_pkg(package) | |
add_common_missing_pkg(package) | |
print('=======Fix @typescript-eslint=====') | |
eslint_version = yarn_why('@typescript-eslint/eslint-plugin') | |
if eslint_version and 'resolutions' in package: | |
package['resolutions']['@typescript-eslint/eslint-plugin'] = eslint_version | |
package['resolutions']['@typescript-eslint/parser'] = eslint_version | |
print('==========Add @types/node=======') | |
if 'devDependencies' in package: | |
package['devDependencies']['@types/node'] = '16.18.55' | |
print('========Scan Code=======') | |
import_packages = scan_src_folder() | |
print('========Detect Phantom Dependencies=======') | |
for pkg_name in import_packages: | |
if pkg_name not in all_package_names: | |
version = yarn_why(pkg_name) | |
if version: | |
package['dependencies'][pkg_name] = version | |
print('Fix Phantom pkg:' + pkg_name) | |
else: | |
print('CANNOT fix Phantom pkg:' + pkg_name) | |
print('========Update Script=========') | |
for cmd in package['scripts']: | |
package['scripts'][cmd] = text_replace(package['scripts'][cmd]).replace(' webpack ', ' webpack-cli ') | |
# update misc | |
package['scripts']['preinstall'] = 'npx only-allow pnpm' | |
package['engines'] = { | |
"node": ">=16", | |
"pnpm": "8.8.0" | |
} | |
package["packageManager"] = "[email protected]" | |
with open('package.json', 'wt') as f: | |
json.dump(package, f, indent=2) | |
update_buildspec() | |
update_github_workflow() | |
update_git_hook() | |
update_readme() | |
print('========Creating pnpm lockfile=========') | |
subprocess.run(["pnpm", "import"], check=True) | |
print('========Pnpm install=========') | |
subprocess.run(["pnpm", "i"], check=True) | |
print('========Pnpm prettier=========') | |
subprocess.run(["pnpm", "--if-present", "prettier"], check=True) | |
print('========Remove Yarn Lockfile=========') | |
os.remove('yarn.lock') | |
print('========Pnpm build=========') | |
subprocess.run(["pnpm", "--if-present", "build"], check=True) | |
print('========Pnpm lint=========') | |
subprocess.run(["pnpm", "--if-present", "lint", ], check=True) | |
print('========Done=========') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment