@salesforce/pwa-kit-dev 3.8.0-preview.0-basepath → 3.8.0-preview.2-basepath

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/configs/babel/babel-config.js +33 -0
  2. package/configs/eslint/README.md +21 -0
  3. package/configs/eslint/eslint-config.js +11 -0
  4. package/configs/eslint/index.js +11 -0
  5. package/configs/eslint/no-react.js +18 -0
  6. package/configs/eslint/partials/base.js +38 -0
  7. package/configs/eslint/partials/jest.js +24 -0
  8. package/configs/eslint/partials/react.js +29 -0
  9. package/configs/eslint/partials/typescript-permit-any.js +31 -0
  10. package/configs/eslint/partials/typescript.js +17 -0
  11. package/configs/eslint/recommended.js +20 -0
  12. package/configs/eslint/safe-types.js +20 -0
  13. package/configs/jest/jest-babel-transform.js +19 -0
  14. package/configs/jest/jest.config.js +40 -0
  15. package/configs/jest/mocks/fileMock.js +9 -0
  16. package/configs/jest/mocks/styleMock.js +9 -0
  17. package/configs/jest/mocks/svgMock.js +11 -0
  18. package/configs/webpack/config-names.js +19 -0
  19. package/configs/webpack/config.js +406 -0
  20. package/configs/webpack/overrides-plugin.js +168 -0
  21. package/configs/webpack/overrides-plugin.test.js +388 -0
  22. package/configs/webpack/plugins.js +86 -0
  23. package/configs/webpack/test/overrides/exists.js +7 -0
  24. package/configs/webpack/test/overrides/newExtension.js +7 -0
  25. package/configs/webpack/test/overrides/path/data.js +7 -0
  26. package/configs/webpack/test/overrides/path/index.js +7 -0
  27. package/configs/webpack/test/overrides/path/index.mock.js +7 -0
  28. package/configs/webpack/test/overrides/path/nested/icon.svg +0 -0
  29. package/configs/webpack/test/package.json +13 -0
  30. package/configs/webpack/utils.js +23 -0
  31. package/package.json +5 -4
  32. package/ssr/server/build-dev-server.js +468 -0
  33. package/ssr/server/build-dev-server.test.js +661 -0
  34. package/ssr/server/loading-screen/css/main.css +272 -0
  35. package/ssr/server/loading-screen/css/normalize.css +349 -0
  36. package/ssr/server/loading-screen/img/cloud-1.svg +1 -0
  37. package/ssr/server/loading-screen/img/cloud-2.svg +1 -0
  38. package/ssr/server/loading-screen/img/cloud-3.svg +1 -0
  39. package/ssr/server/loading-screen/img/cloud.svg +1 -0
  40. package/ssr/server/loading-screen/img/codey-arm.svg +1 -0
  41. package/ssr/server/loading-screen/img/codey-bear.svg +1 -0
  42. package/ssr/server/loading-screen/img/codey-bg.svg +1 -0
  43. package/ssr/server/loading-screen/img/codey-cloud.svg +1 -0
  44. package/ssr/server/loading-screen/img/codey-search.svg +1 -0
  45. package/ssr/server/loading-screen/img/codey.svg +1 -0
  46. package/ssr/server/loading-screen/img/codeyCarry.svg +1 -0
  47. package/ssr/server/loading-screen/img/devDocumentation.svg +1 -0
  48. package/ssr/server/loading-screen/img/devGithub.svg +1 -0
  49. package/ssr/server/loading-screen/img/devTrailhead.svg +1 -0
  50. package/ssr/server/loading-screen/img/logo.svg +1 -0
  51. package/ssr/server/loading-screen/img/slds_spinner_brand_9EA9F1.gif +0 -0
  52. package/ssr/server/loading-screen/index.html +129 -0
  53. package/ssr/server/test_fixtures/app/main.js +6 -0
  54. package/ssr/server/test_fixtures/app/static/favicon.ico +0 -0
  55. package/ssr/server/test_fixtures/localhost.pem +45 -0
  56. package/utils/mocks/dependency-tree-mock-data.js +127 -0
  57. package/utils/script-utils.js +497 -0
  58. package/utils/script-utils.test.js +496 -0
  59. package/utils/test-fixtures/minimal-built-app/ssr.js +9 -0
  60. package/utils/test-fixtures/minimal-built-app/static/favicon.ico +0 -0
@@ -0,0 +1,468 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.shouldCompress = exports.setLocalAssetHeaders = exports.makeErrorHandler = exports.DevServerMixin = exports.DevServerFactory = void 0;
7
+ var _compression = _interopRequireDefault(require("compression"));
8
+ var _express = _interopRequireDefault(require("express"));
9
+ var _path = _interopRequireDefault(require("path"));
10
+ var _fs = _interopRequireDefault(require("fs"));
11
+ var _https = _interopRequireDefault(require("https"));
12
+ var _http = _interopRequireDefault(require("http"));
13
+ var _mimeTypes = _interopRequireDefault(require("mime-types"));
14
+ var _webpack = _interopRequireDefault(require("webpack"));
15
+ var _webpackDevMiddleware = _interopRequireDefault(require("webpack-dev-middleware"));
16
+ var _webpackHotServerMiddleware = _interopRequireDefault(require("webpack-hot-server-middleware"));
17
+ var _webpackHotMiddleware = _interopRequireDefault(require("webpack-hot-middleware"));
18
+ var _open = _interopRequireDefault(require("open"));
19
+ var _requireFromString = _interopRequireDefault(require("require-from-string"));
20
+ var _buildRemoteServer = require("@salesforce/pwa-kit-runtime/ssr/server/build-remote-server");
21
+ var _ssrShared = require("@salesforce/pwa-kit-runtime/utils/ssr-shared");
22
+ var _ssrPaths = require("@salesforce/pwa-kit-runtime/utils/ssr-paths");
23
+ var _configNames = require("../../configs/webpack/config-names");
24
+ var _crypto = require("crypto");
25
+ var _chalk = _interopRequireDefault(require("chalk"));
26
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
27
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /*
28
+ * Copyright (c) 2022, Salesforce, Inc.
29
+ * All rights reserved.
30
+ * SPDX-License-Identifier: BSD-3-Clause
31
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
32
+ */
33
+ const CONTENT_TYPE = 'content-type';
34
+ const CONTENT_ENCODING = 'content-encoding';
35
+ const NO_CACHE = 'max-age=0, nocache, nostore, must-revalidate';
36
+
37
+ /**
38
+ * @private
39
+ */
40
+ const DevServerMixin = exports.DevServerMixin = {
41
+ /**
42
+ * @private
43
+ */
44
+ _logStartupMessage(options) {
45
+ console.log(`Starting the DevServer on ${_chalk.default.cyan(this._getDevServerURL(options))}`);
46
+ },
47
+ /**
48
+ * @private
49
+ */
50
+ _getAllowCookies(options) {
51
+ return 'MRT_ALLOW_COOKIES' in process.env ? process.env.MRT_ALLOW_COOKIES === 'true' : options.localAllowCookies;
52
+ },
53
+ /**
54
+ * @private
55
+ */
56
+ _getProtocol(options) {
57
+ return process.env.DEV_SERVER_PROTOCOL || options.protocol;
58
+ },
59
+ /**
60
+ * @private
61
+ */
62
+
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ _getDefaultCacheControl(options) {
65
+ return NO_CACHE;
66
+ },
67
+ /**
68
+ * @private
69
+ */
70
+ _strictSSL(options) {
71
+ return options.strictSSL;
72
+ },
73
+ /**
74
+ * Since dev server does not have access to apiGateway event object,
75
+ * here we generate an uuid and assign it under locals
76
+ * @private
77
+ */
78
+ _setRequestId(app) {
79
+ app.use((req, res, next) => {
80
+ res.locals.requestId = (0, _crypto.randomUUID)();
81
+ next();
82
+ });
83
+ },
84
+ /**
85
+ * @private
86
+ */
87
+ _setCompression(app) {
88
+ app.use((0, _compression.default)({
89
+ level: 9,
90
+ filter: shouldCompress
91
+ }));
92
+ },
93
+ /**
94
+ * @private
95
+ */
96
+ _setupMetricsFlushing(app) {
97
+ // Flush metrics at the end of sending. We do this here to
98
+ // keep the code paths consistent between local and remote
99
+ // servers. For the remote server, the flushing is done
100
+ // by the Lambda integration.
101
+ app.use((req, res, next) => {
102
+ res.on('finish', () => app.metrics.flush());
103
+ next();
104
+ });
105
+ },
106
+ /**
107
+ * @private
108
+ */
109
+
110
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
111
+ _setupProxying(app, options) {
112
+ _ssrShared.proxyConfigs.forEach(config => {
113
+ app.use(config.proxyPath, config.proxy);
114
+ app.use(config.cachingPath, config.cachingProxy);
115
+ });
116
+ },
117
+ /**
118
+ * @private
119
+ */
120
+
121
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
122
+ _handleMissingSlasPrivateEnvVar(app) {
123
+ throw new Error(`Server cannot start. Environment variable PWA_KIT_SLAS_CLIENT_SECRET not set. Please set this environment variable to proceed.`);
124
+ },
125
+ /**
126
+ * @private
127
+ */
128
+ _addSDKInternalHandlers(app) {
129
+ // This is separated out because these routes must not have our SSR middleware applied to them.
130
+ // But the SSR render function must!
131
+
132
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
133
+ let config = require('../../configs/webpack/config');
134
+ const projectWebpackPath = _path.default.resolve(app.options.projectDir, 'webpack.config.js');
135
+ if (_fs.default.existsSync(projectWebpackPath)) {
136
+ config = require(projectWebpackPath);
137
+ }
138
+ app.__compiler = (0, _webpack.default)(config);
139
+ app.__devMiddleware = (0, _webpackDevMiddleware.default)(app.__compiler, {
140
+ serverSideRender: true
141
+ });
142
+ app.__isInitialBuild = true;
143
+ app.__webpackReady = () => Boolean(app.__devMiddleware.context.state);
144
+ app.__devMiddleware.waitUntilValid(() => {
145
+ app.__isInitialBuild = false;
146
+ // Be just a little more generous before letting eg. Lighthouse hit it!
147
+ setTimeout(() => {
148
+ console.log(_chalk.default.cyan('First build complete'));
149
+ }, 75);
150
+ });
151
+ if (config.some(cnf => cnf.name === _configNames.SERVER)) {
152
+ app.__hotServerMiddleware = (0, _webpackHotServerMiddleware.default)(app.__compiler);
153
+ }
154
+ app.use(`${(0, _ssrPaths.getBundlePath)()}/development`, app.__devMiddleware);
155
+ app.__hmrMiddleware = (_, res) => res.status(501).send('Hot Module Reloading is disabled.');
156
+ const clientCompiler = app.__compiler.compilers.find(compiler => compiler.name === _configNames.CLIENT);
157
+ if (clientCompiler && process.env.HMR !== 'false') {
158
+ app.__hmrMiddleware = (0, _webpackHotMiddleware.default)(clientCompiler, {
159
+ path: '/'
160
+ }); // path is relative to the endpoint the middleware is attached to
161
+ }
162
+ app.use('/__mrt/hmr', app.__hmrMiddleware);
163
+ app.use('/__mrt/status', (req, res) => {
164
+ return res.json({
165
+ ready: app.__webpackReady()
166
+ });
167
+ });
168
+ app.use('/__mrt/loading-screen/', _express.default.static(_path.default.resolve(__dirname, 'loading-screen'), {
169
+ dotFiles: 'deny'
170
+ }));
171
+ app.get('/__mrt/clear-browser-data', (_, res) => {
172
+ console.log(_chalk.default.cyan('Clearing browser data'), '(cache, service worker, web storage for browsers supporting Clear-Site-Data header)');
173
+ console.log('For more info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#browser_compatibility');
174
+ console.log('');
175
+
176
+ // Note: this header value needs the double quotes.
177
+ res.set('Clear-Site-Data', '"cache", "storage"');
178
+ res.send();
179
+ });
180
+ },
181
+ /**
182
+ * @private
183
+ */
184
+ _addStaticAssetServing(app) {
185
+ // Proxy bundle asset requests to the local
186
+ // build directory.
187
+ app.use(`${(0, _ssrPaths.getBundlePath)()}/development`, _express.default.static(_path.default.resolve(process.cwd(), 'src'), {
188
+ dotFiles: 'deny',
189
+ setHeaders: setLocalAssetHeaders,
190
+ fallthrough: true
191
+ }));
192
+ },
193
+ /**
194
+ * @private
195
+ */
196
+ _addDevServerGarbageCollection(app) {
197
+ app.use((req, res, next) => {
198
+ const done = () => {
199
+ // We collect garbage because when a Lambda environment is
200
+ // re-used, we want to start with minimal memory usage. This
201
+ // call typically takes less than 100mS, and can dramatically
202
+ // reduce memory usage, so we accept the runtime cost.
203
+ // For the local dev server, we do this now. For a remote
204
+ // server, we use a different strategy (see createLambdaHandler).
205
+ req.app._collectGarbage();
206
+ };
207
+ res.on('finish', done);
208
+ res.on('close', done);
209
+ next();
210
+ });
211
+ },
212
+ serveStaticFile(filePath, opts = {}) {
213
+ // Warning: Ugly part of the Bundle spec that we need to maintain.
214
+ //
215
+ // This function assumes that an SDK build step will copy all
216
+ // non-webpacked assets from the 'app' dir to the 'build' dir.
217
+ //
218
+ // If you look carefully through the history, this has never
219
+ // been true though – assets get copied from app/static to
220
+ // build/static but this isn't really clear from the API.
221
+ //
222
+ // To see where those assets get copied, see here:
223
+ //
224
+ // packages/pwa-kit-dev/src/configs/webpack/config.js
225
+ //
226
+ // We have plans to make a robust Bundle spec in 246!
227
+ //
228
+ // Discussion here:
229
+ //
230
+ // https://salesforce-internal.slack.com/archives/C8YDDMKFZ/p1677793769255659?thread_ts=1677791840.174309&cid=C8YDDMKFZ
231
+
232
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
233
+ const pkg = require(_path.default.resolve(process.cwd(), 'package.json'));
234
+ return (req, res) => {
235
+ var _pkg$ccExtensibility, _pkg$ccExtensibility2, _pkg$ccExtensibility3;
236
+ const baseDir = _path.default.resolve(req.app.options.projectDir, pkg !== null && pkg !== void 0 && (_pkg$ccExtensibility = pkg.ccExtensibility) !== null && _pkg$ccExtensibility !== void 0 && _pkg$ccExtensibility.overridesDir ? pkg === null || pkg === void 0 ? void 0 : (_pkg$ccExtensibility2 = pkg.ccExtensibility) === null || _pkg$ccExtensibility2 === void 0 ? void 0 : (_pkg$ccExtensibility3 = _pkg$ccExtensibility2.overridesDir) === null || _pkg$ccExtensibility3 === void 0 ? void 0 : _pkg$ccExtensibility3.replace(/^\//, '') : '', 'app');
237
+ return this._serveStaticFile(req, res, baseDir, filePath, opts);
238
+ };
239
+ },
240
+ serveServiceWorker(req, res) {
241
+ req.app.__devMiddleware.waitUntilValid(() => {
242
+ const sourceMap = req.path.endsWith('.map');
243
+ const file = sourceMap ? 'worker.js.map' : 'worker.js';
244
+ const type = sourceMap ? '.js.map' : '.js';
245
+ const content = DevServerFactory._getWebpackAsset(req, _configNames.CLIENT_OPTIONAL, file);
246
+ if (content === null) {
247
+ // Service worker does not exist. Reminder that SW is optional for MRT apps.
248
+ res.sendStatus(404);
249
+ } else {
250
+ res.type(type);
251
+ res.send(content);
252
+ }
253
+ });
254
+ },
255
+ render(req, res, next) {
256
+ const app = req.app;
257
+ if (app !== null && app !== void 0 && app.__isInitialBuild) {
258
+ this._redirectToLoadingScreen(req, res, next);
259
+ } else {
260
+ // Ensure that we do not try to render anything until the webpack bundle is valid.
261
+ // There was a bug previously where developers would refresh the page while webpack was building,
262
+ // causing them to get redirected to the loading page and sometimes getting stuck,
263
+ // requiring them to restart their dev server
264
+ app.__devMiddleware.waitUntilValid(() => {
265
+ app.__hotServerMiddleware(req, res, next);
266
+ });
267
+ }
268
+ },
269
+ /**
270
+ * @private
271
+ */
272
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
273
+ _redirectToLoadingScreen(req, res, next) {
274
+ const path = encodeURIComponent(req.originalUrl);
275
+ res.redirect(`/__mrt/loading-screen/index.html?loading=1&path=${path}`);
276
+ },
277
+ /**
278
+ * @private
279
+ */
280
+ _getDevServerHostAndPort(options) {
281
+ const split = options.devServerHostName.split(':');
282
+ const hostname = split.length === 2 ? split[0] : options.devServerHostName;
283
+ const port = split.length === 2 ? split[1] : options.port;
284
+ return {
285
+ hostname,
286
+ port
287
+ };
288
+ },
289
+ /**
290
+ * @private
291
+ */
292
+ _getDevServerURL(options) {
293
+ const {
294
+ protocol
295
+ } = options;
296
+ const {
297
+ hostname,
298
+ port
299
+ } = this._getDevServerHostAndPort(options);
300
+ return `${protocol}://${hostname}:${port}`;
301
+ },
302
+ /**
303
+ * @private
304
+ */
305
+ _createHandler(app) {
306
+ const {
307
+ protocol,
308
+ sslFilePath
309
+ } = app.options;
310
+ const {
311
+ hostname,
312
+ port
313
+ } = this._getDevServerHostAndPort(app.options);
314
+ let server;
315
+ if (protocol === 'https') {
316
+ const sslFile = _fs.default.readFileSync(sslFilePath);
317
+ server = _https.default.createServer({
318
+ key: sslFile,
319
+ cert: sslFile
320
+ }, app);
321
+ } else {
322
+ server = _http.default.createServer(app);
323
+ }
324
+ server.on('error', makeErrorHandler(process, server, console.log));
325
+ server.on('close', () => app.applicationCache.close());
326
+ server.listen({
327
+ hostname,
328
+ port
329
+ }, () => {
330
+ /* istanbul ignore next */
331
+ if (process.env.NODE_ENV !== 'test') {
332
+ (0, _open.default)(`${this._getDevServerURL(app.options)}/__mrt/loading-screen/index.html?loading=1`);
333
+ }
334
+ });
335
+ return {
336
+ handler: undefined,
337
+ server,
338
+ app
339
+ };
340
+ },
341
+ /**
342
+ * Load any request processor code to emulate in the dev server the code
343
+ * that would run on a Lambda Edge function.
344
+ *
345
+ * @private
346
+ */
347
+ _getRequestProcessor(req) {
348
+ const compiled = this._getWebpackAsset(req, _configNames.REQUEST_PROCESSOR, 'request-processor.js');
349
+ if (compiled) {
350
+ const module = (0, _requireFromString.default)(compiled);
351
+ if (!module.processRequest) {
352
+ throw new Error(`Request processor module "request-processor.js" does not export processRequest`);
353
+ }
354
+ return module;
355
+ } else {
356
+ return null;
357
+ }
358
+ },
359
+ /**
360
+ * Return the compiled source for a webpack asset as a string.
361
+ *
362
+ * @param req
363
+ * @param compilerName
364
+ * @param fileName
365
+ * @returns {null|String}
366
+ * @private
367
+ */
368
+ _getWebpackAsset(req, compilerName, fileName) {
369
+ if (req.app.__webpackReady()) {
370
+ const outputFileSystem = req.app.__devMiddleware.context.outputFileSystem;
371
+ // Projects may have a large amount of stats data to process that can lead to performance issues
372
+ // we pass in options that help prevent that - preset: 'none' processes no data (TODO: make configurable)
373
+ const jsonWebpackStats = req.app.__devMiddleware.context.stats.toJson({
374
+ preset: 'none',
375
+ outputPath: true
376
+ });
377
+ try {
378
+ const rp = jsonWebpackStats.children.find(child => child.name === compilerName);
379
+ const assetPath = _path.default.join(rp.outputPath, fileName);
380
+ return outputFileSystem.readFileSync(assetPath, 'utf-8');
381
+ } catch (e) {
382
+ // The file doesn't exist – this is fine, many are optional
383
+ return null;
384
+ }
385
+ } else {
386
+ // The file isn't compiled yet
387
+ return null;
388
+ }
389
+ }
390
+ };
391
+
392
+ /**
393
+ * Set the headers for a bundle asset. This is used only in local
394
+ * dev server mode.
395
+ *
396
+ * @private
397
+ * @param res - the response object
398
+ * @param assetPath - the path to the asset file (with no query string
399
+ * or other URL elements)
400
+ */
401
+ const setLocalAssetHeaders = (res, assetPath) => {
402
+ const base = _path.default.basename(assetPath);
403
+ const contentType = _mimeTypes.default.lookup(base);
404
+ res.set(CONTENT_TYPE, contentType); // || 'application/octet-stream'
405
+
406
+ // Stat the file and return the last-modified Date
407
+ // in RFC1123 format. Also use that value as the ETag
408
+ // and Last-Modified
409
+ const mtime = _fs.default.statSync(assetPath).mtime;
410
+ const mtimeRFC1123 = mtime.toUTCString();
411
+ res.set('date', mtimeRFC1123);
412
+ res.set('last-modified', mtimeRFC1123);
413
+ res.set('etag', mtime.getTime());
414
+
415
+ // We don't cache local bundle assets
416
+ res.set('cache-control', NO_CACHE);
417
+ };
418
+
419
+ /**
420
+ * Crash the app with a user-friendly message when the port is already in use.
421
+ *
422
+ * @private
423
+ * @param {*} proc - Node's process module
424
+ * @param {*} server - the server to attach the listener to
425
+ * @param {*} log - logging function
426
+ */
427
+ exports.setLocalAssetHeaders = setLocalAssetHeaders;
428
+ const makeErrorHandler = (proc, server, log) => {
429
+ return e => {
430
+ if (e.code === 'EADDRINUSE') {
431
+ log(`This port is already being used by another process.`);
432
+ server.close();
433
+ proc.exit(2);
434
+ }
435
+ };
436
+ };
437
+
438
+ /**
439
+ * Filter function for compression module.
440
+ *
441
+ * @private
442
+ * @param req {IncomingMessage} ExpressJS Request
443
+ * @param res {ServerResponse} ExpressJS Response
444
+ * @returns {Boolean}
445
+ */
446
+ exports.makeErrorHandler = makeErrorHandler;
447
+ const shouldCompress = (req, res) => {
448
+ // If there is already a CONTENT_ENCODING header, then we
449
+ // do not apply any compression. This allows project code
450
+ // to handle encoding, if required.
451
+ if (res.getHeader(CONTENT_ENCODING)) {
452
+ // Set a flag on the response so that the persistent cache logic
453
+ // can tell there was already a content-encoding header.
454
+ res.locals.contentEncodingSet = true;
455
+ return false;
456
+ }
457
+
458
+ // Let the compression module make the decision about compressing.
459
+ // Even if we return true here, the module may still choose
460
+ // not to compress the data.
461
+ return _compression.default.filter(req, res);
462
+ };
463
+
464
+ /**
465
+ * @private
466
+ */
467
+ exports.shouldCompress = shouldCompress;
468
+ const DevServerFactory = exports.DevServerFactory = _extends({}, _buildRemoteServer.RemoteServerFactory, DevServerMixin);