react-zutils 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of react-zutils might be problematic. Click here for more details.
- package/FileSizeReporter.js +156 -0
- package/README.md +8 -0
- package/WebpackDevServerUtils.js +500 -0
- package/chalk.js +12 -0
- package/checkRequiredFiles.js +32 -0
- package/errorOverlayMiddleware.js +23 -0
- package/eslintFormatter.js +111 -0
- package/formatWebpackMessages.js +116 -0
- package/getCSSModuleLocalIdent.js +40 -0
- package/getProcessForPort.js +85 -0
- package/getPublicUrlOrPath.js +65 -0
- package/globby.js +12 -0
- package/immer.js +12 -0
- package/index.d.ts +3 -0
- package/index.js +5 -0
- package/launch.js +14 -0
- package/launchEditor.js +399 -0
- package/noopServiceWorkerMiddleware.js +40 -0
- package/package.json +39 -0
- package/printBuildError.js +45 -0
- package/printHostingInstructions.js +128 -0
- package/redirectServedPathMiddleware.js +26 -0
- package/setup-script.js +1 -0
- package/typescriptFormatter.js +45 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
|
8
|
+
'use strict';
|
9
|
+
|
10
|
+
var fs = require('fs');
|
11
|
+
var path = require('path');
|
12
|
+
var chalk = require('chalk');
|
13
|
+
var filesize = require('filesize');
|
14
|
+
var recursive = require('recursive-readdir');
|
15
|
+
var stripAnsi = require('strip-ansi');
|
16
|
+
var gzipSize = require('gzip-size').sync;
|
17
|
+
|
18
|
+
function canReadAsset(asset) {
|
19
|
+
return (
|
20
|
+
/\.(js|css)$/.test(asset) &&
|
21
|
+
!/service-worker\.js/.test(asset) &&
|
22
|
+
!/precache-manifest\.[0-9a-f]+\.js/.test(asset)
|
23
|
+
);
|
24
|
+
}
|
25
|
+
|
26
|
+
// Prints a detailed summary of build files.
|
27
|
+
function printFileSizesAfterBuild(
|
28
|
+
webpackStats,
|
29
|
+
previousSizeMap,
|
30
|
+
buildFolder,
|
31
|
+
maxBundleGzipSize,
|
32
|
+
maxChunkGzipSize
|
33
|
+
) {
|
34
|
+
var root = previousSizeMap.root;
|
35
|
+
var sizes = previousSizeMap.sizes;
|
36
|
+
var assets = (webpackStats.stats || [webpackStats])
|
37
|
+
.map(stats =>
|
38
|
+
stats
|
39
|
+
.toJson({ all: false, assets: true })
|
40
|
+
.assets.filter(asset => canReadAsset(asset.name))
|
41
|
+
.map(asset => {
|
42
|
+
var fileContents = fs.readFileSync(path.join(root, asset.name));
|
43
|
+
var size = gzipSize(fileContents);
|
44
|
+
var previousSize = sizes[removeFileNameHash(root, asset.name)];
|
45
|
+
var difference = getDifferenceLabel(size, previousSize);
|
46
|
+
return {
|
47
|
+
folder: path.join(
|
48
|
+
path.basename(buildFolder),
|
49
|
+
path.dirname(asset.name)
|
50
|
+
),
|
51
|
+
name: path.basename(asset.name),
|
52
|
+
size: size,
|
53
|
+
sizeLabel:
|
54
|
+
filesize(size) + (difference ? ' (' + difference + ')' : ''),
|
55
|
+
};
|
56
|
+
})
|
57
|
+
)
|
58
|
+
.reduce((single, all) => all.concat(single), []);
|
59
|
+
assets.sort((a, b) => b.size - a.size);
|
60
|
+
var longestSizeLabelLength = Math.max.apply(
|
61
|
+
null,
|
62
|
+
assets.map(a => stripAnsi(a.sizeLabel).length)
|
63
|
+
);
|
64
|
+
var suggestBundleSplitting = false;
|
65
|
+
assets.forEach(asset => {
|
66
|
+
var sizeLabel = asset.sizeLabel;
|
67
|
+
var sizeLength = stripAnsi(sizeLabel).length;
|
68
|
+
if (sizeLength < longestSizeLabelLength) {
|
69
|
+
var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength);
|
70
|
+
sizeLabel += rightPadding;
|
71
|
+
}
|
72
|
+
var isMainBundle = asset.name.indexOf('main.') === 0;
|
73
|
+
var maxRecommendedSize = isMainBundle
|
74
|
+
? maxBundleGzipSize
|
75
|
+
: maxChunkGzipSize;
|
76
|
+
var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize;
|
77
|
+
if (isLarge && path.extname(asset.name) === '.js') {
|
78
|
+
suggestBundleSplitting = true;
|
79
|
+
}
|
80
|
+
console.log(
|
81
|
+
' ' +
|
82
|
+
(isLarge ? chalk.yellow(sizeLabel) : sizeLabel) +
|
83
|
+
' ' +
|
84
|
+
chalk.dim(asset.folder + path.sep) +
|
85
|
+
chalk.cyan(asset.name)
|
86
|
+
);
|
87
|
+
});
|
88
|
+
if (suggestBundleSplitting) {
|
89
|
+
console.log();
|
90
|
+
console.log(
|
91
|
+
chalk.yellow('The bundle size is significantly larger than recommended.')
|
92
|
+
);
|
93
|
+
console.log(
|
94
|
+
chalk.yellow(
|
95
|
+
'Consider reducing it with code splitting: https://goo.gl/9VhYWB'
|
96
|
+
)
|
97
|
+
);
|
98
|
+
console.log(
|
99
|
+
chalk.yellow(
|
100
|
+
'You can also analyze the project dependencies: https://goo.gl/LeUzfb'
|
101
|
+
)
|
102
|
+
);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
function removeFileNameHash(buildFolder, fileName) {
|
107
|
+
return fileName
|
108
|
+
.replace(buildFolder, '')
|
109
|
+
.replace(/\\/g, '/')
|
110
|
+
.replace(
|
111
|
+
/\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/,
|
112
|
+
(match, p1, p2, p3, p4) => p1 + p4
|
113
|
+
);
|
114
|
+
}
|
115
|
+
|
116
|
+
// Input: 1024, 2048
|
117
|
+
// Output: "(+1 KB)"
|
118
|
+
function getDifferenceLabel(currentSize, previousSize) {
|
119
|
+
var FIFTY_KILOBYTES = 1024 * 50;
|
120
|
+
var difference = currentSize - previousSize;
|
121
|
+
var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
|
122
|
+
if (difference >= FIFTY_KILOBYTES) {
|
123
|
+
return chalk.red('+' + fileSize);
|
124
|
+
} else if (difference < FIFTY_KILOBYTES && difference > 0) {
|
125
|
+
return chalk.yellow('+' + fileSize);
|
126
|
+
} else if (difference < 0) {
|
127
|
+
return chalk.green(fileSize);
|
128
|
+
} else {
|
129
|
+
return '';
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
function measureFileSizesBeforeBuild(buildFolder) {
|
134
|
+
return new Promise(resolve => {
|
135
|
+
recursive(buildFolder, (err, fileNames) => {
|
136
|
+
var sizes;
|
137
|
+
if (!err && fileNames) {
|
138
|
+
sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
|
139
|
+
var contents = fs.readFileSync(fileName);
|
140
|
+
var key = removeFileNameHash(buildFolder, fileName);
|
141
|
+
memo[key] = gzipSize(contents);
|
142
|
+
return memo;
|
143
|
+
}, {});
|
144
|
+
}
|
145
|
+
resolve({
|
146
|
+
root: buildFolder,
|
147
|
+
sizes: sizes || {},
|
148
|
+
});
|
149
|
+
});
|
150
|
+
});
|
151
|
+
}
|
152
|
+
|
153
|
+
module.exports = {
|
154
|
+
measureFileSizesBeforeBuild: measureFileSizesBeforeBuild,
|
155
|
+
printFileSizesAfterBuild: printFileSizesAfterBuild,
|
156
|
+
};
|
package/README.md
ADDED
@@ -0,0 +1,500 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
'use strict';
|
8
|
+
|
9
|
+
const address = require('address');
|
10
|
+
const fs = require('fs');
|
11
|
+
const path = require('path');
|
12
|
+
const url = require('url');
|
13
|
+
const chalk = require('chalk');
|
14
|
+
const detect = require('detect-port-alt');
|
15
|
+
const isRoot = require('is-root');
|
16
|
+
const prompts = require('prompts');
|
17
|
+
const clearConsole = require('./clearConsole');
|
18
|
+
const formatWebpackMessages = require('./formatWebpackMessages');
|
19
|
+
const getProcessForPort = require('./getProcessForPort');
|
20
|
+
const typescriptFormatter = require('./typescriptFormatter');
|
21
|
+
const forkTsCheckerWebpackPlugin = require('./ForkTsCheckerWebpackPlugin');
|
22
|
+
|
23
|
+
const isInteractive = process.stdout.isTTY;
|
24
|
+
|
25
|
+
function prepareUrls(protocol, host, port, pathname = '/') {
|
26
|
+
const formatUrl = hostname =>
|
27
|
+
url.format({
|
28
|
+
protocol,
|
29
|
+
hostname,
|
30
|
+
port,
|
31
|
+
pathname,
|
32
|
+
});
|
33
|
+
const prettyPrintUrl = hostname =>
|
34
|
+
url.format({
|
35
|
+
protocol,
|
36
|
+
hostname,
|
37
|
+
port: chalk.bold(port),
|
38
|
+
pathname,
|
39
|
+
});
|
40
|
+
|
41
|
+
const isUnspecifiedHost = host === '0.0.0.0' || host === '::';
|
42
|
+
let prettyHost, lanUrlForConfig, lanUrlForTerminal;
|
43
|
+
if (isUnspecifiedHost) {
|
44
|
+
prettyHost = 'localhost';
|
45
|
+
try {
|
46
|
+
// This can only return an IPv4 address
|
47
|
+
lanUrlForConfig = address.ip();
|
48
|
+
if (lanUrlForConfig) {
|
49
|
+
// Check if the address is a private ip
|
50
|
+
// https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
|
51
|
+
if (
|
52
|
+
/^10[.]|^172[.](1[6-9]|2[0-9]|3[0-1])[.]|^192[.]168[.]/.test(
|
53
|
+
lanUrlForConfig
|
54
|
+
)
|
55
|
+
) {
|
56
|
+
// Address is private, format it for later use
|
57
|
+
lanUrlForTerminal = prettyPrintUrl(lanUrlForConfig);
|
58
|
+
} else {
|
59
|
+
// Address is not private, so we will discard it
|
60
|
+
lanUrlForConfig = undefined;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
} catch (_e) {
|
64
|
+
// ignored
|
65
|
+
}
|
66
|
+
} else {
|
67
|
+
prettyHost = host;
|
68
|
+
}
|
69
|
+
const localUrlForTerminal = prettyPrintUrl(prettyHost);
|
70
|
+
const localUrlForBrowser = formatUrl(prettyHost);
|
71
|
+
return {
|
72
|
+
lanUrlForConfig,
|
73
|
+
lanUrlForTerminal,
|
74
|
+
localUrlForTerminal,
|
75
|
+
localUrlForBrowser,
|
76
|
+
};
|
77
|
+
}
|
78
|
+
|
79
|
+
function printInstructions(appName, urls, useYarn) {
|
80
|
+
console.log();
|
81
|
+
console.log(`You can now view ${chalk.bold(appName)} in the browser.`);
|
82
|
+
console.log();
|
83
|
+
|
84
|
+
if (urls.lanUrlForTerminal) {
|
85
|
+
console.log(
|
86
|
+
` ${chalk.bold('Local:')} ${urls.localUrlForTerminal}`
|
87
|
+
);
|
88
|
+
console.log(
|
89
|
+
` ${chalk.bold('On Your Network:')} ${urls.lanUrlForTerminal}`
|
90
|
+
);
|
91
|
+
} else {
|
92
|
+
console.log(` ${urls.localUrlForTerminal}`);
|
93
|
+
}
|
94
|
+
|
95
|
+
console.log();
|
96
|
+
console.log('Note that the development build is not optimized.');
|
97
|
+
console.log(
|
98
|
+
`To create a production build, use ` +
|
99
|
+
`${chalk.cyan(`${useYarn ? 'yarn' : 'npm run'} build`)}.`
|
100
|
+
);
|
101
|
+
console.log();
|
102
|
+
}
|
103
|
+
|
104
|
+
function createCompiler({
|
105
|
+
appName,
|
106
|
+
config,
|
107
|
+
devSocket,
|
108
|
+
urls,
|
109
|
+
useYarn,
|
110
|
+
useTypeScript,
|
111
|
+
tscCompileOnError,
|
112
|
+
webpack,
|
113
|
+
}) {
|
114
|
+
// "Compiler" is a low-level interface to webpack.
|
115
|
+
// It lets us listen to some events and provide our own custom messages.
|
116
|
+
let compiler;
|
117
|
+
try {
|
118
|
+
compiler = webpack(config);
|
119
|
+
} catch (err) {
|
120
|
+
console.log(chalk.red('Failed to compile.'));
|
121
|
+
console.log();
|
122
|
+
console.log(err.message || err);
|
123
|
+
console.log();
|
124
|
+
process.exit(1);
|
125
|
+
}
|
126
|
+
|
127
|
+
// "invalid" event fires when you have changed a file, and webpack is
|
128
|
+
// recompiling a bundle. WebpackDevServer takes care to pause serving the
|
129
|
+
// bundle, so if you refresh, it'll wait instead of serving the old one.
|
130
|
+
// "invalid" is short for "bundle invalidated", it doesn't imply any errors.
|
131
|
+
compiler.hooks.invalid.tap('invalid', () => {
|
132
|
+
if (isInteractive) {
|
133
|
+
clearConsole();
|
134
|
+
}
|
135
|
+
console.log('Compiling...');
|
136
|
+
});
|
137
|
+
|
138
|
+
let isFirstCompile = true;
|
139
|
+
let tsMessagesPromise;
|
140
|
+
let tsMessagesResolver;
|
141
|
+
|
142
|
+
if (useTypeScript) {
|
143
|
+
compiler.hooks.beforeCompile.tap('beforeCompile', () => {
|
144
|
+
tsMessagesPromise = new Promise(resolve => {
|
145
|
+
tsMessagesResolver = msgs => resolve(msgs);
|
146
|
+
});
|
147
|
+
});
|
148
|
+
|
149
|
+
forkTsCheckerWebpackPlugin
|
150
|
+
.getCompilerHooks(compiler)
|
151
|
+
.receive.tap('afterTypeScriptCheck', (diagnostics, lints) => {
|
152
|
+
const allMsgs = [...diagnostics, ...lints];
|
153
|
+
const format = message =>
|
154
|
+
`${message.file}\n${typescriptFormatter(message, true)}`;
|
155
|
+
|
156
|
+
tsMessagesResolver({
|
157
|
+
errors: allMsgs.filter(msg => msg.severity === 'error').map(format),
|
158
|
+
warnings: allMsgs
|
159
|
+
.filter(msg => msg.severity === 'warning')
|
160
|
+
.map(format),
|
161
|
+
});
|
162
|
+
});
|
163
|
+
}
|
164
|
+
|
165
|
+
// "done" event fires when webpack has finished recompiling the bundle.
|
166
|
+
// Whether or not you have warnings or errors, you will get this event.
|
167
|
+
compiler.hooks.done.tap('done', async stats => {
|
168
|
+
if (isInteractive) {
|
169
|
+
clearConsole();
|
170
|
+
}
|
171
|
+
|
172
|
+
// We have switched off the default webpack output in WebpackDevServer
|
173
|
+
// options so we are going to "massage" the warnings and errors and present
|
174
|
+
// them in a readable focused way.
|
175
|
+
// We only construct the warnings and errors for speed:
|
176
|
+
// https://github.com/facebook/create-react-app/issues/4492#issuecomment-421959548
|
177
|
+
const statsData = stats.toJson({
|
178
|
+
all: false,
|
179
|
+
warnings: true,
|
180
|
+
errors: true,
|
181
|
+
});
|
182
|
+
|
183
|
+
if (useTypeScript && statsData.errors.length === 0) {
|
184
|
+
const delayedMsg = setTimeout(() => {
|
185
|
+
console.log(
|
186
|
+
chalk.yellow(
|
187
|
+
'Files successfully emitted, waiting for typecheck results...'
|
188
|
+
)
|
189
|
+
);
|
190
|
+
}, 100);
|
191
|
+
|
192
|
+
const messages = await tsMessagesPromise;
|
193
|
+
clearTimeout(delayedMsg);
|
194
|
+
if (tscCompileOnError) {
|
195
|
+
statsData.warnings.push(...messages.errors);
|
196
|
+
} else {
|
197
|
+
statsData.errors.push(...messages.errors);
|
198
|
+
}
|
199
|
+
statsData.warnings.push(...messages.warnings);
|
200
|
+
|
201
|
+
// Push errors and warnings into compilation result
|
202
|
+
// to show them after page refresh triggered by user.
|
203
|
+
if (tscCompileOnError) {
|
204
|
+
stats.compilation.warnings.push(...messages.errors);
|
205
|
+
} else {
|
206
|
+
stats.compilation.errors.push(...messages.errors);
|
207
|
+
}
|
208
|
+
stats.compilation.warnings.push(...messages.warnings);
|
209
|
+
|
210
|
+
if (messages.errors.length > 0) {
|
211
|
+
if (tscCompileOnError) {
|
212
|
+
devSocket.warnings(messages.errors);
|
213
|
+
} else {
|
214
|
+
devSocket.errors(messages.errors);
|
215
|
+
}
|
216
|
+
} else if (messages.warnings.length > 0) {
|
217
|
+
devSocket.warnings(messages.warnings);
|
218
|
+
}
|
219
|
+
|
220
|
+
if (isInteractive) {
|
221
|
+
clearConsole();
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
const messages = formatWebpackMessages(statsData);
|
226
|
+
const isSuccessful = !messages.errors.length && !messages.warnings.length;
|
227
|
+
if (isSuccessful) {
|
228
|
+
console.log(chalk.green('Compiled successfully!'));
|
229
|
+
}
|
230
|
+
if (isSuccessful && (isInteractive || isFirstCompile)) {
|
231
|
+
printInstructions(appName, urls, useYarn);
|
232
|
+
}
|
233
|
+
isFirstCompile = false;
|
234
|
+
|
235
|
+
// If errors exist, only show errors.
|
236
|
+
if (messages.errors.length) {
|
237
|
+
// Only keep the first error. Others are often indicative
|
238
|
+
// of the same problem, but confuse the reader with noise.
|
239
|
+
if (messages.errors.length > 1) {
|
240
|
+
messages.errors.length = 1;
|
241
|
+
}
|
242
|
+
console.log(chalk.red('Failed to compile.\n'));
|
243
|
+
console.log(messages.errors.join('\n\n'));
|
244
|
+
return;
|
245
|
+
}
|
246
|
+
|
247
|
+
// Show warnings if no errors were found.
|
248
|
+
if (messages.warnings.length) {
|
249
|
+
console.log(chalk.yellow('Compiled with warnings.\n'));
|
250
|
+
console.log(messages.warnings.join('\n\n'));
|
251
|
+
|
252
|
+
// Teach some ESLint tricks.
|
253
|
+
console.log(
|
254
|
+
'\nSearch for the ' +
|
255
|
+
chalk.underline(chalk.yellow('keywords')) +
|
256
|
+
' to learn more about each warning.'
|
257
|
+
);
|
258
|
+
console.log(
|
259
|
+
'To ignore, add ' +
|
260
|
+
chalk.cyan('// eslint-disable-next-line') +
|
261
|
+
' to the line before.\n'
|
262
|
+
);
|
263
|
+
}
|
264
|
+
});
|
265
|
+
|
266
|
+
// You can safely remove this after ejecting.
|
267
|
+
// We only use this block for testing of Create React App itself:
|
268
|
+
const isSmokeTest = process.argv.some(
|
269
|
+
arg => arg.indexOf('--smoke-test') > -1
|
270
|
+
);
|
271
|
+
if (isSmokeTest) {
|
272
|
+
compiler.hooks.failed.tap('smokeTest', async () => {
|
273
|
+
await tsMessagesPromise;
|
274
|
+
process.exit(1);
|
275
|
+
});
|
276
|
+
compiler.hooks.done.tap('smokeTest', async stats => {
|
277
|
+
await tsMessagesPromise;
|
278
|
+
if (stats.hasErrors() || stats.hasWarnings()) {
|
279
|
+
process.exit(1);
|
280
|
+
} else {
|
281
|
+
process.exit(0);
|
282
|
+
}
|
283
|
+
});
|
284
|
+
}
|
285
|
+
|
286
|
+
return compiler;
|
287
|
+
}
|
288
|
+
|
289
|
+
function resolveLoopback(proxy) {
|
290
|
+
const o = url.parse(proxy);
|
291
|
+
o.host = undefined;
|
292
|
+
if (o.hostname !== 'localhost') {
|
293
|
+
return proxy;
|
294
|
+
}
|
295
|
+
// Unfortunately, many languages (unlike node) do not yet support IPv6.
|
296
|
+
// This means even though localhost resolves to ::1, the application
|
297
|
+
// must fall back to IPv4 (on 127.0.0.1).
|
298
|
+
// We can re-enable this in a few years.
|
299
|
+
/*try {
|
300
|
+
o.hostname = address.ipv6() ? '::1' : '127.0.0.1';
|
301
|
+
} catch (_ignored) {
|
302
|
+
o.hostname = '127.0.0.1';
|
303
|
+
}*/
|
304
|
+
|
305
|
+
try {
|
306
|
+
// Check if we're on a network; if we are, chances are we can resolve
|
307
|
+
// localhost. Otherwise, we can just be safe and assume localhost is
|
308
|
+
// IPv4 for maximum compatibility.
|
309
|
+
if (!address.ip()) {
|
310
|
+
o.hostname = '127.0.0.1';
|
311
|
+
}
|
312
|
+
} catch (_ignored) {
|
313
|
+
o.hostname = '127.0.0.1';
|
314
|
+
}
|
315
|
+
return url.format(o);
|
316
|
+
}
|
317
|
+
|
318
|
+
// We need to provide a custom onError function for httpProxyMiddleware.
|
319
|
+
// It allows us to log custom error messages on the console.
|
320
|
+
function onProxyError(proxy) {
|
321
|
+
return (err, req, res) => {
|
322
|
+
const host = req.headers && req.headers.host;
|
323
|
+
console.log(
|
324
|
+
chalk.red('Proxy error:') +
|
325
|
+
' Could not proxy request ' +
|
326
|
+
chalk.cyan(req.url) +
|
327
|
+
' from ' +
|
328
|
+
chalk.cyan(host) +
|
329
|
+
' to ' +
|
330
|
+
chalk.cyan(proxy) +
|
331
|
+
'.'
|
332
|
+
);
|
333
|
+
console.log(
|
334
|
+
'See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (' +
|
335
|
+
chalk.cyan(err.code) +
|
336
|
+
').'
|
337
|
+
);
|
338
|
+
console.log();
|
339
|
+
|
340
|
+
// And immediately send the proper error response to the client.
|
341
|
+
// Otherwise, the request will eventually timeout with ERR_EMPTY_RESPONSE on the client side.
|
342
|
+
if (res.writeHead && !res.headersSent) {
|
343
|
+
res.writeHead(500);
|
344
|
+
}
|
345
|
+
res.end(
|
346
|
+
'Proxy error: Could not proxy request ' +
|
347
|
+
req.url +
|
348
|
+
' from ' +
|
349
|
+
host +
|
350
|
+
' to ' +
|
351
|
+
proxy +
|
352
|
+
' (' +
|
353
|
+
err.code +
|
354
|
+
').'
|
355
|
+
);
|
356
|
+
};
|
357
|
+
}
|
358
|
+
|
359
|
+
function prepareProxy(proxy, appPublicFolder, servedPathname) {
|
360
|
+
// `proxy` lets you specify alternate servers for specific requests.
|
361
|
+
if (!proxy) {
|
362
|
+
return undefined;
|
363
|
+
}
|
364
|
+
if (typeof proxy !== 'string') {
|
365
|
+
console.log(
|
366
|
+
chalk.red('When specified, "proxy" in package.json must be a string.')
|
367
|
+
);
|
368
|
+
console.log(
|
369
|
+
chalk.red('Instead, the type of "proxy" was "' + typeof proxy + '".')
|
370
|
+
);
|
371
|
+
console.log(
|
372
|
+
chalk.red('Either remove "proxy" from package.json, or make it a string.')
|
373
|
+
);
|
374
|
+
process.exit(1);
|
375
|
+
}
|
376
|
+
|
377
|
+
// If proxy is specified, let it handle any request except for
|
378
|
+
// files in the public folder and requests to the WebpackDevServer socket endpoint.
|
379
|
+
// https://github.com/facebook/create-react-app/issues/6720
|
380
|
+
const sockPath = process.env.WDS_SOCKET_PATH || '/sockjs-node';
|
381
|
+
const isDefaultSockHost = !process.env.WDS_SOCKET_HOST;
|
382
|
+
function mayProxy(pathname) {
|
383
|
+
const maybePublicPath = path.resolve(
|
384
|
+
appPublicFolder,
|
385
|
+
pathname.replace(new RegExp('^' + servedPathname), '')
|
386
|
+
);
|
387
|
+
const isPublicFileRequest = fs.existsSync(maybePublicPath);
|
388
|
+
// used by webpackHotDevClient
|
389
|
+
const isWdsEndpointRequest =
|
390
|
+
isDefaultSockHost && pathname.startsWith(sockPath);
|
391
|
+
return !(isPublicFileRequest || isWdsEndpointRequest);
|
392
|
+
}
|
393
|
+
|
394
|
+
if (!/^http(s)?:\/\//.test(proxy)) {
|
395
|
+
console.log(
|
396
|
+
chalk.red(
|
397
|
+
'When "proxy" is specified in package.json it must start with either http:// or https://'
|
398
|
+
)
|
399
|
+
);
|
400
|
+
process.exit(1);
|
401
|
+
}
|
402
|
+
|
403
|
+
let target;
|
404
|
+
if (process.platform === 'win32') {
|
405
|
+
target = resolveLoopback(proxy);
|
406
|
+
} else {
|
407
|
+
target = proxy;
|
408
|
+
}
|
409
|
+
return [
|
410
|
+
{
|
411
|
+
target,
|
412
|
+
logLevel: 'silent',
|
413
|
+
// For single page apps, we generally want to fallback to /index.html.
|
414
|
+
// However we also want to respect `proxy` for API calls.
|
415
|
+
// So if `proxy` is specified as a string, we need to decide which fallback to use.
|
416
|
+
// We use a heuristic: We want to proxy all the requests that are not meant
|
417
|
+
// for static assets and as all the requests for static assets will be using
|
418
|
+
// `GET` method, we can proxy all non-`GET` requests.
|
419
|
+
// For `GET` requests, if request `accept`s text/html, we pick /index.html.
|
420
|
+
// Modern browsers include text/html into `accept` header when navigating.
|
421
|
+
// However API calls like `fetch()` won’t generally accept text/html.
|
422
|
+
// If this heuristic doesn’t work well for you, use `src/setupProxy.js`.
|
423
|
+
context: function (pathname, req) {
|
424
|
+
return (
|
425
|
+
req.method !== 'GET' ||
|
426
|
+
(mayProxy(pathname) &&
|
427
|
+
req.headers.accept &&
|
428
|
+
req.headers.accept.indexOf('text/html') === -1)
|
429
|
+
);
|
430
|
+
},
|
431
|
+
onProxyReq: proxyReq => {
|
432
|
+
// Browsers may send Origin headers even with same-origin
|
433
|
+
// requests. To prevent CORS issues, we have to change
|
434
|
+
// the Origin to match the target URL.
|
435
|
+
if (proxyReq.getHeader('origin')) {
|
436
|
+
proxyReq.setHeader('origin', target);
|
437
|
+
}
|
438
|
+
},
|
439
|
+
onError: onProxyError(target),
|
440
|
+
secure: false,
|
441
|
+
changeOrigin: true,
|
442
|
+
ws: true,
|
443
|
+
xfwd: true,
|
444
|
+
},
|
445
|
+
];
|
446
|
+
}
|
447
|
+
|
448
|
+
function choosePort(host, defaultPort) {
|
449
|
+
return detect(defaultPort, host).then(
|
450
|
+
port =>
|
451
|
+
new Promise(resolve => {
|
452
|
+
if (port === defaultPort) {
|
453
|
+
return resolve(port);
|
454
|
+
}
|
455
|
+
const message =
|
456
|
+
process.platform !== 'win32' && defaultPort < 1024 && !isRoot()
|
457
|
+
? `Admin permissions are required to run a server on a port below 1024.`
|
458
|
+
: `Something is already running on port ${defaultPort}.`;
|
459
|
+
if (isInteractive) {
|
460
|
+
clearConsole();
|
461
|
+
const existingProcess = getProcessForPort(defaultPort);
|
462
|
+
const question = {
|
463
|
+
type: 'confirm',
|
464
|
+
name: 'shouldChangePort',
|
465
|
+
message:
|
466
|
+
chalk.yellow(
|
467
|
+
message +
|
468
|
+
`${existingProcess ? ` Probably:\n ${existingProcess}` : ''}`
|
469
|
+
) + '\n\nWould you like to run the app on another port instead?',
|
470
|
+
initial: true,
|
471
|
+
};
|
472
|
+
prompts(question).then(answer => {
|
473
|
+
if (answer.shouldChangePort) {
|
474
|
+
resolve(port);
|
475
|
+
} else {
|
476
|
+
resolve(null);
|
477
|
+
}
|
478
|
+
});
|
479
|
+
} else {
|
480
|
+
console.log(chalk.red(message));
|
481
|
+
resolve(null);
|
482
|
+
}
|
483
|
+
}),
|
484
|
+
err => {
|
485
|
+
throw new Error(
|
486
|
+
chalk.red(`Could not find an open port at ${chalk.bold(host)}.`) +
|
487
|
+
'\n' +
|
488
|
+
('Network error message: ' + err.message || err) +
|
489
|
+
'\n'
|
490
|
+
);
|
491
|
+
}
|
492
|
+
);
|
493
|
+
}
|
494
|
+
|
495
|
+
module.exports = {
|
496
|
+
choosePort,
|
497
|
+
createCompiler,
|
498
|
+
prepareProxy,
|
499
|
+
prepareUrls,
|
500
|
+
};
|
package/chalk.js
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
|
8
|
+
'use strict';
|
9
|
+
|
10
|
+
var chalk = require('chalk');
|
11
|
+
|
12
|
+
module.exports = chalk;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2015-present, Facebook, Inc.
|
3
|
+
*
|
4
|
+
* This source code is licensed under the MIT license found in the
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
6
|
+
*/
|
7
|
+
|
8
|
+
'use strict';
|
9
|
+
|
10
|
+
var fs = require('fs');
|
11
|
+
var path = require('path');
|
12
|
+
var chalk = require('chalk');
|
13
|
+
|
14
|
+
function checkRequiredFiles(files) {
|
15
|
+
var currentFilePath;
|
16
|
+
try {
|
17
|
+
files.forEach(filePath => {
|
18
|
+
currentFilePath = filePath;
|
19
|
+
fs.accessSync(filePath, fs.F_OK);
|
20
|
+
});
|
21
|
+
return true;
|
22
|
+
} catch (err) {
|
23
|
+
var dirName = path.dirname(currentFilePath);
|
24
|
+
var fileName = path.basename(currentFilePath);
|
25
|
+
console.log(chalk.red('Could not find a required file.'));
|
26
|
+
console.log(chalk.red(' Name: ') + chalk.cyan(fileName));
|
27
|
+
console.log(chalk.red(' Searched in: ') + chalk.cyan(dirName));
|
28
|
+
return false;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
module.exports = checkRequiredFiles;
|