templates.js
6.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
* 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';
const copyProjectTemplateAndReplace = require('./copyProjectTemplateAndReplace');
const execSync = require('child_process').execSync;
const fs = require('fs');
const path = require('path');
/**
* Templates released as part of react-native in local-cli/templates.
*/
const builtInTemplates = {
navigation: 'HelloNavigation',
};
function listTemplatesAndExit(newProjectName, options) {
if (options.template === true) {
// Just listing templates using 'react-native init --template'.
// Not creating a new app.
// Print available templates and exit.
const templateKeys = Object.keys(builtInTemplates);
if (templateKeys.length === 0) {
// Just a guard, should never happen as long builtInTemplates
// above is defined correctly :)
console.log(
'There are no templates available besides ' +
'the default "Hello World" one.'
);
} else {
console.log(
'The available templates are:\n' +
templateKeys.join('\n') +
'\nYou can use these to create an app based on a template, for example: ' +
'you could run: ' +
'react-native init ' + newProjectName + ' --template ' + templateKeys[0]
);
}
// Exit 'react-native init'
return true;
}
// Continue 'react-native init'
return false;
}
/**
* @param destPath Create the new project at this path.
* @param newProjectName For example 'AwesomeApp'.
* @param template Template to use, for example 'navigation'.
* @param yarnVersion Version of yarn available on the system, or null if
* yarn is not available. For example '0.18.1'.
*/
function createProjectFromTemplate(destPath, newProjectName, template, yarnVersion) {
// Expand the basic 'HelloWorld' template
copyProjectTemplateAndReplace(
path.resolve('node_modules', 'react-native', 'local-cli', 'templates', 'HelloWorld'),
destPath,
newProjectName
);
if (template === undefined) {
// No specific template, use just the HelloWorld template above
return;
}
// Keep the files from the 'HelloWorld' template, and overwrite some of them
// with the specified project template.
// The 'HelloWorld' template contains the native files (these are used by
// all templates) and every other template only contains additional JS code.
// Reason:
// This way we don't have to duplicate the native files in every template.
// If we duplicated them we'd make RN larger and risk that people would
// forget to maintain all the copies so they would go out of sync.
const builtInTemplateName = builtInTemplates[template];
if (builtInTemplateName) {
// template is e.g. 'navigation',
// use the built-in local-cli/templates/HelloNavigation folder
createFromBuiltInTemplate(builtInTemplateName, destPath, newProjectName, yarnVersion);
} else {
// template is e.g. 'ignite',
// use the template react-native-template-ignite from npm
createFromRemoteTemplate(template, destPath, newProjectName, yarnVersion);
}
}
// (We might want to get rid of built-in templates in the future -
// publish them to npm and install from there.)
function createFromBuiltInTemplate(templateName, destPath, newProjectName, yarnVersion) {
const templatePath = path.resolve(
'node_modules', 'react-native', 'local-cli', 'templates', templateName
);
copyProjectTemplateAndReplace(
templatePath,
destPath,
newProjectName,
);
installTemplateDependencies(templatePath, yarnVersion);
}
/**
* The following formats are supported for the template:
* - 'demo' -> Fetch the package react-native-template-demo from npm
* - git://..., http://..., file://... or any other URL supported by npm
*/
function createFromRemoteTemplate(template, destPath, newProjectName, yarnVersion) {
let installPackage;
let templateName;
if (template.includes('://')) {
// URL, e.g. git://, file://
installPackage = template;
templateName = template.substr(template.lastIndexOf('/') + 1);
} else {
// e.g 'demo'
installPackage = 'react-native-template-' + template;
templateName = installPackage;
}
// Check if the template exists
console.log(`Fetching template ${installPackage}...`);
try {
if (yarnVersion) {
execSync(`yarn add ${installPackage} --ignore-scripts`, {stdio: 'inherit'});
} else {
execSync(`npm install ${installPackage} --save --save-exact --ignore-scripts`, {stdio: 'inherit'});
}
const templatePath = path.resolve(
'node_modules', templateName
);
copyProjectTemplateAndReplace(
templatePath,
destPath,
newProjectName,
{
// Every template contains a dummy package.json file included
// only for publishing the template to npm.
// We want to ignore this dummy file, otherwise it would overwrite
// our project's package.json file.
ignorePaths: ['package.json', 'dependencies.json'],
}
);
installTemplateDependencies(templatePath, yarnVersion);
} finally {
// Clean up the temp files
try {
if (yarnVersion) {
execSync(`yarn remove ${templateName} --ignore-scripts`);
} else {
execSync(`npm uninstall ${templateName} --ignore-scripts`);
}
} catch (err) {
// Not critical but we still want people to know and report
// if this the clean up fails.
console.warn(
`Failed to clean up template temp files in node_modules/${templateName}. ` +
'This is not a critical error, you can work on your app.'
);
}
}
}
function installTemplateDependencies(templatePath, yarnVersion) {
// dependencies.json is a special file that lists additional dependencies
// that are required by this template
const dependenciesJsonPath = path.resolve(
templatePath, 'dependencies.json'
);
console.log('Adding dependencies for the project...');
if (!fs.existsSync(dependenciesJsonPath)) {
console.log('No additional dependencies.');
return;
}
let dependencies;
try {
dependencies = JSON.parse(fs.readFileSync(dependenciesJsonPath));
} catch (err) {
throw new Error(
'Could not parse the template\'s dependencies.json: ' + err.message
);
}
for (let depName in dependencies) {
const depVersion = dependencies[depName];
const depToInstall = depName + '@' + depVersion;
console.log('Adding ' + depToInstall + '...');
if (yarnVersion) {
execSync(`yarn add ${depToInstall}`, {stdio: 'inherit'});
} else {
execSync(`npm install ${depToInstall} --save --save-exact`, {stdio: 'inherit'});
}
}
console.log('Linking native dependencies into the project\'s build files...');
execSync('react-native link', {stdio: 'inherit'});
}
module.exports = {
listTemplatesAndExit,
createProjectFromTemplate,
};