UNPKG

@reason-native/cli

Version:

[![Build Status](https://dev.azure.com/reason-native/reason-native/_apis/build/status/facebookexperimental.reason-native?branchName=master)](https://dev.azure.com/reason-native/reason-native/_build/latest?definitionId=1?branchName=master) ![Supported Plat

298 lines (277 loc) 11 kB
/** * Copyright 2004-present Facebook. All Rights Reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ //this file was copied from https://github.com/facebook/reason/blob/master/scripts/esy-prepublish.js // // Usage: Run from the repo root: // // node scripts/esy-prepublish.js relative/path/to/some-package-name.json // The script will copy relative/path/to/some-package-name.json into // ./package.json and delete any remaining esy.json at the root. It will also // search for relative/path/to/some-package-name.README.md (or if that is not // found, then relative/path/to/README.md) and copy it to ./README.md at the // repo root so that the published package has its appropriate README on the // npm page. const fs = require('fs'); const cp = require('child_process'); const path = require('path'); const quote = s => '"' + s + '"'; const opamifyName = name => { if(name.indexOf("@opam/") === 0) { return '"' + name.substr(6) + '"'; } else if(name === '@esy-ocaml/reason') { return '"reason"'; } else { if(name.indexOf('@') === 0) { var scopeAndPackage = name.substr(1).split('/'); // return 'npm--' + scopeAndPackage[0] + '--' + scopeAndPackage[1]; // Assumes the packages have name.opam files, without the scope. return '"' + scopeAndPackage[1] + '"'; } else { return '"' + name + '"'; } } }; const opamifyVersion = v => { var v = v.trim(); if(v.charAt(0) === '^') { var postCaret = v.substr(1); var nextDotIndex = postCaret.indexOf('.'); if(nextDotIndex !== -1) { var major = postCaret.substr(0, nextDotIndex); var rest = postCaret.substr(nextDotIndex + 1); return '>= "' + postCaret + '" & < "' + (parseInt(major) + 1) + '.0.0"'; } else { var major = postCaret.substr(0, nextDotIndex); return '>= "' + postCaret + '" & < "' + (parseInt(postCaret) + 1) + '"'; } } else { return v.replace(/\s+<\s+/g, s => '" & < "') .replace(/\s+<=\s*/g, s => '" & <= "') .replace(/^<\s+/g, s => '< "') .replace(/^<=\s*/g, s => '<= "') .replace(/\s+>\s+/g, s => '" & > "') .replace(/\s+>=/g, s => '" & >= "') .replace(/^>\s+/g, s => '> "') .replace(/^>=\s*/g, s => '>= "') + '"'; } }; const depMap = (o) => { return Object.entries(o).map(([name, vers]) => opamifyName(name) + (vers === '*' ? '' : ' {' + opamifyVersion(vers) + '}') ) }; const createOpamText = pkg => { const opamTemplate = [ 'opam-version: ' + quote("2.0"), 'maintainer: ' + quote(pkg.author), 'authors: [' + quote(pkg.author) + ']', 'license: ' + quote(pkg.license), 'homepage: ' + quote(pkg.homepage), 'doc: ' + quote(pkg.homepage), pkg.repository && pkg.repository.url ? ('bug-reports: ' + quote(pkg.repository.url)) : '', pkg.repository && pkg.repository.url ? ('dev-repo: ' + quote(pkg.repository.url.replace('https', 'git').replace('http', 'git'))) : '', 'tags: [' + (pkg.keywords ? pkg.keywords.map(quote).join(' ') : '') + ']', 'build: [ [' + pkg.esy.build.split(' ').map(quote).join(' ') + ' ] ]', 'depends: [', ].concat(depMap(pkg.dependencies).map(s=>' ' + s)).concat([ ']', 'synopsis: ' + quote(pkg.description.charAt(0).toUpperCase() + pkg.description.substr(1)), 'description: ' + quote(pkg.description) ]); return opamTemplate.join('\n') + '\n'; }; if (process.cwd() !== path.resolve(__dirname, '..')) { console.log("ERROR: Must run `make esy-prepublish` from project root."); process.exit(1); } let projectRoot = process.cwd(); let relativeJsonPaths = []; for (var i = 2; i < process.argv.length; i++) { let jsonRelativePath = process.argv[i]; relativeJsonPaths.push(jsonRelativePath); } if (relativeJsonPaths.length === 0) { relativeJsonPaths = ['esy.json']; } for (var i = 0; i < relativeJsonPaths.length; i++) { let jsonRelativePath = relativeJsonPaths[i]; let subpackageJson = path.resolve(projectRoot, jsonRelativePath); if (path.extname(jsonRelativePath) !== '.json') { console.log( 'You specified an relative path to something that isn\'t a json file (' + subpackageJson + '). Specify location of json files relative to repo root.' ); process.exit(1); } if (!jsonRelativePath || !fs.existsSync(subpackageJson)) { console.log( 'You specified an invalid release package root (' + subpackageJson + '). Specify location of packages to release relative to repo root directory.' ); process.exit(1); } } const head = cp.spawnSync('git', ['rev-parse', '--verify', 'HEAD']).stdout.toString(); const master = cp.spawnSync('git', ['rev-parse', '--verify', 'master']).stdout.toString(); // Since we generate opam files, don't check for uncommitted. // let uncommitted = // cp.spawnSync('git', ['diff-index', 'HEAD', '--']).stdout.toString(); // if (uncommitted !== "") { // console.log('ERROR: You have uncommitted changes. Please try on a clean master branch'); // process.exit(1); // } process.chdir(projectRoot); let tarResult = cp.spawnSync( 'tar', [ '--exclude', '_esy', '--exclude', 'node_modules', '--exclude', '_build', '--exclude', '.git', '-cf', 'template.tar', '.' ] ); let tarErr = tarResult.stderr.toString(); // if (tarErr !== '') { // console.log('ERROR: Could not create template npm pack for prepublish'); // throw new Error('Error:' + tarErr); // } try { let _releaseDir = path.resolve(projectRoot, '_release'); // For each subpackage, we release the entire source code for all packages, but // with the root package.json swapped out with the esy.json file in the // subpackage. for (var i = 0; i < relativeJsonPaths.length; i++) { process.chdir(projectRoot); let jsonRelativePath = relativeJsonPaths[i]; let jsonResolvedPath = path.resolve(projectRoot, jsonRelativePath); const packageJson = require(jsonResolvedPath); const packageName = packageJson.name; const packageVersion = packageJson.version; console.log(''); console.log('Preparing: ' + jsonRelativePath + ' ' + packageName + '@' + packageVersion); console.log('-----------------------------------------------------------------------------'); let subpackageReleaseDir = path.resolve(_releaseDir, jsonRelativePath); if (fs.existsSync(subpackageReleaseDir)) { console.log('YOU NEED TO REMOVE THE ' + subpackageReleaseDir + ' DIR FIRST!'); process.exit(1); } if (!fs.existsSync(_releaseDir)) { fs.mkdirSync(_releaseDir); } fs.mkdirSync(subpackageReleaseDir); let subpackageReleasePrepDir = path.resolve(_releaseDir, path.join(jsonRelativePath), '_prep'); fs.mkdirSync(subpackageReleasePrepDir); fs.copyFileSync( path.join(projectRoot, 'template.tar'), path.join(subpackageReleasePrepDir, 'template.tar') ); process.chdir(subpackageReleasePrepDir); cp.spawnSync('tar', ['-xvf', 'template.tar']); fs.unlinkSync(path.join(subpackageReleasePrepDir, 'template.tar')); let readmePath = path.resolve(subpackageReleasePrepDir, 'README.md'); let readmePkgPath = path.resolve( subpackageReleasePrepDir, path.join('src', path.basename(jsonRelativePath, '.json'), 'README.md') ); let readmeResolvedPath = fs.existsSync(readmePkgPath) ? readmePkgPath : fs.existsSync(readmePath) ? readmePath : null; let toCopy = [ { originPath: path.resolve(subpackageReleasePrepDir, jsonRelativePath), destPath: path.resolve(subpackageReleasePrepDir, 'package.json') }, { originPath: readmeResolvedPath, destPath: path.resolve(subpackageReleasePrepDir, 'README.md') } ]; for (var j = 0; j < toCopy.length; j++) { let originPath = toCopy[j].originPath; let destPath = toCopy[j].destPath; if (originPath !== null && fs.existsSync(originPath) && destPath !== originPath) { fs.renameSync(originPath, destPath); } } // If an esy.json file remains, we need to remove it so that it isn't // picked up as the default by esy (it gives priority to esy.json over // package.json). But this has to be done _after_ the `mv` above, in case // the json file that someone published _was_ the esy.json file. let esyFile = path.resolve(subpackageReleasePrepDir, 'esy.json'); if (fs.existsSync(esyFile)) { fs.unlinkSync(esyFile); } // Create a npm pack to remove all the stuff in .npmignore. This would // happen when you publish too, but we'll create a directory ./package that // has all of it removed so you can also easily test linking against it // from other projects. process.chdir(subpackageReleasePrepDir); // Npm pack is just a convenient way to strip out any unnecessary files. const packResult = cp.spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['pack']); if (packResult.status !== 0) { console.log('ERROR: Could not create npm pack for ' + subpackageReleasePrepDir); throw new Error('Error:' + packResult.stderr.toString()); } const mvTo = subpackageReleaseDir; fs.readdirSync(subpackageReleasePrepDir).filter(fn => fn.endsWith('.tgz')).forEach(fn => { fs.renameSync(fn, path.join(mvTo, fn)); }); process.chdir(mvTo); const tarResult = cp.spawnSync('tar', ['-xvf', '*.tgz'], { shell: true }); if (tarResult.error) { console.log('ERROR: Could not untar in ' + mvTo); throw new Error('Error:' + tarResult.stderr.toString()); } console.log('Prepared for publishing at: '); console.log(' ' + subpackageReleaseDir); if(packageJson['esy-prepublish-generate-opam']) { try { const opamText = createOpamText(packageJson); const opamFileName = path.basename(jsonRelativePath, '.json') + '.opam'; let opamResolvedPath = path.resolve(projectRoot, opamFileName); fs.writeFileSync(opamResolvedPath, opamText); console.log("Opam file generated. Commit it. Or don't:"); console.log(' ' + opamResolvedPath); } catch(e) { console.log("Could not generate opam file. See error below."); console.log( "To disable opam file generation, remove `\"esy-prepublish-generate-opam\": true` from " + jsonRelativePath ); console.log(' ' + e.toString()); } } else { console.log("To generate opam file, add `\"esy-prepublish-generate-opam\": true` to " + jsonRelativePath); } console.log('To publish the package to npm do:'); console.log(' cd ' + path.resolve(subpackageReleaseDir, 'package')); console.log(' npm publish --access=public'); console.log(''); } } finally { fs.unlinkSync(path.join(projectRoot, 'template.tar')); }