├── package.json ├── README.md └── upgradeSyntaxFrom2To3.js /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgrade-reason-syntax", 3 | "version": "1.0.5", 4 | "description": "", 5 | "main": "upgradeSyntaxFrom2To3.js", 6 | "bin": { 7 | "upgradeSyntaxFrom2To3": "upgradeSyntaxFrom2To3.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/reasonml/upgradeSyntaxFrom2To3.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/reasonml/upgradeSyntaxFrom2To3/issues" 18 | }, 19 | "homepage": "https://github.com/reasonml/upgradeSyntaxFrom2To3#readme" 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Convert your Reason project from Reason 2 to 3 2 | 3 | ## What is Reason 2/3? 4 | 5 | See the [announcement](https://reasonml.github.io/community/blog/#reason-3) 6 | and the [changelog guide](https://github.com/facebook/reason/blob/master/HISTORY.md#300). 7 | 8 | ## How do I upgrade my project? 9 | 10 | ### Before you start 11 | 12 | Converting your code to to the new syntax is done by running the 13 | `upgradeSyntaxFrom2To3` converter on your project's Reason code. 14 | Make sure that your project is syntactically valid in Reason 2. 15 | Invalid code will fail to convert. 16 | 17 | ### Convert your code 18 | 19 | Do this at the root of your project: 20 | 21 | ``` 22 | npm install -g upgrade-reason-syntax 23 | npm install --save-dev bs-platform@2.2.2 24 | upgradeSyntaxFrom2To3 mySource/* 25 | ``` 26 | 27 | **Make sure you did install bs-platform 2.2.2**. Sometimes your lockfile might have locked it to `1.x.x`. 2.2.2 is also the **last** version to support Reason syntax 2. 28 | 29 | The script accepts a list of files/globs to convert. Pass as many as you want. It'll intelligently skip over any file that's not Reason. 30 | 31 | After you're done converting your projects: 32 | 33 | - Remove the backup files at `mySource/*.backup` 34 | - Add `"refmt": 3` to your bsconfig.json to make BuckleScript use the new syntax. 35 | - Feel free to uninstall this library! 36 | 37 | **If you're on native**, this is also your workflow. (So you'll need node.js). 38 | 39 | ## How does it work? 40 | 41 | It's a simple node.js script that takes the old `refmt` and the new `refmt3` from your project's BuckleScript 2.0.0's source at `node_modules/bs-platform`, and then: 42 | 43 | - Iterates over all your relevant files, making a backup copy 44 | - Turns your Reason files into an AST (abstract syntax tree) using `refmt` 45 | - Turns the ASTs into the new syntax using `refmt3` and writes them back 46 | 47 | That's it! Enjoy =) 48 | -------------------------------------------------------------------------------- /upgradeSyntaxFrom2To3.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const childProcess = require('child_process'); 7 | 8 | if (process.argv.length <= 2) { 9 | console.error('You need to pass a list of the files you\'d like to upgrade, like so: `upgradeSyntaxFrom2To3 src/*`'); 10 | process.exit(1); 11 | } 12 | 13 | const cyan = '\u{1b}[36m'; 14 | const cyanEnd = '\u{1b}[39m'; 15 | const dim = '\u{1b}[2m'; 16 | const dimEnd = '\u{1b}[22m'; 17 | 18 | const binariesRoot = path.join('.', 'node_modules', 'bs-platform', 'lib'); 19 | const oldBinary = path.join(binariesRoot, 'refmt.exe'); 20 | const newBinary = path.join(binariesRoot, 'refmt3.exe'); 21 | 22 | const filesToMigrate = process.argv.slice(2); 23 | console.log(`${cyan}Starting Reason syntax 2 -> 3 migration!${cyanEnd} 24 | ${dim}Note that this is just a dumb script that takes all your files and do: 25 | 26 | ${oldBinary} --print binary_reason foo.re | ${newBinary} --parse binary_reason --print re${dimEnd} 27 | `) 28 | 29 | let filesThatAreBackups = []; 30 | let filesThatAreInvalid = []; 31 | let filesThatHaveBackups = []; 32 | const bsconfig = path.join('.', 'bsconfig.json'); 33 | 34 | const packageLock = path.join('.', 'package-lock.json'); 35 | const shrinkwrap = path.join('.', 'npm-shrinkwrap.json'); 36 | const yarnlock = path.join('.', 'yarn.lock'); 37 | 38 | function migrate(files) { 39 | let addRefmt3ToBsconfig; 40 | 41 | // check that bsconfig exists 42 | if (fs.existsSync(bsconfig)) { 43 | const bsconfigContent = fs.readFileSync(bsconfig, {encoding: 'utf-8'}); 44 | addRefmt3ToBsconfig = bsconfigContent.indexOf('refmt') === -1 45 | ? `${cyan}Please add \`"refmt": 3\` to your \`bsconfig.json\`${cyanEnd}` 46 | : `Add \`"refmt": 3\` to your \`bsconfig.json\`, then run the build again to verify that nothing errored.`; 47 | } else { 48 | console.log(`${cyan} 49 | We can't find a bsconfig.json file in this project${cyanEnd}; did you invoke the upgrade command at the right place?`); 50 | return; 51 | } 52 | 53 | files.forEach((file) => { 54 | const extension = path.extname(file); 55 | 56 | if (extension === '.backup') { 57 | filesThatAreBackups.push(file); 58 | return; 59 | } 60 | 61 | if (extension !== '.re' && extension !== '.rei') { 62 | // not reason file 63 | return; 64 | } 65 | 66 | if (fs.existsSync(file + '.backup')) { 67 | filesThatHaveBackups.push(file); 68 | return; 69 | } 70 | 71 | const finalPrintingFlags = extension === '.re' 72 | ? ['--parse', 'binary_reason', '--print', 're'] 73 | : ['--parse', 'binary_reason', '--print', 're', '--interface', 'true']; 74 | 75 | const backupName = file + '.backup'; 76 | 77 | const parsingFlags = extension === '.re' 78 | ? ['--parse', 're', '--print', 'binary_reason'] 79 | : ['--parse', 're', '--print', 'binary_reason', '--interface', 'true']; 80 | 81 | const isInterfaceFlag = extension === '.re' 82 | ? '' 83 | : '--interface true'; 84 | 85 | const fileContent = fs.readFileSync(file, {encoding: 'utf-8'}); 86 | // backup the file 87 | fs.writeFileSync(backupName, fileContent, {encoding: 'utf-8'}); 88 | let binaryAST; 89 | try { 90 | binaryAST = childProcess.execSync(`${oldBinary} --parse re --print binary_reason ${isInterfaceFlag} ${backupName}`); 91 | } catch(e) { 92 | filesThatAreInvalid.push(file); 93 | console.log(e.message); 94 | return; 95 | } 96 | 97 | const tempName = backupName + '.temp'; 98 | fs.writeFileSync(tempName, binaryAST, {encoding: 'utf-8'}); 99 | 100 | const finalContent = childProcess.execSync(`${newBinary} --parse binary_reason --print re ${isInterfaceFlag} ${tempName}`); 101 | fs.writeFileSync(file, finalContent, {encoding: 'utf-8'}); 102 | 103 | fs.unlinkSync(tempName); 104 | }) 105 | 106 | if (filesThatAreBackups.length !== 0) { 107 | console.log(`${cyan} 108 | The follow files are skipped, because they're backup files from a previous run of this script: 109 | ${cyanEnd}`); 110 | console.log(filesThatAreBackups.map(f => '- ' + f).join('\n')); 111 | } 112 | 113 | if (filesThatAreInvalid.length !== 0) { 114 | console.log(`${cyan} 115 | The follow files failed to transform (syntax error or missing refmt): 116 | ${cyanEnd}`); 117 | console.log(filesThatAreInvalid.map(f => '- ' + f).join('\n')); 118 | } 119 | 120 | if (filesThatHaveBackups.length !== 0) { 121 | console.log(`${cyan} 122 | The follow files are skipped, because they already have their backup (and are this presumed to be transformed already): 123 | ${cyanEnd}`); 124 | console.log(filesThatHaveBackups.map(f => '- ' + f).join('\n')); 125 | } 126 | 127 | const packageLockContent = fs.existsSync(packageLock) ? fs.readFileSync(packageLock, {encoding: 'utf-8'}) : ''; 128 | const shrinkwrapContent = fs.existsSync(shrinkwrap) ? fs.readFileSync(shrinkwrap, {encoding: 'utf-8'}) : ''; 129 | const yarnlockContent = fs.existsSync(yarnlock) ? fs.readFileSync(yarnlock, {encoding: 'utf-8'}) : ''; 130 | 131 | const hasLockedBsPlatform = 132 | packageLockContent.indexOf('bs-platform') >= 0 133 | || shrinkwrapContent.indexOf('bs-platform') >= 0 134 | || yarnlockContent.indexOf('bs-platform') >= 0; 135 | 136 | const unlockBsPlatform = hasLockedBsPlatform 137 | ? `\n- ${cyan}You seem to have locked bs-platform in your npm/yarn lockfile${cyanEnd}. Make sure you've actually upgraded bs-platform!` 138 | : ''; 139 | 140 | console.log(`${cyan} 141 | All done! IMPORTANT things to do after the codemod:${cyanEnd} 142 | 143 | - We've backed up your old code at \`yourFileName.backup\`. Feel free to examine & delete them. 144 | - ${addRefmt3ToBsconfig}${unlockBsPlatform} 145 | 146 | If there's any change to your build or artifacts, please check and file us an issue. Apologies in advance; it's a big refactoring. 147 | 148 | Thank you!`); 149 | } 150 | 151 | migrate(filesToMigrate); 152 | --------------------------------------------------------------------------------