├── .github ├── CODEOWNERS └── dco.yml ├── .gitignore ├── CONTRIBUTING.txt ├── DCO.txt ├── LICENSE ├── README.md ├── createFitbitApp.js ├── index.js ├── package.json └── yarn.lock /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Fitbit/developer-tools 2 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | require: 2 | members: false 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage/ 2 | /lib-cov/ 3 | /node_modules/ 4 | 5 | # OS metadata 6 | .DS_Store 7 | Thumbs.db 8 | 9 | yarn-error.log 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.txt: -------------------------------------------------------------------------------- 1 | The current version of our contributing guide can be found at https://dev.fitbit.com/community/contributing/. 2 | -------------------------------------------------------------------------------- /DCO.txt: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2018, Facebook, Inc. 4 | Copyright (c) 2018-present, Fitbit, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | @fitbit/create-fitbit-app 2 | ========================== 3 | 4 | Adapted directly from Facebook's [create-react-app](https://github.com/facebook/create-react-app) 5 | 6 | Creates a basic Fitbit OS app or clockface 7 | 8 | ### Usage 9 | 10 | `npx create-fitbit-app ` 11 | 12 | #### Options 13 | 14 | `--sdk-version ` Use a non-standard version of @fitbit/sdk 15 | -------------------------------------------------------------------------------- /createFitbitApp.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-2018, Facebook, Inc. 3 | * Copyright (c) 2018-present, Fitbit, Inc. 4 | * 5 | * This source code is licensed under the MIT license found in the 6 | * LICENSE file in the root directory of this source tree. 7 | */ 8 | 9 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 | // /!\ DO NOT MODIFY THIS FILE /!\ 11 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | // 13 | // create-fitbit-app is installed globally on people's computers. This means 14 | // that it is extremely difficult to have them upgrade the version and 15 | // because there's only one global version installed, it is very prone to 16 | // breaking changes. 17 | // 18 | // The only job of create-fitbit-app is to init the repository and then 19 | // forward all the commands to the local version of create-fitbit-app. 20 | // 21 | // If you need to add a new command, please add it to the scripts/ folder. 22 | // 23 | // The only reason to modify this file is to add more warnings and 24 | // troubleshooting information for the `create-fitbit-app` command. 25 | // 26 | // Do not make breaking changes! We absolutely don't want to have to 27 | // tell people to update their global version of create-fitbit-app. 28 | // 29 | // Also be careful with new language features. 30 | // This file must work on Node 8+. 31 | // 32 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | // /!\ DO NOT MODIFY THIS FILE /!\ 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | 'use strict'; 37 | 38 | const validateProjectName = require('validate-npm-package-name'); 39 | const chalk = require('chalk'); 40 | const commander = require('commander'); 41 | const fs = require('fs-extra'); 42 | const path = require('path'); 43 | const execSync = require('child_process').execSync; 44 | const spawn = require('cross-spawn'); 45 | const semver = require('semver'); 46 | const dns = require('dns'); 47 | const tmp = require('tmp'); 48 | const unpack = require('tar-pack').unpack; 49 | const url = require('url'); 50 | const hyperquest = require('hyperquest'); 51 | const envinfo = require('envinfo'); 52 | const os = require('os'); 53 | const packageJson = require('./package.json'); 54 | 55 | const sdkPackageName = '@fitbit/sdk'; 56 | const sdkToolsPackageName = '@fitbit/sdk-cli'; 57 | const sdkPackageDependencies = [sdkPackageName, sdkToolsPackageName]; 58 | 59 | const minNpmVersion = '5.0.0'; 60 | 61 | // These files should be allowed to remain on a failed install, 62 | // but then silently removed during the next create. 63 | const errorLogFilePatterns = [ 64 | 'npm-debug.log', 65 | 'yarn-error.log', 66 | 'yarn-debug.log', 67 | ]; 68 | 69 | let projectName; 70 | 71 | const program = new commander.Command(packageJson.name) 72 | .version(packageJson.version) 73 | .arguments('') 74 | .usage(`${chalk.green('')} [options]`) 75 | .action(name => { 76 | projectName = name; 77 | }) 78 | .option('--verbose', 'print additional logs') 79 | .option('--info', 'print environment debug info') 80 | .option( 81 | '--sdk-version ', 82 | 'use a non-standard version of ' + sdkPackageName, 83 | ) 84 | .option('--use-npm') 85 | .allowUnknownOption() 86 | .on('--help', () => { 87 | console.log(` Only ${chalk.green('')} is required.`); 88 | console.log(); 89 | console.log( 90 | ` A custom ${chalk.cyan('--sdk-version')} can be one of:` 91 | ); 92 | console.log(` - a specific npm version: ${chalk.green('0.8.2')}`); 93 | console.log(` - a specific npm tag: ${chalk.green('@next')}`); 94 | console.log( 95 | ` - a custom fork published on npm: ${chalk.green( 96 | 'my-fitbit-sdk' 97 | )}` 98 | ); 99 | console.log( 100 | ` - a local path relative to the current working directory: ${chalk.green( 101 | 'file:../my-fitbit-sdk' 102 | )}` 103 | ); 104 | console.log( 105 | ` - a .tgz archive: ${chalk.green( 106 | 'https://mysite.com/my-fitbit-sdk-0.8.2.tgz' 107 | )}` 108 | ); 109 | console.log( 110 | ` - a .tar.gz archive: ${chalk.green( 111 | 'https://mysite.com/my-fitbit-sdk-0.8.2.tar.gz' 112 | )}` 113 | ); 114 | console.log( 115 | ` It is not needed unless you specifically want to use a fork.` 116 | ); 117 | console.log(); 118 | }) 119 | .parse(process.argv); 120 | 121 | if (program.info) { 122 | console.log(chalk.bold('\nEnvironment Info:')); 123 | return envinfo 124 | .run( 125 | { 126 | System: ['OS', 'CPU'], 127 | Binaries: ['Node', 'npm', 'Yarn'], 128 | Browsers: ['Chrome', 'Edge', 'Internet Explorer', 'Firefox', 'Safari'], 129 | npmPackages: [sdkPackageName, sdkToolsPackageName], 130 | npmGlobalPackages: ['create-fitbit-app'], 131 | }, 132 | { 133 | duplicates: true, 134 | showNotFound: true, 135 | } 136 | ) 137 | .then(console.log); 138 | } 139 | 140 | if (typeof projectName === 'undefined') { 141 | console.error('Please specify the project directory:'); 142 | console.log( 143 | ` ${chalk.cyan(program.name())} ${chalk.green('')}` 144 | ); 145 | console.log(); 146 | console.log('For example:'); 147 | console.log(` ${chalk.cyan(program.name())} ${chalk.green('my-fitbit-app')}`); 148 | console.log(); 149 | console.log( 150 | `Run ${chalk.cyan(`${program.name()} --help`)} to see all options.` 151 | ); 152 | process.exit(1); 153 | } 154 | 155 | function printValidationResults(results) { 156 | if (typeof results !== 'undefined') { 157 | results.forEach(error => { 158 | console.error(chalk.red(` * ${error}`)); 159 | }); 160 | } 161 | } 162 | 163 | createApp( 164 | projectName, 165 | program.verbose, 166 | program.sdkVersion, 167 | program.useNpm, 168 | ); 169 | 170 | function createApp(name, verbose, version, useNpm) { 171 | const root = path.resolve(name); 172 | const appName = path.basename(root); 173 | 174 | checkAppName(appName); 175 | fs.ensureDirSync(name); 176 | if (!isSafeToCreateProjectIn(root, name)) { 177 | process.exit(1); 178 | } 179 | 180 | console.log(`Creating a new Fitbit SDK app in ${chalk.green(root)}.`); 181 | console.log(); 182 | 183 | const packageJson = { 184 | name: appName, 185 | version: '0.1.0', 186 | private: true, 187 | license: 'UNLICENSED', 188 | }; 189 | fs.writeFileSync( 190 | path.join(root, 'package.json'), 191 | JSON.stringify(packageJson, null, 2) + os.EOL 192 | ); 193 | 194 | const useYarn = useNpm ? false : isYarnAvailable(root); 195 | const originalDirectory = process.cwd(); 196 | process.chdir(root); 197 | if (!useYarn && !checkThatNpmCanReadCwd()) { 198 | process.exit(1); 199 | } 200 | 201 | if (!useYarn) { 202 | const npmInfo = checkNpmVersion(); 203 | if (!npmInfo.hasMinNpm) { 204 | console.error( 205 | chalk.red( 206 | 'Fitbit SDK requires npm ' + minNpmVersion + ' or higher. \n' + 207 | 'Please update your version of npm.' 208 | ), 209 | ); 210 | process.exit(1); 211 | } 212 | } 213 | 214 | run(root, appName, version, verbose, originalDirectory, useYarn); 215 | } 216 | 217 | function isYarnAvailable() { 218 | try { 219 | execSync('yarnpkg --version', { stdio: 'ignore' }); 220 | return true; 221 | } catch (e) { 222 | return false; 223 | } 224 | } 225 | 226 | function install(root, useYarn, dependencies, verbose, isOnline) { 227 | return new Promise((resolve, reject) => { 228 | let command; 229 | let args; 230 | if (useYarn) { 231 | command = 'yarnpkg'; 232 | args = ['add', '--dev', '--exact']; 233 | if (!isOnline) { 234 | args.push('--offline'); 235 | } 236 | [].push.apply(args, dependencies); 237 | 238 | // Explicitly set cwd() to work around issues like 239 | // https://github.com/facebook/create-react-app/issues/3326. 240 | // Unfortunately we can only do this for Yarn because npm support for 241 | // equivalent --prefix flag doesn't help with this issue. 242 | // This is why for npm, we run checkThatNpmCanReadCwd() early instead. 243 | args.push('--cwd'); 244 | args.push(root); 245 | 246 | if (!isOnline) { 247 | console.log(chalk.yellow('You appear to be offline.')); 248 | console.log(chalk.yellow('Falling back to the local Yarn cache.')); 249 | console.log(); 250 | } 251 | } else { 252 | command = 'npm'; 253 | args = [ 254 | 'install', 255 | '--save-dev', 256 | '--save-exact', 257 | '--loglevel', 258 | 'error', 259 | ].concat(dependencies); 260 | } 261 | 262 | if (verbose) { 263 | args.push('--verbose'); 264 | } 265 | 266 | const child = spawn(command, args, { stdio: 'inherit' }); 267 | child.on('close', code => { 268 | if (code !== 0) { 269 | reject({ 270 | command: `${command} ${args.join(' ')}`, 271 | }); 272 | return; 273 | } 274 | resolve(); 275 | }); 276 | }); 277 | } 278 | 279 | function run( 280 | root, 281 | appName, 282 | version, 283 | verbose, 284 | originalDirectory, 285 | useYarn 286 | ) { 287 | const packageToInstall = getInstallPackage(version, originalDirectory); 288 | const allDependencies = [sdkToolsPackageName, packageToInstall]; 289 | 290 | console.log('Installing packages. This might take a couple of minutes.'); 291 | getPackageName(packageToInstall) 292 | .then(packageName => 293 | checkIfOnline(useYarn).then(isOnline => ({ 294 | isOnline: isOnline, 295 | packageName: packageName, 296 | })) 297 | ) 298 | .then(info => { 299 | const isOnline = info.isOnline; 300 | const packageName = info.packageName; 301 | console.log( 302 | `Installing ${chalk.cyan(packageName)} and ${chalk.cyan(sdkToolsPackageName)}...` 303 | ); 304 | console.log(); 305 | 306 | return install(root, useYarn, allDependencies, verbose, isOnline).then( 307 | () => packageName 308 | ); 309 | }) 310 | .then(packageName => { 311 | checkNodeVersion(packageName); 312 | setRangePrefixForDep(packageName, '~'); 313 | setRangePrefixForDep(sdkToolsPackageName, '^'); 314 | 315 | const scriptsPath = path.resolve( 316 | process.cwd(), 317 | 'node_modules', 318 | packageName, 319 | 'lib', 320 | 'newProject.js' 321 | ); 322 | const init = require(scriptsPath).default; 323 | return init(root, appName, verbose, originalDirectory); 324 | }) 325 | .catch(reason => { 326 | console.log(); 327 | console.log('Aborting installation.'); 328 | if (reason.command) { 329 | console.log(` ${chalk.cyan(reason.command)} has failed.`); 330 | } else { 331 | console.log(chalk.red('Unexpected error. Please report it as a bug:')); 332 | console.log(reason); 333 | } 334 | console.log(); 335 | 336 | // On 'exit' we will delete these files from target directory. 337 | const knownGeneratedFiles = ['package.json', 'node_modules', 'package-lock.json', 'yarn.lock']; 338 | const currentFiles = fs.readdirSync(path.join(root)); 339 | currentFiles.forEach(file => { 340 | knownGeneratedFiles.forEach(fileToMatch => { 341 | // This remove all of knownGeneratedFiles. 342 | if (file === fileToMatch) { 343 | console.log(`Deleting generated file... ${chalk.cyan(file)}`); 344 | fs.removeSync(path.join(root, file)); 345 | } 346 | }); 347 | }); 348 | const remainingFiles = fs.readdirSync(path.join(root)); 349 | if (!remainingFiles.length) { 350 | // Delete target folder if empty 351 | console.log( 352 | `Deleting ${chalk.cyan(`${appName}/`)} from ${chalk.cyan( 353 | path.resolve(root, '..') 354 | )}` 355 | ); 356 | process.chdir(path.resolve(root, '..')); 357 | fs.removeSync(path.join(root)); 358 | } 359 | console.log('Done.'); 360 | process.exit(1); 361 | }); 362 | } 363 | 364 | function getInstallPackage(version, originalDirectory) { 365 | let packageToInstall = sdkPackageName; 366 | const validSemver = semver.valid(version); 367 | if (validSemver) { 368 | packageToInstall += `@${validSemver}`; 369 | } else if (version) { 370 | if (version[0] === '@' && version.indexOf('/') === -1) { 371 | packageToInstall += version; 372 | } else if (version.match(/^file:/)) { 373 | packageToInstall = `file:${path.resolve( 374 | originalDirectory, 375 | version.match(/^file:(.*)?$/)[1] 376 | )}`; 377 | } else { 378 | // for tar.gz or alternative paths 379 | packageToInstall = version; 380 | } 381 | } 382 | return packageToInstall; 383 | } 384 | 385 | function getTemporaryDirectory() { 386 | return new Promise((resolve, reject) => { 387 | // Unsafe cleanup lets us recursively delete the directory if it contains 388 | // contents; by default it only allows removal if it's empty 389 | tmp.dir({ unsafeCleanup: true }, (err, tmpdir, callback) => { 390 | if (err) { 391 | reject(err); 392 | } else { 393 | resolve({ 394 | tmpdir: tmpdir, 395 | cleanup: () => { 396 | try { 397 | callback(); 398 | } catch (ignored) { 399 | // Callback might throw and fail, since it's a temp directory the 400 | // OS will clean it up eventually... 401 | } 402 | }, 403 | }); 404 | } 405 | }); 406 | }); 407 | } 408 | 409 | function extractStream(stream, dest) { 410 | return new Promise((resolve, reject) => { 411 | stream.pipe( 412 | unpack(dest, err => { 413 | if (err) { 414 | reject(err); 415 | } else { 416 | resolve(dest); 417 | } 418 | }) 419 | ); 420 | }); 421 | } 422 | 423 | // Extract package name from tarball url or path. 424 | function getPackageName(installPackage) { 425 | if (installPackage.match(/^.+\.(tgz|tar\.gz)$/)) { 426 | return getTemporaryDirectory() 427 | .then(obj => { 428 | let stream; 429 | if (/^http/.test(installPackage)) { 430 | stream = hyperquest(installPackage); 431 | } else { 432 | stream = fs.createReadStream(installPackage); 433 | } 434 | return extractStream(stream, obj.tmpdir).then(() => obj); 435 | }) 436 | .then(obj => { 437 | const packageName = require(path.join(obj.tmpdir, 'package.json')).name; 438 | obj.cleanup(); 439 | return packageName; 440 | }) 441 | .catch(err => { 442 | // The package name could be with or without semver version, e.g. fitbit-sdk-0.2.0-alpha.1.tgz 443 | // However, this function returns package name only without semver version. 444 | console.log( 445 | `Could not extract the package name from the archive: ${err.message}` 446 | ); 447 | const assumedProjectName = installPackage.match( 448 | /^.+\/(.+?)(?:-\d+.+)?\.(tgz|tar\.gz)$/ 449 | )[1]; 450 | console.log( 451 | `Based on the filename, assuming it is "${chalk.cyan( 452 | assumedProjectName 453 | )}"` 454 | ); 455 | return Promise.resolve(assumedProjectName); 456 | }); 457 | } else if (installPackage.indexOf('git+') === 0) { 458 | // Pull package name out of git urls e.g: 459 | // git+https://github.com/mycompany/fitbit-sdk.git 460 | // git+ssh://github.com/mycompany/fitbit-sdk.git#v1.2.3 461 | return Promise.resolve(installPackage.match(/([^/]+)\.git(#.*)?$/)[1]); 462 | } else if (installPackage.match(/.+@/)) { 463 | // Do not match @scope/ when stripping off @version or @tag 464 | return Promise.resolve( 465 | installPackage.charAt(0) + installPackage.substr(1).split('@')[0] 466 | ); 467 | } else if (installPackage.match(/^file:/)) { 468 | const installPackagePath = installPackage.match(/^file:(.*)?$/)[1]; 469 | const installPackageJson = require(path.join( 470 | installPackagePath, 471 | 'package.json' 472 | )); 473 | return Promise.resolve(installPackageJson.name); 474 | } 475 | return Promise.resolve(installPackage); 476 | } 477 | 478 | function checkNpmVersion() { 479 | let hasMinNpm = false; 480 | let npmVersion = null; 481 | try { 482 | npmVersion = execSync('npm --version') 483 | .toString() 484 | .trim(); 485 | hasMinNpm = semver.gte(npmVersion, minNpmVersion); 486 | } catch (err) { 487 | // ignore 488 | } 489 | return { 490 | hasMinNpm: hasMinNpm, 491 | npmVersion: npmVersion, 492 | }; 493 | } 494 | 495 | function checkNodeVersion(packageName) { 496 | const packageJsonPath = path.resolve( 497 | process.cwd(), 498 | 'node_modules', 499 | packageName, 500 | 'package.json' 501 | ); 502 | const packageJson = require(packageJsonPath); 503 | if (!packageJson.engines || !packageJson.engines.node) { 504 | return; 505 | } 506 | 507 | if (!semver.satisfies(process.version, packageJson.engines.node)) { 508 | console.error( 509 | chalk.red( 510 | 'You are running Node %s.\n' + 511 | 'Fitbit SDK requires Node %s or higher. \n' + 512 | 'Please update your version of Node.' 513 | ), 514 | process.version, 515 | packageJson.engines.node 516 | ); 517 | process.exit(1); 518 | } 519 | } 520 | 521 | function checkAppName(appName) { 522 | const validationResult = validateProjectName(appName); 523 | if (!validationResult.validForNewPackages) { 524 | console.error( 525 | `Could not create a project called ${chalk.red( 526 | `"${appName}"` 527 | )} because of npm naming restrictions:` 528 | ); 529 | printValidationResults(validationResult.errors); 530 | printValidationResults(validationResult.warnings); 531 | process.exit(1); 532 | } 533 | 534 | const dependencies = sdkPackageDependencies.sort(); 535 | if (dependencies.indexOf(appName) >= 0) { 536 | console.error( 537 | chalk.red( 538 | `We cannot create a project called ${chalk.green( 539 | appName 540 | )} because a dependency with the same name exists.\n` + 541 | `Due to the way npm works, the following names are not allowed:\n\n` 542 | ) + 543 | chalk.cyan(dependencies.map(depName => ` ${depName}`).join('\n')) + 544 | chalk.red('\n\nPlease choose a different project name.') 545 | ); 546 | process.exit(1); 547 | } 548 | } 549 | 550 | function makeRangePrefix(dependencies, name, prefix) { 551 | const version = dependencies[name]; 552 | 553 | if (typeof version === 'undefined') { 554 | console.error(chalk.red(`Missing ${name} dependency in package.json`)); 555 | process.exit(1); 556 | } 557 | 558 | let patchedVersion = `${prefix}${version}`; 559 | 560 | if (!semver.validRange(patchedVersion)) { 561 | console.error( 562 | `Unable to patch ${name} dependency version because version ${chalk.red( 563 | version 564 | )} will become invalid ${chalk.red(patchedVersion)}` 565 | ); 566 | patchedVersion = version; 567 | } 568 | 569 | dependencies[name] = patchedVersion; 570 | } 571 | 572 | function setRangePrefixForDep(packageName, prefix) { 573 | const packagePath = path.join(process.cwd(), 'package.json'); 574 | const packageJson = require(packagePath); 575 | 576 | if (typeof packageJson.devDependencies === 'undefined') { 577 | console.error(chalk.red('Missing devDependencies in package.json')); 578 | process.exit(1); 579 | } 580 | 581 | const packageVersion = packageJson.devDependencies[packageName]; 582 | if (typeof packageVersion === 'undefined') { 583 | console.error(chalk.red(`Unable to find ${packageName} in package.json`)); 584 | process.exit(1); 585 | } 586 | 587 | makeRangePrefix(packageJson.devDependencies, packageName, prefix); 588 | 589 | fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + os.EOL); 590 | } 591 | 592 | // If project only contains files generated by GH, it’s safe. 593 | // Also, if project contains remnant error logs from a previous 594 | // installation, lets remove them now. 595 | // We also special case IJ-based products .idea because it integrates with CRA: 596 | // https://github.com/facebook/create-react-app/pull/368#issuecomment-243446094 597 | function isSafeToCreateProjectIn(root, name) { 598 | const validFiles = [ 599 | '.DS_Store', 600 | 'Thumbs.db', 601 | '.git', 602 | '.gitignore', 603 | '.idea', 604 | 'README.md', 605 | 'LICENSE', 606 | 'web.iml', 607 | '.hg', 608 | '.hgignore', 609 | '.hgcheck', 610 | '.npmignore', 611 | 'mkdocs.yml', 612 | 'docs', 613 | '.travis.yml', 614 | '.gitlab-ci.yml', 615 | '.gitattributes', 616 | ]; 617 | console.log(); 618 | 619 | const conflicts = fs 620 | .readdirSync(root) 621 | .filter(file => !validFiles.includes(file)) 622 | // Don't treat log files from previous installation as conflicts 623 | .filter( 624 | file => !errorLogFilePatterns.some(pattern => file.indexOf(pattern) === 0) 625 | ); 626 | 627 | if (conflicts.length > 0) { 628 | console.log( 629 | `The directory ${chalk.green(name)} contains files that could conflict:` 630 | ); 631 | console.log(); 632 | for (const file of conflicts) { 633 | console.log(` ${file}`); 634 | } 635 | console.log(); 636 | console.log( 637 | 'Either try using a new directory name, or remove the files listed above.' 638 | ); 639 | 640 | return false; 641 | } 642 | 643 | // Remove any remnant files from a previous installation 644 | const currentFiles = fs.readdirSync(path.join(root)); 645 | currentFiles.forEach(file => { 646 | errorLogFilePatterns.forEach(errorLogFilePattern => { 647 | // This will catch `(npm-debug|yarn-error|yarn-debug).log*` files 648 | if (file.indexOf(errorLogFilePattern) === 0) { 649 | fs.removeSync(path.join(root, file)); 650 | } 651 | }); 652 | }); 653 | return true; 654 | } 655 | 656 | function getProxy() { 657 | if (process.env.https_proxy) { 658 | return process.env.https_proxy; 659 | } else { 660 | try { 661 | // Trying to read https-proxy from .npmrc 662 | let httpsProxy = execSync('npm config get https-proxy') 663 | .toString() 664 | .trim(); 665 | return httpsProxy !== 'null' ? httpsProxy : undefined; 666 | } catch (e) { 667 | return; 668 | } 669 | } 670 | } 671 | function checkThatNpmCanReadCwd() { 672 | const cwd = process.cwd(); 673 | let childOutput = null; 674 | try { 675 | // Note: intentionally using spawn over exec since 676 | // the problem doesn't reproduce otherwise. 677 | // `npm config list` is the only reliable way I could find 678 | // to reproduce the wrong path. Just printing process.cwd() 679 | // in a Node process was not enough. 680 | childOutput = spawn.sync('npm', ['config', 'list']).output.join(''); 681 | } catch (err) { 682 | // Something went wrong spawning node. 683 | // Not great, but it means we can't do this check. 684 | // We might fail later on, but let's continue. 685 | return true; 686 | } 687 | if (typeof childOutput !== 'string') { 688 | return true; 689 | } 690 | const lines = childOutput.split('\n'); 691 | // `npm config list` output includes the following line: 692 | // "; cwd = C:\path\to\current\dir" (unquoted) 693 | // I couldn't find an easier way to get it. 694 | const prefix = '; cwd = '; 695 | const line = lines.find(line => line.indexOf(prefix) === 0); 696 | if (typeof line !== 'string') { 697 | // Fail gracefully. They could remove it. 698 | return true; 699 | } 700 | const npmCWD = line.substring(prefix.length); 701 | if (npmCWD === cwd) { 702 | return true; 703 | } 704 | console.error( 705 | chalk.red( 706 | `Could not start an npm process in the right directory.\n\n` + 707 | `The current directory is: ${chalk.bold(cwd)}\n` + 708 | `However, a newly started npm process runs in: ${chalk.bold( 709 | npmCWD 710 | )}\n\n` + 711 | `This is probably caused by a misconfigured system terminal shell.` 712 | ) 713 | ); 714 | if (process.platform === 'win32') { 715 | console.error( 716 | chalk.red(`On Windows, this can usually be fixed by running:\n\n`) + 717 | ` ${chalk.cyan( 718 | 'reg' 719 | )} delete "HKCU\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n` + 720 | ` ${chalk.cyan( 721 | 'reg' 722 | )} delete "HKLM\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n\n` + 723 | chalk.red(`Try to run the above two lines in the terminal.\n`) + 724 | chalk.red( 725 | `To learn more about this problem, read: https://blogs.msdn.microsoft.com/oldnewthing/20071121-00/?p=24433/` 726 | ) 727 | ); 728 | } 729 | return false; 730 | } 731 | 732 | function checkIfOnline(useYarn) { 733 | if (!useYarn) { 734 | // Don't ping the Yarn registry. 735 | // We'll just assume the best case. 736 | return Promise.resolve(true); 737 | } 738 | 739 | return new Promise(resolve => { 740 | dns.lookup('registry.yarnpkg.com', err => { 741 | let proxy; 742 | if (err != null && (proxy = getProxy())) { 743 | // If a proxy is defined, we likely can't resolve external hostnames. 744 | // Try to resolve the proxy name as an indication of a connection. 745 | dns.lookup(url.parse(proxy).hostname, proxyErr => { 746 | resolve(proxyErr == null); 747 | }); 748 | } else { 749 | resolve(err == null); 750 | } 751 | }); 752 | }); 753 | } 754 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Copyright (c) 2015-2018, Facebook, Inc. 5 | * Copyright (c) 2018-present, Fitbit, Inc. 6 | * 7 | * This source code is licensed under the MIT license found in the 8 | * LICENSE file in the root directory of this source tree. 9 | */ 10 | 11 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | // /!\ DO NOT MODIFY THIS FILE /!\ 13 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | // 15 | // create-fitbit-app is installed globally on people's computers. This means 16 | // that it is extremely difficult to have them upgrade the version and 17 | // because there's only one global version installed, it is very prone to 18 | // breaking changes. 19 | // 20 | // The only job of create-fitbit-app is to init the repository and then 21 | // forward all the commands to the local version of create-fitbit-app. 22 | // 23 | // If you need to add a new command, please add it to the scripts/ folder. 24 | // 25 | // The only reason to modify this file is to add more warnings and 26 | // troubleshooting information for the `create-fitbit-app` command. 27 | // 28 | // Do not make breaking changes! We absolutely don't want to have to 29 | // tell people to update their global version of create-fitbit-app. 30 | // 31 | // Also be careful with new language features. 32 | // This file must work on Node 0.10+. 33 | // 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | // /!\ DO NOT MODIFY THIS FILE /!\ 36 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 37 | 38 | 'use strict'; 39 | 40 | var chalk = require('chalk'); 41 | 42 | var currentNodeVersion = process.versions.node; 43 | var semver = currentNodeVersion.split('.'); 44 | var major = semver[0]; 45 | 46 | if (major < 8) { 47 | console.error( 48 | chalk.red( 49 | 'You are running Node ' + 50 | currentNodeVersion + 51 | '.\n' + 52 | 'Fitbit SDK requires Node 8 or higher. \n' + 53 | 'Please update your version of Node.' 54 | ) 55 | ); 56 | process.exit(1); 57 | } 58 | 59 | require('./createFitbitApp'); 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-fitbit-app", 3 | "version": "1.0.1", 4 | "description": "Create Fitbit SDK apps with one command.", 5 | "license": "MIT", 6 | "author": "Fitbit, Inc.", 7 | "repository": "github:Fitbit/create-fitbit-app", 8 | "bugs": { 9 | "url": "https://github.com/Fitbit/create-fitbit-app/issues" 10 | }, 11 | "homepage": "https://github.com/Fitbit/create-fitbit-app#readme", 12 | "engines": { 13 | "node": ">=8" 14 | }, 15 | "files": [ 16 | "index.js", 17 | "createFitbitApp.js" 18 | ], 19 | "bin": { 20 | "create-fitbit-app": "./index.js" 21 | }, 22 | "dependencies": { 23 | "chalk": "^3.0.0", 24 | "commander": "^5.0.0", 25 | "cross-spawn": "^7.0.1", 26 | "envinfo": "7.5.0", 27 | "fs-extra": "^8.1.0", 28 | "hyperquest": "^2.1.2", 29 | "semver": "^7.1.3", 30 | "tar-pack": "^3.4.0", 31 | "tmp": "0.1.0", 32 | "validate-npm-package-name": "^3.0.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/color-name@^1.1.1": 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" 8 | integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== 9 | 10 | ansi-styles@^4.1.0: 11 | version "4.2.1" 12 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" 13 | integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== 14 | dependencies: 15 | "@types/color-name" "^1.1.1" 16 | color-convert "^2.0.1" 17 | 18 | balanced-match@^1.0.0: 19 | version "1.0.0" 20 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 21 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 22 | 23 | block-stream@*: 24 | version "0.0.9" 25 | resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" 26 | integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= 27 | dependencies: 28 | inherits "~2.0.0" 29 | 30 | brace-expansion@^1.1.7: 31 | version "1.1.11" 32 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 33 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 34 | dependencies: 35 | balanced-match "^1.0.0" 36 | concat-map "0.0.1" 37 | 38 | buffer-from@^0.1.1: 39 | version "0.1.2" 40 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0" 41 | integrity sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg== 42 | 43 | builtins@^1.0.3: 44 | version "1.0.3" 45 | resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" 46 | integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= 47 | 48 | chalk@^3.0.0: 49 | version "3.0.0" 50 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" 51 | integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== 52 | dependencies: 53 | ansi-styles "^4.1.0" 54 | supports-color "^7.1.0" 55 | 56 | color-convert@^2.0.1: 57 | version "2.0.1" 58 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 59 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 60 | dependencies: 61 | color-name "~1.1.4" 62 | 63 | color-name@~1.1.4: 64 | version "1.1.4" 65 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 66 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 67 | 68 | commander@^5.0.0: 69 | version "5.0.0" 70 | resolved "https://registry.yarnpkg.com/commander/-/commander-5.0.0.tgz#dbf1909b49e5044f8fdaf0adc809f0c0722bdfd0" 71 | integrity sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ== 72 | 73 | concat-map@0.0.1: 74 | version "0.0.1" 75 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 76 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 77 | 78 | core-util-is@~1.0.0: 79 | version "1.0.2" 80 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 81 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 82 | 83 | cross-spawn@^7.0.1: 84 | version "7.0.1" 85 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" 86 | integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== 87 | dependencies: 88 | path-key "^3.1.0" 89 | shebang-command "^2.0.0" 90 | which "^2.0.1" 91 | 92 | debug@^2.2.0: 93 | version "2.6.9" 94 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 95 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 96 | dependencies: 97 | ms "2.0.0" 98 | 99 | duplexer2@~0.0.2: 100 | version "0.0.2" 101 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" 102 | integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= 103 | dependencies: 104 | readable-stream "~1.1.9" 105 | 106 | envinfo@7.5.0: 107 | version "7.5.0" 108 | resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.0.tgz#91410bb6db262fb4f1409bd506e9ff57e91023f4" 109 | integrity sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ== 110 | 111 | fs-extra@^8.1.0: 112 | version "8.1.0" 113 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 114 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== 115 | dependencies: 116 | graceful-fs "^4.2.0" 117 | jsonfile "^4.0.0" 118 | universalify "^0.1.0" 119 | 120 | fs.realpath@^1.0.0: 121 | version "1.0.0" 122 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 123 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 124 | 125 | fstream-ignore@^1.0.5: 126 | version "1.0.5" 127 | resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" 128 | integrity sha1-nDHa40dnAY/h0kmyTa2mfQktoQU= 129 | dependencies: 130 | fstream "^1.0.0" 131 | inherits "2" 132 | minimatch "^3.0.0" 133 | 134 | fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.12: 135 | version "1.0.12" 136 | resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" 137 | integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== 138 | dependencies: 139 | graceful-fs "^4.1.2" 140 | inherits "~2.0.0" 141 | mkdirp ">=0.5 0" 142 | rimraf "2" 143 | 144 | glob@^7.1.3: 145 | version "7.1.6" 146 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 147 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 148 | dependencies: 149 | fs.realpath "^1.0.0" 150 | inflight "^1.0.4" 151 | inherits "2" 152 | minimatch "^3.0.4" 153 | once "^1.3.0" 154 | path-is-absolute "^1.0.0" 155 | 156 | graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: 157 | version "4.2.3" 158 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" 159 | integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== 160 | 161 | has-flag@^4.0.0: 162 | version "4.0.0" 163 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 164 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 165 | 166 | hyperquest@^2.1.2: 167 | version "2.1.3" 168 | resolved "https://registry.yarnpkg.com/hyperquest/-/hyperquest-2.1.3.tgz#523127d7a343181b40bf324e231d2576edf52633" 169 | integrity sha512-fUuDOrB47PqNK/BAMOS13v41UoaqIxqSLHX6CAbOD7OfT+/GCWO1/vPLfTNutOeXrv1ikuaZ3yux+33Z9vh+rw== 170 | dependencies: 171 | buffer-from "^0.1.1" 172 | duplexer2 "~0.0.2" 173 | through2 "~0.6.3" 174 | 175 | inflight@^1.0.4: 176 | version "1.0.6" 177 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 178 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 179 | dependencies: 180 | once "^1.3.0" 181 | wrappy "1" 182 | 183 | inherits@2, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: 184 | version "2.0.4" 185 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 186 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 187 | 188 | isarray@0.0.1: 189 | version "0.0.1" 190 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" 191 | integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= 192 | 193 | isarray@~1.0.0: 194 | version "1.0.0" 195 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 196 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 197 | 198 | isexe@^2.0.0: 199 | version "2.0.0" 200 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 201 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 202 | 203 | jsonfile@^4.0.0: 204 | version "4.0.0" 205 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 206 | integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= 207 | optionalDependencies: 208 | graceful-fs "^4.1.6" 209 | 210 | minimatch@^3.0.0, minimatch@^3.0.4: 211 | version "3.0.4" 212 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 213 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 214 | dependencies: 215 | brace-expansion "^1.1.7" 216 | 217 | minimist@^1.2.5: 218 | version "1.2.5" 219 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 220 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== 221 | 222 | "mkdirp@>=0.5 0": 223 | version "0.5.3" 224 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" 225 | integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== 226 | dependencies: 227 | minimist "^1.2.5" 228 | 229 | ms@2.0.0: 230 | version "2.0.0" 231 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 232 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 233 | 234 | once@^1.3.0, once@^1.3.3: 235 | version "1.4.0" 236 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 237 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 238 | dependencies: 239 | wrappy "1" 240 | 241 | path-is-absolute@^1.0.0: 242 | version "1.0.1" 243 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 244 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 245 | 246 | path-key@^3.1.0: 247 | version "3.1.1" 248 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 249 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 250 | 251 | process-nextick-args@~2.0.0: 252 | version "2.0.1" 253 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 254 | integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 255 | 256 | "readable-stream@>=1.0.33-1 <1.1.0-0": 257 | version "1.0.34" 258 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" 259 | integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= 260 | dependencies: 261 | core-util-is "~1.0.0" 262 | inherits "~2.0.1" 263 | isarray "0.0.1" 264 | string_decoder "~0.10.x" 265 | 266 | readable-stream@^2.1.4: 267 | version "2.3.7" 268 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 269 | integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 270 | dependencies: 271 | core-util-is "~1.0.0" 272 | inherits "~2.0.3" 273 | isarray "~1.0.0" 274 | process-nextick-args "~2.0.0" 275 | safe-buffer "~5.1.1" 276 | string_decoder "~1.1.1" 277 | util-deprecate "~1.0.1" 278 | 279 | readable-stream@~1.1.9: 280 | version "1.1.14" 281 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" 282 | integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= 283 | dependencies: 284 | core-util-is "~1.0.0" 285 | inherits "~2.0.1" 286 | isarray "0.0.1" 287 | string_decoder "~0.10.x" 288 | 289 | rimraf@2, rimraf@^2.5.1, rimraf@^2.6.3: 290 | version "2.7.1" 291 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 292 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 293 | dependencies: 294 | glob "^7.1.3" 295 | 296 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 297 | version "5.1.2" 298 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 299 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 300 | 301 | semver@^7.1.3: 302 | version "7.1.3" 303 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" 304 | integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== 305 | 306 | shebang-command@^2.0.0: 307 | version "2.0.0" 308 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 309 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 310 | dependencies: 311 | shebang-regex "^3.0.0" 312 | 313 | shebang-regex@^3.0.0: 314 | version "3.0.0" 315 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 316 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 317 | 318 | string_decoder@~0.10.x: 319 | version "0.10.31" 320 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 321 | integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= 322 | 323 | string_decoder@~1.1.1: 324 | version "1.1.1" 325 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 326 | integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 327 | dependencies: 328 | safe-buffer "~5.1.0" 329 | 330 | supports-color@^7.1.0: 331 | version "7.1.0" 332 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" 333 | integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== 334 | dependencies: 335 | has-flag "^4.0.0" 336 | 337 | tar-pack@^3.4.0: 338 | version "3.4.1" 339 | resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" 340 | integrity sha512-PPRybI9+jM5tjtCbN2cxmmRU7YmqT3Zv/UDy48tAh2XRkLa9bAORtSWLkVc13+GJF+cdTh1yEnHEk3cpTaL5Kg== 341 | dependencies: 342 | debug "^2.2.0" 343 | fstream "^1.0.10" 344 | fstream-ignore "^1.0.5" 345 | once "^1.3.3" 346 | readable-stream "^2.1.4" 347 | rimraf "^2.5.1" 348 | tar "^2.2.1" 349 | uid-number "^0.0.6" 350 | 351 | tar@^2.2.1: 352 | version "2.2.2" 353 | resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" 354 | integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== 355 | dependencies: 356 | block-stream "*" 357 | fstream "^1.0.12" 358 | inherits "2" 359 | 360 | through2@~0.6.3: 361 | version "0.6.5" 362 | resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" 363 | integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= 364 | dependencies: 365 | readable-stream ">=1.0.33-1 <1.1.0-0" 366 | xtend ">=4.0.0 <4.1.0-0" 367 | 368 | tmp@0.1.0: 369 | version "0.1.0" 370 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" 371 | integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== 372 | dependencies: 373 | rimraf "^2.6.3" 374 | 375 | uid-number@^0.0.6: 376 | version "0.0.6" 377 | resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" 378 | integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= 379 | 380 | universalify@^0.1.0: 381 | version "0.1.2" 382 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 383 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 384 | 385 | util-deprecate@~1.0.1: 386 | version "1.0.2" 387 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 388 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 389 | 390 | validate-npm-package-name@^3.0.0: 391 | version "3.0.0" 392 | resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" 393 | integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= 394 | dependencies: 395 | builtins "^1.0.3" 396 | 397 | which@^2.0.1: 398 | version "2.0.2" 399 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 400 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 401 | dependencies: 402 | isexe "^2.0.0" 403 | 404 | wrappy@1: 405 | version "1.0.2" 406 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 407 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 408 | 409 | "xtend@>=4.0.0 <4.1.0-0": 410 | version "4.0.2" 411 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 412 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 413 | --------------------------------------------------------------------------------