-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Supported: * Initializing a new project. * Starting a project (if logged in w/ Exponent credentials -- anon access to come), running on physical device * Running a project in an iOS simulator * Running tests on the template project (with npm, waiting on yarnpkg/yarn#760 for yarn test support)
- Loading branch information
0 parents
commit 2d92704
Showing
25 changed files
with
7,946 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules | ||
build/ | ||
yarn-error.log | ||
*.tgz | ||
.DS_Store |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# create-react-native-app | ||
|
||
If you're reading this right now, `create-react-native-app` isn't yet ready for general consumption. | ||
|
||
Please keep an eye on this README, we'll update with details when it's all ready to try out. | ||
|
||
## TODO | ||
|
||
* license, authors, changelog, etc | ||
* confirm packaging works as intended | ||
* npm run eject (bare and with exponentview) | ||
* npm run android? | ||
|
||
## Console Colors | ||
|
||
The convention for using chalk in console output: | ||
|
||
* blue -> in progress | ||
* green -> success | ||
* yellow -> warn | ||
* red -> error | ||
* cyan -> something the user inputs | ||
* underline -> URL |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"presets": ["es2015", "stage-1"], | ||
"plugins": ["transform-runtime", "add-module-exports", "transform-flow-strip-types"] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# create-react-native-app | ||
|
||
## Development | ||
|
||
`yarn && gulp` will start a watcher that will build artifacts and place them in the build directory. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
const path = require('path'); | ||
const gulp = require('gulp'); | ||
const babel = require('gulp-babel'); | ||
const changed = require('gulp-changed'); | ||
const plumber = require('gulp-plumber'); | ||
const sourcemaps = require('gulp-sourcemaps'); | ||
const rimraf = require('rimraf'); | ||
|
||
const paths = { | ||
source: 'src/**/*.js', | ||
build: 'build', | ||
sourceRoot: path.join(__dirname, 'src'), | ||
}; | ||
|
||
const tasks = { | ||
babel() { | ||
return gulp.src(paths.source) | ||
.pipe(changed(paths.build)) | ||
.pipe(plumber()) | ||
.pipe(sourcemaps.init()) | ||
.pipe(babel()) | ||
.pipe(sourcemaps.write('__sourcemaps__', { sourceRoot: paths.sourceRoot })) | ||
.pipe(gulp.dest(paths.build)); | ||
}, | ||
|
||
watchBabel(done) { | ||
gulp.watch(paths.source, tasks.babel); | ||
done(); | ||
}, | ||
}; | ||
|
||
gulp.task('build', tasks.babel); | ||
gulp.task('babel', tasks.babel); | ||
gulp.task('watch', tasks.watchBabel); | ||
gulp.task('clean', done => { | ||
rimraf(paths.build, done); | ||
}); | ||
|
||
gulp.task('default', gulp.series('watch')); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "create-react-native-app", | ||
"version": "0.1.0", | ||
"description": "Create React Native apps with no build configuration.", | ||
"license": "BSD-3-Clause", | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"files": [ | ||
"build" | ||
], | ||
"bin": { | ||
"create-react-native-app": "./build/index.js" | ||
}, | ||
"dependencies": { | ||
"babel-runtime": "^6.9.2", | ||
"chalk": "^1.1.1", | ||
"cross-spawn": "^4.0.0", | ||
"fs-promise": "^1.0.0", | ||
"minimist": "^1.2.0", | ||
"path-exists": "^2.1.0", | ||
"semver": "^5.0.3", | ||
"source-map-support": "^0.4.1" | ||
}, | ||
"devDependencies": { | ||
"babel-plugin-add-module-exports": "^0.2.1", | ||
"babel-plugin-transform-flow-strip-types": "^6.8.0", | ||
"babel-plugin-transform-runtime": "^6.9.0", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-preset-stage-1": "^6.5.0", | ||
"gulp": "git+https://github.com/gulpjs/gulp#4.0", | ||
"gulp-babel": "^6.1.2", | ||
"gulp-changed": "^1.3.0", | ||
"gulp-plumber": "^1.1.0", | ||
"gulp-sourcemaps": "^1.6.0", | ||
"rimraf": "^2.5.2" | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
#!/usr/bin/env node | ||
|
||
// @flow | ||
|
||
// DON'T MODIFY THIS FILE | ||
// IF AT ALL POSSIBLE, MAKE ANY CHANGES IN THE SCRIPTS PACKAGE | ||
|
||
import fsp from 'fs-promise'; | ||
import chalk from 'chalk'; | ||
import minimist from 'minimist'; | ||
import path from 'path'; | ||
import pathExists from 'path-exists'; | ||
import semver from 'semver'; | ||
import spawn from 'cross-spawn'; | ||
|
||
const argv = minimist(process.argv.slice(2)); | ||
|
||
/** | ||
* Arguments: | ||
* --version - to print current version | ||
* --verbose - to print npm logs during init | ||
* --scripts-version <alternative package> | ||
* Example of valid values: | ||
* - a specific npm version: "0.22.0-rc1" | ||
* - a .tgz archive from npm: "https://registry.npmjs.org/react-native-scripts/-/react-native-scripts-0.20.0.tgz" | ||
* - a package from `tasks/clean_pack.sh`: "/home/adam/create-react-native-app/react-native-scripts-0.22.0.tgz" | ||
*/ | ||
const commands = argv._; | ||
if (commands.length === 0) { | ||
if (argv.version) { | ||
const version = require('../package.json').version; | ||
console.log(`create-react-native-app version: ${version}`); | ||
process.exit(); | ||
} | ||
console.error( | ||
'Usage: create-react-native-app <project-directory> [--verbose]' | ||
); | ||
process.exit(1); | ||
} | ||
|
||
createApp(commands[0], !!argv.verbose, argv['scripts-version']).then(() => {}); | ||
|
||
async function createApp(name: string, verbose: boolean, version: ?string): Promise<void> { | ||
const root = path.resolve(name); | ||
const appName = path.basename(root); | ||
|
||
const packageToInstall = getInstallPackage(version); | ||
const packageName = getPackageName(packageToInstall); | ||
checkAppName(appName, packageName); | ||
|
||
if (!await pathExists(name)) { | ||
await fsp.mkdir(root); | ||
} else if (!await isSafeToCreateProjectIn(root)) { | ||
console.log(`The directory \`${name}\` contains file(s) that could conflict. Aborting.`); | ||
process.exit(1); | ||
} | ||
|
||
console.log(`Creating a new React Native app in ${root}.`); | ||
console.log(); | ||
|
||
const packageJson = { | ||
name: appName, | ||
version: '0.1.0', | ||
private: true, | ||
}; | ||
await fsp.writeFile( | ||
path.join(root, 'package.json'), | ||
JSON.stringify(packageJson, null, 2) | ||
); | ||
process.chdir(root); | ||
|
||
console.log('Installing packages. This might take a couple minutes.'); | ||
console.log('Installing react-native-scripts...'); | ||
console.log(); | ||
|
||
await run(root, appName, version, verbose, packageToInstall, packageName); | ||
} | ||
|
||
function install(packageToInstall: string, verbose: boolean, | ||
callback: (code: number, command: string, args: Array<string>) => Promise<void> | ||
): void { | ||
let args = [ | ||
'add', | ||
'--dev', | ||
'--exact', | ||
packageToInstall, | ||
]; | ||
const proc = spawn('yarnpkg', args, {stdio: 'inherit'}); | ||
|
||
let yarnExists = true; | ||
proc.on('error', function(err) { | ||
if (err.code === 'ENOENT') { | ||
yarnExists = false; | ||
} | ||
}); | ||
|
||
proc.on('close', function(code) { | ||
if (yarnExists) { | ||
callback(code, 'yarnpkg', args).then(() => {}, (e) => { throw e; }); | ||
return; | ||
} | ||
// No Yarn installed, continuing with npm. | ||
args = ['install']; | ||
|
||
if (verbose) { | ||
args.push('--verbose'); | ||
} | ||
|
||
args = args.concat([ | ||
'--save-dev', | ||
'--save-exact', | ||
packageToInstall, | ||
]); | ||
|
||
const npmProc = spawn('npm', args, {stdio: 'inherit'}); | ||
npmProc.on('close', function(code) { | ||
callback(code, 'npm', args).then(() => {}, (e) => { throw e; });; | ||
}); | ||
}); | ||
} | ||
|
||
async function run(root: string, appName: string, version: ?string, verbose: boolean, | ||
packageToInstall: string, packageName: string): Promise<void> { | ||
|
||
install(packageToInstall, verbose, async (code: number, command: string, args: Array<string>) => { | ||
if (code !== 0) { | ||
console.error(`\`${command} ${args.join(' ')}\` failed`); | ||
return; | ||
} | ||
|
||
await checkNodeVersion(packageName); | ||
|
||
const scriptsPath = path.resolve( | ||
process.cwd(), | ||
'node_modules', | ||
packageName, | ||
'build', | ||
'scripts', | ||
'init.js' | ||
); | ||
|
||
// $FlowFixMe (dikaiosune) maybe there's a way to convince flow this is legit? | ||
const init = require(scriptsPath); | ||
await init(root, appName, verbose); | ||
}); | ||
} | ||
|
||
function getInstallPackage(version: ?string): string { | ||
let packageToInstall = 'react-native-scripts'; | ||
const validSemver = semver.valid(version); | ||
if (validSemver) { | ||
packageToInstall += '@' + validSemver; | ||
} else if (version) { | ||
// for tar.gz or alternative paths | ||
packageToInstall = version; | ||
} | ||
return packageToInstall; | ||
} | ||
|
||
// Extract package name from tarball url or path. | ||
function getPackageName(installPackage: string): string { | ||
if (installPackage.indexOf('.tgz') > -1) { | ||
// The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz | ||
// However, this function returns package name only wihout semver version. | ||
const matches = installPackage.match(/^.+\/(.+?)(?:-\d+.+)?\.tgz$/); | ||
if (matches && matches.length >= 2) { | ||
return matches[1]; | ||
} else { | ||
throw new Error(`Provided scripts package (${installPackage}) doesn't have a valid filename.`); | ||
} | ||
} else if (installPackage.indexOf('@') > 0) { | ||
// Do not match @scope/ when stripping off @version or @tag | ||
return installPackage.charAt(0) + installPackage.substr(1).split('@')[0]; | ||
} | ||
return installPackage; | ||
} | ||
|
||
async function checkNodeVersion(packageName: string): Promise<void> { | ||
const packageJsonPath = path.resolve( | ||
process.cwd(), | ||
'node_modules', | ||
packageName, | ||
'package.json' | ||
); | ||
|
||
const packageJson = JSON.parse(await fsp.readFile(packageJsonPath)); | ||
if (!packageJson.engines || !packageJson.engines.node) { | ||
return; | ||
} | ||
|
||
if (!semver.satisfies(process.version, packageJson.engines.node)) { | ||
console.error( | ||
chalk.red( | ||
'You are currently running Node %s but create-react-native-app requires %s.' + | ||
' Please use a supported version of Node.\n' | ||
), | ||
process.version, | ||
packageJson.engines.node | ||
); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
function checkAppName(appName: string, packageName: string): void { | ||
const allDependencies = ['react-native-scripts', 'exponent', 'vector-icons', 'react', 'react-native']; | ||
|
||
if (allDependencies.indexOf(appName) >= 0) { | ||
console.error( | ||
chalk.red( | ||
'We cannot create a project called `' + appName + '` because a dependency with the same name exists.\n' + | ||
'Due to the way npm works, the following names are not allowed:\n\n' | ||
) + | ||
chalk.cyan( | ||
allDependencies.map((depName) => { | ||
return ' ' + depName; | ||
}).join('\n') | ||
) + | ||
chalk.red('\n\nPlease choose a different project name.') | ||
); | ||
process.exit(1); | ||
} | ||
} | ||
|
||
// If project only contains files generated by GH, it’s safe | ||
async function isSafeToCreateProjectIn(root: string): Promise<boolean> { | ||
const validFiles = ['.DS_Store', 'Thumbs.db', '.git', '.gitignore', 'README.md', 'LICENSE']; | ||
return (await fsp.readdir(root)) | ||
.every((file) => { | ||
return validFiles.indexOf(file) >= 0; | ||
}); | ||
} |
Oops, something went wrong.