Dustin Ingram 07dc35e4ad Add .gitattributes file, treat .pbxproj as binary
Summary:
By default, the `.pbxproj` file generated by React Native should be treated as binary data to prevent Git from possibly automatically converting or fixing CRLF issues with this file, which would break it.

See https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes#identifying-binary-files-znTLIVhvhG (where it specifically mentions `.pbxproj` files) for more information.
Closes https://github.com/facebook/react-native/pull/10864

Differential Revision: D4162492

fbshipit-source-id: 2c8f2f0ab2bb41c23b1460ef6cba672ef38f6310
2016-11-10 17:58:41 -08:00

214 lines
6.2 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var execSync = require('child_process').execSync;
var fs = require('fs');
var path = require('path');
var semver = require('semver')
var utils = require('../generator-utils');
var yeoman = require('yeoman-generator');
// Use Yarn if available, it's much faster than the npm client.
// Return the version of yarn installed on the system, null if yarn is not available.
function getYarnVersionIfAvailable() {
let yarnVersion;
try {
// execSync returns a Buffer -> convert to string
if (process.platform.startsWith('win')) {
yarnVersion = (execSync('yarn --version').toString() || '').trim();
} else {
yarnVersion = (execSync('yarn --version 2>/dev/null').toString() || '').trim();
}
} catch (error) {
return null;
}
// yarn < 0.16 has a 'missing manifest' bug
try {
if (semver.gte(yarnVersion, '0.16.0')) {
return yarnVersion;
} else {
return null;
}
} catch (error) {
console.error('Cannot parse yarn version: ' + yarnVersion);
return null;
}
}
/**
* Check that 'react-native init' itself used yarn to install React Native.
* When using an old global react-native-cli@1.0.0 (or older), we don't want
* to install React Native with npm, and React + Jest with yarn.
* Let's be safe and not mix yarn and npm in a single project.
* @param projectDir e.g. /Users/martin/AwesomeApp
*/
function isGlobalCliUsingYarn(projectDir) {
return fs.existsSync(path.join(projectDir, 'yarn.lock'));
}
module.exports = yeoman.generators.NamedBase.extend({
constructor: function() {
yeoman.generators.NamedBase.apply(this, arguments);
this.option('skip-ios', {
desc: 'Skip generating iOS files',
type: Boolean,
defaults: false
});
this.option('skip-android', {
desc: 'Skip generating Android files',
type: Boolean,
defaults: false
});
this.option('skip-jest', {
desc: 'Skip installing Jest',
type: Boolean,
defaults: false
});
this.option('upgrade', {
desc: 'Specify an upgrade',
type: Boolean,
defaults: false
});
// Temporary option until yarn becomes stable.
this.option('npm', {
desc: 'Use the npm client, even if yarn is available.',
type: Boolean,
defaults: false
});
// this passes command line arguments down to the composed generators
var args = {args: arguments[0], options: this.options};
if (!this.options['skip-ios']) {
this.composeWith('react:ios', args, {
local: require.resolve(path.resolve(__dirname, '..', 'generator-ios'))
});
}
if (!this.options['skip-android']) {
this.composeWith('react:android', args, {
local: require.resolve(path.resolve(__dirname, '..', 'generator-android'))
});
}
},
configuring: function() {
utils.copyAndReplace(
this.templatePath('../../../.flowconfig'),
this.destinationPath('.flowconfig'),
{
'Libraries\/react-native\/react-native-interface.js' : 'node_modules/react-native/Libraries/react-native/react-native-interface.js',
'^flow/$' : 'node_modules/react-native/flow\nflow/'
}
);
this.fs.copy(
this.templatePath('_gitignore'),
this.destinationPath('.gitignore')
);
this.fs.copy(
this.templatePath('_gitattributes'),
this.destinationPath('.gitattributes')
);
this.fs.copy(
this.templatePath('_watchmanconfig'),
this.destinationPath('.watchmanconfig')
);
this.fs.copy(
this.templatePath('_buckconfig'),
this.destinationPath('.buckconfig')
);
},
writing: function() {
if (this.options.upgrade) {
// never upgrade index.*.js files
return;
}
if (!this.options['skip-ios']) {
this.fs.copyTpl(
this.templatePath('index.ios.js'),
this.destinationPath('index.ios.js'),
{name: this.name}
);
}
if (!this.options['skip-android']) {
this.fs.copyTpl(
this.templatePath('index.android.js'),
this.destinationPath('index.android.js'),
{name: this.name}
);
}
},
install: function() {
if (this.options.upgrade) {
return;
}
var reactNativePackageJson = require('../../package.json');
var { peerDependencies } = reactNativePackageJson;
if (!peerDependencies) {
return;
}
var reactVersion = peerDependencies.react;
if (!reactVersion) {
return;
}
const yarnVersion = (!this.options['npm']) && getYarnVersionIfAvailable() && isGlobalCliUsingYarn(this.destinationRoot());
console.log('Installing React...');
if (yarnVersion) {
execSync(`yarn add react@${reactVersion}`);
} else {
this.npmInstall(`react@${reactVersion}`, { '--save': true, '--save-exact': true });
}
if (!this.options['skip-jest']) {
console.log('Installing Jest...');
if (yarnVersion) {
execSync(`yarn add jest babel-jest jest-react-native babel-preset-react-native react-test-renderer@${reactVersion} --dev --exact`);
} else {
this.npmInstall(`jest babel-jest babel-preset-react-native react-test-renderer@${reactVersion}`.split(' '), {
saveDev: true,
'--save-exact': true
});
}
fs.writeFileSync(
path.join(
this.destinationRoot(),
'.babelrc'
),
'{\n"presets": ["react-native"]\n}'
);
this.fs.copy(
this.templatePath('__tests__'),
this.destinationPath('__tests__'),
{
nodir: false
}
);
var packageJSONPath = path.join(
this.destinationRoot(),
'package.json'
);
var packageJSON = JSON.parse(
fs.readFileSync(
packageJSONPath
)
);
packageJSON.scripts.test = 'jest';
packageJSON.jest = {
preset: 'react-native'
};
fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON, null, '\t'));
}
}
});