@sasjs/cli 6.30.0 → 6.31.0
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.
- package/build/commands/build/internal/mockSite/assets/api-console.css +365 -0
- package/build/commands/build/internal/mockSite/assets/api-console.js +762 -0
- package/build/commands/build/internal/mockSite/assets/api-index.html +83 -0
- package/build/commands/build/internal/mockSite/assets/boot.js +23 -1
- package/build/commands/build/internal/mockSite/assets/persist.js +76 -5
- package/build/commands/build/internal/mockSite/assets/runtime-core.js +413 -50
- package/build/commands/build/internal/mockSite/assets/sw.js +156 -15
- package/build/commands/build/internal/mockSite/mockSite.d.ts +25 -0
- package/build/commands/build/internal/mockSite/mockSite.js +311 -24
- package/build/commands/build/internal/mockSite/spec/apiConsole.spec.d.ts +1 -0
- package/build/commands/build/internal/mockSite/spec/apiConsole.spec.js +1371 -0
- package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.d.ts +1 -0
- package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.js +303 -0
- package/build/commands/build/internal/mockSite/spec/boot.spec.js +50 -3
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +217 -3
- package/build/commands/build/internal/mockSite/spec/persist.spec.js +179 -1
- package/build/commands/build/internal/mockSite/spec/runtime-core.spec.js +546 -0
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +407 -53
- package/build/commands/version/spec/version.spec.js +11 -1
- package/build/commands/version/version.d.ts +5 -3
- package/build/commands/version/version.js +17 -7
- package/build/types/mockWebBasePath.d.ts +9 -0
- package/package.json +13 -32
|
@@ -79,7 +79,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
79
79
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
80
80
|
};
|
|
81
81
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
82
|
-
exports.DRIVE_FORMAT = exports.MAX_SEED_BYTES = exports.MAX_BINARY_SEED_BYTES = exports.buildLintBundle = exports.WEBOUT_SENTINELS = void 0;
|
|
82
|
+
exports.SHARD_GROUP_BYTES = exports.SHARD_FILE_BYTES = exports.DRIVE_FORMAT = exports.MAX_SEED_BYTES = exports.MAX_BINARY_SEED_BYTES = exports.buildLintBundle = exports.WEBOUT_SENTINELS = void 0;
|
|
83
83
|
exports.buildMockSite = buildMockSite;
|
|
84
84
|
var crypto_1 = require("crypto");
|
|
85
85
|
var fs_1 = __importDefault(require("fs"));
|
|
@@ -512,6 +512,137 @@ var countPrograms = function (files, extension) {
|
|
|
512
512
|
return inProgramFolder && key.toLowerCase().endsWith(extension);
|
|
513
513
|
}).length;
|
|
514
514
|
};
|
|
515
|
+
/**
|
|
516
|
+
* The size at which a captured file is fetched on its own rather than with its
|
|
517
|
+
* folder.
|
|
518
|
+
*
|
|
519
|
+
* A shard is a download the visitor waits for, and an app's folder holds both
|
|
520
|
+
* its bundles and the small files a listing shows - its logo, a config. Splitting
|
|
521
|
+
* on size keeps one from dragging the other: the tile asks for the folder's small
|
|
522
|
+
* files, the app's own page asks for the bundle.
|
|
523
|
+
*/
|
|
524
|
+
exports.SHARD_FILE_BYTES = 256 * 1024;
|
|
525
|
+
/**
|
|
526
|
+
* The size below which a folder's files ride in the boot shard.
|
|
527
|
+
*
|
|
528
|
+
* The boot shard is fetched before the first page renders, so it is paid by every
|
|
529
|
+
* visit. Folders under this size are cheaper there than as a request of their
|
|
530
|
+
* own; folders over it are only fetched when something reads them.
|
|
531
|
+
*/
|
|
532
|
+
exports.SHARD_GROUP_BYTES = 64 * 1024;
|
|
533
|
+
/** The folder a captured path is grouped under: its first three segments. */
|
|
534
|
+
var shardFolderFor = function (filePath) {
|
|
535
|
+
var prefix = '/sasjs/drive/files/';
|
|
536
|
+
if (!filePath.startsWith(prefix))
|
|
537
|
+
return path_1.default.dirname(filePath);
|
|
538
|
+
var parts = filePath.slice(prefix.length).split('/');
|
|
539
|
+
// a file at the drive root groups with the other root files, which is where
|
|
540
|
+
// the index, the linter rules and a project's configs live
|
|
541
|
+
return (prefix +
|
|
542
|
+
parts
|
|
543
|
+
.slice(0, Math.max(0, parts.length - 1))
|
|
544
|
+
.slice(0, 3)
|
|
545
|
+
.join('/'));
|
|
546
|
+
};
|
|
547
|
+
/**
|
|
548
|
+
* Splits a captured drive into a boot shard and the pieces fetched on demand.
|
|
549
|
+
*
|
|
550
|
+
* A site that ships its whole drive as one file makes every visitor download all
|
|
551
|
+
* of it before the first page renders - on a demonstration site that is every
|
|
552
|
+
* app's bundle, behind a list of tiles that names them. The split is by what a
|
|
553
|
+
* page reads, not by what the drive holds:
|
|
554
|
+
*
|
|
555
|
+
* - a file at or over `SHARD_FILE_BYTES` gets a shard of its own;
|
|
556
|
+
* - the rest are grouped by folder, and a folder under `SHARD_GROUP_BYTES` joins
|
|
557
|
+
* the boot shard (id 0), so no visit pays a round trip for a logo;
|
|
558
|
+
* - everything over it becomes a shard, fetched when a path in it is read.
|
|
559
|
+
*
|
|
560
|
+
* The index carries every path with its shard and byte count, so a listing, a
|
|
561
|
+
* stat and an existence check answer for the whole drive with nothing fetched.
|
|
562
|
+
*/
|
|
563
|
+
var shardSnapshot = function (files) {
|
|
564
|
+
var e_7, _a, e_8, _b, _c;
|
|
565
|
+
var paths = Object.keys(files).sort();
|
|
566
|
+
var bytesOf = function (filePath) {
|
|
567
|
+
return Buffer.byteLength(String(files[filePath]));
|
|
568
|
+
};
|
|
569
|
+
var groups = new Map();
|
|
570
|
+
try {
|
|
571
|
+
for (var paths_1 = __values(paths), paths_1_1 = paths_1.next(); !paths_1_1.done; paths_1_1 = paths_1.next()) {
|
|
572
|
+
var filePath = paths_1_1.value;
|
|
573
|
+
var big = bytesOf(filePath) >= exports.SHARD_FILE_BYTES;
|
|
574
|
+
var key = big ? "file:".concat(filePath) : "dir:".concat(shardFolderFor(filePath));
|
|
575
|
+
groups.set(key, (groups.get(key) || []).concat(filePath));
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
579
|
+
finally {
|
|
580
|
+
try {
|
|
581
|
+
if (paths_1_1 && !paths_1_1.done && (_a = paths_1.return)) _a.call(paths_1);
|
|
582
|
+
}
|
|
583
|
+
finally { if (e_7) throw e_7.error; }
|
|
584
|
+
}
|
|
585
|
+
var shards = [{ paths: [], bytes: 0 }];
|
|
586
|
+
try {
|
|
587
|
+
for (var _d = __values(__spreadArray([], __read(groups.keys()), false).sort()), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
588
|
+
var key = _e.value;
|
|
589
|
+
var members = groups.get(key);
|
|
590
|
+
var bytes = members.reduce(function (total, filePath) { return total + bytesOf(filePath); }, 0);
|
|
591
|
+
var ridesInBoot = key.startsWith('dir:') && bytes <= exports.SHARD_GROUP_BYTES;
|
|
592
|
+
if (ridesInBoot) {
|
|
593
|
+
(_c = shards[0].paths).push.apply(_c, __spreadArray([], __read(members), false));
|
|
594
|
+
shards[0].bytes += bytes;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
shards.push({ paths: members, bytes: bytes });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
catch (e_8_1) { e_8 = { error: e_8_1 }; }
|
|
601
|
+
finally {
|
|
602
|
+
try {
|
|
603
|
+
if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
|
|
604
|
+
}
|
|
605
|
+
finally { if (e_8) throw e_8.error; }
|
|
606
|
+
}
|
|
607
|
+
var entries = {};
|
|
608
|
+
shards.forEach(function (shard, id) {
|
|
609
|
+
var e_9, _a;
|
|
610
|
+
try {
|
|
611
|
+
for (var _b = __values(shard.paths), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
612
|
+
var filePath = _c.value;
|
|
613
|
+
entries[filePath] = { shard: id, bytes: bytesOf(filePath) };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
catch (e_9_1) { e_9 = { error: e_9_1 }; }
|
|
617
|
+
finally {
|
|
618
|
+
try {
|
|
619
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
620
|
+
}
|
|
621
|
+
finally { if (e_9) throw e_9.error; }
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
return {
|
|
625
|
+
index: {
|
|
626
|
+
format: exports.DRIVE_FORMAT,
|
|
627
|
+
sharded: true,
|
|
628
|
+
boot: 0,
|
|
629
|
+
files: paths.length,
|
|
630
|
+
entries: entries,
|
|
631
|
+
shards: shards.map(function (shard, id) { return ({
|
|
632
|
+
id: id,
|
|
633
|
+
file: "host/vfs-snapshot-".concat(id, ".json"),
|
|
634
|
+
bytes: shard.bytes,
|
|
635
|
+
files: shard.paths.length
|
|
636
|
+
}); })
|
|
637
|
+
},
|
|
638
|
+
shards: shards.map(function (shard, id) { return ({
|
|
639
|
+
id: id,
|
|
640
|
+
file: "vfs-snapshot-".concat(id, ".json"),
|
|
641
|
+
bytes: shard.bytes,
|
|
642
|
+
files: Object.fromEntries(shard.paths.map(function (filePath) { return [filePath, files[filePath]]; }))
|
|
643
|
+
}); })
|
|
644
|
+
};
|
|
645
|
+
};
|
|
515
646
|
/** The script-tag attributes that change how an app's script runs. */
|
|
516
647
|
var REPLAYED_ATTRIBUTES = {
|
|
517
648
|
type: 'type',
|
|
@@ -520,6 +651,20 @@ var REPLAYED_ATTRIBUTES = {
|
|
|
520
651
|
integrity: 'integrity',
|
|
521
652
|
nomodule: 'noModule'
|
|
522
653
|
};
|
|
654
|
+
/**
|
|
655
|
+
* Script types a preload may name.
|
|
656
|
+
*
|
|
657
|
+
* The loader only re-runs a tag once the worker controls the page, so without a
|
|
658
|
+
* hint the app's bundle is not requested until the install has finished - the
|
|
659
|
+
* largest asset on the site behind the largest prerequisite. A preload link
|
|
660
|
+
* executes nothing, so the download can start while the worker is still
|
|
661
|
+
* installing.
|
|
662
|
+
*
|
|
663
|
+
* `module` is excluded on purpose: a module is fetched with different
|
|
664
|
+
* credentials semantics from a classic script, so `as="script"` does not match
|
|
665
|
+
* the later tag and the file is fetched twice - slower than no hint at all.
|
|
666
|
+
*/
|
|
667
|
+
var PRELOADABLE_TYPES = ['', 'text/javascript', 'application/javascript'];
|
|
523
668
|
var parseAttributes = function (source) {
|
|
524
669
|
var _a, _b, _c;
|
|
525
670
|
var attributes = {};
|
|
@@ -538,6 +683,45 @@ var escapeAttribute = function (value) {
|
|
|
538
683
|
.replace(/</g, '<')
|
|
539
684
|
.replace(/>/g, '>');
|
|
540
685
|
};
|
|
686
|
+
/** Puts markup at the end of the head, or leaves the page alone when it has none. */
|
|
687
|
+
var insertIntoHead = function (html, markup) {
|
|
688
|
+
var head = /<head(\s[^>]*)?>/i.exec(html);
|
|
689
|
+
if (!head)
|
|
690
|
+
return html;
|
|
691
|
+
var start = head.index + head[0].length;
|
|
692
|
+
var close = html.toLowerCase().indexOf('</head>', start);
|
|
693
|
+
var at = close < 0 ? start : close;
|
|
694
|
+
return html.slice(0, at) + markup + html.slice(at);
|
|
695
|
+
};
|
|
696
|
+
/**
|
|
697
|
+
* Hints the app scripts the loader will replay, so their download starts while
|
|
698
|
+
* the worker installs rather than after it.
|
|
699
|
+
*
|
|
700
|
+
* Only scripts this build owns are hinted - the file has to be in the output for
|
|
701
|
+
* the version query to be meaningful - and only classic ones. A `type="module"`
|
|
702
|
+
* tag is fetched with different credentials semantics, so an `as="script"`
|
|
703
|
+
* preload does not match it; and a cross-origin src would need `crossorigin` on
|
|
704
|
+
* the hint to match its tag. Either mismatch fetches the file twice, which is
|
|
705
|
+
* worse than no hint.
|
|
706
|
+
*/
|
|
707
|
+
var preloadHints = function (scripts, outputFolder) {
|
|
708
|
+
return scripts
|
|
709
|
+
.filter(function (script) {
|
|
710
|
+
if (!script.src)
|
|
711
|
+
return false;
|
|
712
|
+
if (!PRELOADABLE_TYPES.includes((script.type || '').trim().toLowerCase()))
|
|
713
|
+
return false;
|
|
714
|
+
if (script.crossOrigin)
|
|
715
|
+
return false;
|
|
716
|
+
if (/^([a-z]+:)?\/\//i.test(script.src))
|
|
717
|
+
return false;
|
|
718
|
+
if (!outputFolder)
|
|
719
|
+
return true;
|
|
720
|
+
var pathname = script.src.split('?')[0];
|
|
721
|
+
return fs_1.default.existsSync(path_1.default.join(outputFolder, pathname.replace(/^(\.\/|\/)/, '')));
|
|
722
|
+
})
|
|
723
|
+
.map(function (script) { return "<link rel=\"preload\" as=\"script\" href=\"".concat(script.src, "\" />"); });
|
|
724
|
+
};
|
|
541
725
|
/**
|
|
542
726
|
* The boot loader is a FILE rather than an inline script: an app with a strict
|
|
543
727
|
* Content-Security-Policy (`default-src 'self'`, no `unsafe-inline`) refuses an
|
|
@@ -714,7 +898,7 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
714
898
|
var early = [];
|
|
715
899
|
var tagPattern = /<script\b([^>]*)>([\s\S]*?)<\/script>[ \t]*\r?\n?/gi;
|
|
716
900
|
patched = patched.replace(tagPattern, function (tag, source, body) {
|
|
717
|
-
var
|
|
901
|
+
var e_10, _a;
|
|
718
902
|
var attributes = parseAttributes(source);
|
|
719
903
|
var type = (attributes.type || '').trim().toLowerCase();
|
|
720
904
|
var inlined = !attributes.src;
|
|
@@ -753,12 +937,12 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
753
937
|
spec[name_2] = attribute === 'nomodule' ? 'true' : attributes[attribute];
|
|
754
938
|
}
|
|
755
939
|
}
|
|
756
|
-
catch (
|
|
940
|
+
catch (e_10_1) { e_10 = { error: e_10_1 }; }
|
|
757
941
|
finally {
|
|
758
942
|
try {
|
|
759
943
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
760
944
|
}
|
|
761
|
-
finally { if (
|
|
945
|
+
finally { if (e_10) throw e_10.error; }
|
|
762
946
|
}
|
|
763
947
|
scripts.push(spec);
|
|
764
948
|
return '';
|
|
@@ -767,6 +951,9 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
767
951
|
var versioned = outputFolder
|
|
768
952
|
? versionScripts(scripts, outputFolder)
|
|
769
953
|
: scripts;
|
|
954
|
+
var hints = preloadHints(versioned, outputFolder);
|
|
955
|
+
if (hints.length)
|
|
956
|
+
patched = insertIntoHead(patched, hints.join(''));
|
|
770
957
|
var tag_1 = bootTag(versioned, version, basePath);
|
|
771
958
|
// a function, not a replacement string: the tag carries the page's own srcs
|
|
772
959
|
// in `data-scripts`, and a `$&` in one would expand to the matched markup
|
|
@@ -821,6 +1008,67 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
821
1008
|
}
|
|
822
1009
|
return { patched: patched, warnings: warnings };
|
|
823
1010
|
};
|
|
1011
|
+
/** The files the console consists of, under `<output>/api`. */
|
|
1012
|
+
var API_CONSOLE_FILES = [
|
|
1013
|
+
'index.html',
|
|
1014
|
+
'console.js',
|
|
1015
|
+
'console.css',
|
|
1016
|
+
'operations.json'
|
|
1017
|
+
];
|
|
1018
|
+
/**
|
|
1019
|
+
* The API console: a page at `<base>/api/` that lists every operation the API
|
|
1020
|
+
* document declares and sends them against this host.
|
|
1021
|
+
*
|
|
1022
|
+
* The route table is the same one the worker answers from (`ROUTES` from
|
|
1023
|
+
* @sasjs/api), so the console cannot drift from the API: a route the package
|
|
1024
|
+
* adds appears here on the next build, with the parameters it declares.
|
|
1025
|
+
*
|
|
1026
|
+
* Three files and the data, all static. It is a developer surface rather than a
|
|
1027
|
+
* screen of the app, so nothing in the page links to it and nothing preloads it -
|
|
1028
|
+
* a visit costs the demo nothing until someone types the URL.
|
|
1029
|
+
*/
|
|
1030
|
+
var writeApiConsole = function (outputFolder, version) {
|
|
1031
|
+
var folder = path_1.default.join(outputFolder, 'api');
|
|
1032
|
+
// `<base>/api/` is this build's, not the project's: a site that ships its own
|
|
1033
|
+
// files there would have them replaced without a word, so it keeps them and
|
|
1034
|
+
// the console is left out
|
|
1035
|
+
var taken = API_CONSOLE_FILES.filter(function (file) {
|
|
1036
|
+
return fs_1.default.existsSync(path_1.default.join(folder, file));
|
|
1037
|
+
});
|
|
1038
|
+
if (taken.length)
|
|
1039
|
+
return {
|
|
1040
|
+
written: false,
|
|
1041
|
+
operations: 0,
|
|
1042
|
+
warning: "The site already carries ".concat(taken
|
|
1043
|
+
.map(function (file) { return 'api/' + file; })
|
|
1044
|
+
.join(', '), ", so the API console was not written. ") +
|
|
1045
|
+
'Move those files, or set streamConfig.mockApi to false to silence this.'
|
|
1046
|
+
};
|
|
1047
|
+
fs_1.default.mkdirSync(folder, { recursive: true });
|
|
1048
|
+
// The console starts the host itself, so it has to ask for the same worker the
|
|
1049
|
+
// app's loader asks for: a registration is identified by its script URL, and a
|
|
1050
|
+
// bare `sw.js` here would replace the versioned one on every visit to either
|
|
1051
|
+
// page - an install nobody asked for, which is what the version exists to avoid.
|
|
1052
|
+
fs_1.default.writeFileSync(path_1.default.join(folder, 'index.html'), fs_1.default
|
|
1053
|
+
.readFileSync(path_1.default.join(ASSETS_FOLDER, 'api-index.html'), 'utf8')
|
|
1054
|
+
.replace('__MOCK_VERSION__', version));
|
|
1055
|
+
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'api-console.js'), path_1.default.join(folder, 'console.js'));
|
|
1056
|
+
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'api-console.css'), path_1.default.join(folder, 'console.css'));
|
|
1057
|
+
var operations = api_1.ROUTES.map(function (route) { return ({
|
|
1058
|
+
id: route.id,
|
|
1059
|
+
method: route.method,
|
|
1060
|
+
path: route.path,
|
|
1061
|
+
tag: route.tag,
|
|
1062
|
+
summary: route.summary,
|
|
1063
|
+
params: route.params,
|
|
1064
|
+
requestBodies: route.requestBodies,
|
|
1065
|
+
responses: route.responses,
|
|
1066
|
+
implemented: route.implemented,
|
|
1067
|
+
reason: route.reason
|
|
1068
|
+
}); });
|
|
1069
|
+
fs_1.default.writeFileSync(path_1.default.join(folder, 'operations.json'), JSON.stringify(operations, null, 2));
|
|
1070
|
+
return { written: true, operations: operations.length };
|
|
1071
|
+
};
|
|
824
1072
|
/**
|
|
825
1073
|
* Builds a static site that runs the project's JS services (its mocks) in the
|
|
826
1074
|
* browser, so a frontend can be tested with no server and no SAS.
|
|
@@ -837,11 +1085,11 @@ var patchIndexHtml = function (html, version, basePath, outputFolder) {
|
|
|
837
1085
|
*/
|
|
838
1086
|
function buildMockSite(target) {
|
|
839
1087
|
return __awaiter(this, void 0, void 0, function () {
|
|
840
|
-
var projectDir, appLocRaw, appLoc, buildFolder, basePath, webSourceRaw, webFolder, outputFolder, streamConfig, excluded, _a, seed, skipped, jsPrograms, sasPrograms, driveEntries, identity, webFileCount, hostFolder, _b, _c, asset, hostVersion, buildStamp, swPath, snapshotSource, hasSnapshot, transcriptSource, hasTranscript, indexPath,
|
|
841
|
-
var
|
|
842
|
-
var
|
|
843
|
-
return __generator(this, function (
|
|
844
|
-
switch (
|
|
1088
|
+
var projectDir, appLocRaw, appLoc, buildFolder, basePath, webSourceRaw, webFolder, outputFolder, streamConfig, excluded, _a, seed, skipped, jsPrograms, sasPrograms, driveEntries, identity, webFileCount, hostFolder, _b, _c, asset, hostVersion, buildStamp, swPath, snapshotSource, hasSnapshot, snapshotShards, capture, files, split, _d, _e, shard, transcriptSource, hasTranscript, apiConsole, written, indexPath, _f, patched, warnings;
|
|
1089
|
+
var e_11, _g, e_12, _h;
|
|
1090
|
+
var _j, _k, _l, _m, _o, _p, _q, _r;
|
|
1091
|
+
return __generator(this, function (_s) {
|
|
1092
|
+
switch (_s.label) {
|
|
845
1093
|
case 0:
|
|
846
1094
|
projectDir = process.projectDir || process.cwd();
|
|
847
1095
|
appLocRaw = target.appLoc;
|
|
@@ -853,24 +1101,24 @@ function buildMockSite(target) {
|
|
|
853
1101
|
path_1.default.join(projectDir, 'sasjsbuild');
|
|
854
1102
|
return [4 /*yield*/, (0, utils_1.folderExists)(buildFolder)];
|
|
855
1103
|
case 1:
|
|
856
|
-
if (!(
|
|
1104
|
+
if (!(_s.sent())) {
|
|
857
1105
|
throw new Error("Build folder not found: ".concat(buildFolder, ". Run `sasjs compile` first."));
|
|
858
1106
|
}
|
|
859
|
-
basePath = normaliseBasePath((
|
|
860
|
-
webSourceRaw = (
|
|
1107
|
+
basePath = normaliseBasePath((_j = target.streamConfig) === null || _j === void 0 ? void 0 : _j.mockWebBasePath);
|
|
1108
|
+
webSourceRaw = (_k = target.streamConfig) === null || _k === void 0 ? void 0 : _k.webSourcePath;
|
|
861
1109
|
if (!webSourceRaw) {
|
|
862
1110
|
throw new Error('No built web app. Set streamConfig.webSourcePath on the target (for example ./client/dist), and build the frontend first.');
|
|
863
1111
|
}
|
|
864
1112
|
webFolder = path_1.default.resolve(projectDir, webSourceRaw);
|
|
865
1113
|
return [4 /*yield*/, (0, utils_1.folderExists)(webFolder)];
|
|
866
1114
|
case 2:
|
|
867
|
-
if (!(
|
|
1115
|
+
if (!(_s.sent())) {
|
|
868
1116
|
throw new Error("Web source folder not found: ".concat(webFolder, ". Check streamConfig.webSourcePath - build the frontend first."));
|
|
869
1117
|
}
|
|
870
1118
|
if (!fs_1.default.existsSync(path_1.default.join(webFolder, 'index.html'))) {
|
|
871
1119
|
throw new Error("".concat(webFolder, " does not contain an index.html - is streamConfig.webSourcePath pointing at the built app?"));
|
|
872
1120
|
}
|
|
873
|
-
outputFolder = ((
|
|
1121
|
+
outputFolder = ((_l = target.streamConfig) === null || _l === void 0 ? void 0 : _l.mockWebFolder)
|
|
874
1122
|
? path_1.default.resolve(projectDir, target.streamConfig.mockWebFolder)
|
|
875
1123
|
: path_1.default.join(buildFolder, 'mock');
|
|
876
1124
|
assertSafeOutputFolder(outputFolder, projectDir, buildFolder, webFolder);
|
|
@@ -891,14 +1139,14 @@ function buildMockSite(target) {
|
|
|
891
1139
|
// without a SAS licence and without a login. What they cannot do is run here,
|
|
892
1140
|
// so the build says so rather than letting a Run button be the surprise.
|
|
893
1141
|
if (jsPrograms === 0 && sasPrograms > 0) {
|
|
894
|
-
(
|
|
1142
|
+
(_m = process.logger) === null || _m === void 0 ? void 0 : _m.warn("".concat(sasPrograms, " SAS program(s) and no JavaScript in ").concat(buildFolder, ": they are placed on the drive to read, and cannot run in a browser."));
|
|
895
1143
|
}
|
|
896
1144
|
// No PROGRAMS is a different thing again: a compile that produced nothing, or
|
|
897
1145
|
// a stale sasjsbuild from an unrelated command - `sasjs doc` leaves a folder
|
|
898
1146
|
// behind that is not a program folder but does reach the drive, so the count
|
|
899
1147
|
// that decides this is of programs, not of files.
|
|
900
1148
|
if (jsPrograms + sasPrograms === 0) {
|
|
901
|
-
(
|
|
1149
|
+
(_o = process.logger) === null || _o === void 0 ? void 0 : _o.warn("No compiled programs in ".concat(buildFolder, ": this site ships with an empty drive. ") +
|
|
902
1150
|
'That is expected for an interface with no services; for anything else, check that compile wrote the target.');
|
|
903
1151
|
}
|
|
904
1152
|
// The package supplies the drive rules the worker answers with, so an install
|
|
@@ -911,7 +1159,7 @@ function buildMockSite(target) {
|
|
|
911
1159
|
'or check that @sasjs/api still publishes build/browser.js.');
|
|
912
1160
|
}
|
|
913
1161
|
if (skipped.length) {
|
|
914
|
-
(
|
|
1162
|
+
(_p = process.logger) === null || _p === void 0 ? void 0 : _p.warn("Left ".concat(skipped.length, " file(s) off the mock drive, over the ").concat(megabytes(exports.MAX_BINARY_SEED_BYTES), " per-file or ").concat(megabytes(exports.MAX_SEED_BYTES), " total the browser build seeds: ") +
|
|
915
1163
|
"".concat(skipped
|
|
916
1164
|
.slice(0, 3)
|
|
917
1165
|
.map(function (file) {
|
|
@@ -942,12 +1190,12 @@ function buildMockSite(target) {
|
|
|
942
1190
|
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, asset), path_1.default.join(hostFolder, asset));
|
|
943
1191
|
}
|
|
944
1192
|
}
|
|
945
|
-
catch (
|
|
1193
|
+
catch (e_11_1) { e_11 = { error: e_11_1 }; }
|
|
946
1194
|
finally {
|
|
947
1195
|
try {
|
|
948
|
-
if (_c && !_c.done && (
|
|
1196
|
+
if (_c && !_c.done && (_g = _b.return)) _g.call(_b);
|
|
949
1197
|
}
|
|
950
|
-
finally { if (
|
|
1198
|
+
finally { if (e_11) throw e_11.error; }
|
|
951
1199
|
}
|
|
952
1200
|
fs_1.default.copyFileSync(API_BROWSER_BUILD, path_1.default.join(hostFolder, 'api-browser.js'));
|
|
953
1201
|
// 2. the linter, bundled from the installed packages so the code routes answer
|
|
@@ -959,7 +1207,7 @@ function buildMockSite(target) {
|
|
|
959
1207
|
case 3:
|
|
960
1208
|
// 2. the linter, bundled from the installed packages so the code routes answer
|
|
961
1209
|
// with the rules this CLI resolved rather than a second implementation
|
|
962
|
-
|
|
1210
|
+
_s.sent();
|
|
963
1211
|
// sw.js sits at the site root: a worker can only control the folder it is
|
|
964
1212
|
// served from, and the app has to be controlled from /
|
|
965
1213
|
fs_1.default.copyFileSync(path_1.default.join(ASSETS_FOLDER, 'sw.js'), path_1.default.join(outputFolder, 'sw.js'));
|
|
@@ -1012,11 +1260,40 @@ function buildMockSite(target) {
|
|
|
1012
1260
|
.readFileSync(swPath, 'utf8')
|
|
1013
1261
|
.replace("importScripts('host/runtime-core.js')", "importScripts('host/runtime-core.js?v=".concat(hostVersion, "')"))
|
|
1014
1262
|
.replace("importScripts('host/api-browser.js')", "importScripts('host/api-browser.js?v=".concat(hostVersion, "')"))
|
|
1015
|
-
.replace("importScripts('host/lint-browser.js')", "importScripts('host/lint-browser.js?v=".concat(hostVersion, "')"))
|
|
1263
|
+
.replace("importScripts('host/lint-browser.js')", "importScripts('host/lint-browser.js?v=".concat(hostVersion, "')"))
|
|
1264
|
+
// the console's file list, from the one place that writes the folder: a
|
|
1265
|
+
// second copy here would let a new file be served as an API path
|
|
1266
|
+
.replace(/const API_CONSOLE_FILES = \[[^\]]*\]/, "const API_CONSOLE_FILES = ".concat(JSON.stringify(API_CONSOLE_FILES))));
|
|
1016
1267
|
snapshotSource = path_1.default.join(buildFolder, 'mock-snapshot.json');
|
|
1017
1268
|
hasSnapshot = fs_1.default.existsSync(snapshotSource);
|
|
1269
|
+
snapshotShards = 0;
|
|
1018
1270
|
if (hasSnapshot) {
|
|
1019
|
-
fs_1.default.
|
|
1271
|
+
capture = JSON.parse(fs_1.default.readFileSync(snapshotSource, 'utf8'));
|
|
1272
|
+
if (capture &&
|
|
1273
|
+
capture.format !== undefined &&
|
|
1274
|
+
capture.format !== exports.DRIVE_FORMAT)
|
|
1275
|
+
throw new Error("The captured drive at ".concat(snapshotSource, " is format ") +
|
|
1276
|
+
"".concat(JSON.stringify(capture.format), ", and this build writes format ") +
|
|
1277
|
+
"".concat(exports.DRIVE_FORMAT, ". Seed the drive again with window.SASjsMock.download()."));
|
|
1278
|
+
files = capture && capture.files ? capture.files : capture;
|
|
1279
|
+
if (!files || typeof files !== 'object')
|
|
1280
|
+
throw new Error("The captured drive at ".concat(snapshotSource, " carries no files - the export is a JSON object of path to contents."));
|
|
1281
|
+
split = shardSnapshot(files);
|
|
1282
|
+
try {
|
|
1283
|
+
for (_d = __values(split.shards), _e = _d.next(); !_e.done; _e = _d.next()) {
|
|
1284
|
+
shard = _e.value;
|
|
1285
|
+
fs_1.default.writeFileSync(path_1.default.join(hostFolder, shard.file), JSON.stringify({ format: exports.DRIVE_FORMAT, files: shard.files }));
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
|
1289
|
+
finally {
|
|
1290
|
+
try {
|
|
1291
|
+
if (_e && !_e.done && (_h = _d.return)) _h.call(_d);
|
|
1292
|
+
}
|
|
1293
|
+
finally { if (e_12) throw e_12.error; }
|
|
1294
|
+
}
|
|
1295
|
+
fs_1.default.writeFileSync(path_1.default.join(hostFolder, 'vfs-snapshot.json'), JSON.stringify(split.index, null, 2));
|
|
1296
|
+
snapshotShards = split.shards.length;
|
|
1020
1297
|
}
|
|
1021
1298
|
transcriptSource = path_1.default.join(buildFolder, 'sas-transcript.json');
|
|
1022
1299
|
hasTranscript = fs_1.default.existsSync(transcriptSource);
|
|
@@ -1031,13 +1308,21 @@ function buildMockSite(target) {
|
|
|
1031
1308
|
jsPrograms: jsPrograms,
|
|
1032
1309
|
programFolders: PROGRAM_FOLDERS,
|
|
1033
1310
|
snapshot: hasSnapshot,
|
|
1311
|
+
snapshotShards: snapshotShards,
|
|
1034
1312
|
sasTranscript: hasTranscript,
|
|
1035
1313
|
identity: identity.source,
|
|
1036
1314
|
buildStamp: buildStamp,
|
|
1037
1315
|
builtAt: new Date().toISOString()
|
|
1038
1316
|
}, null, 2));
|
|
1317
|
+
apiConsole = false;
|
|
1318
|
+
if (((_q = target.streamConfig) === null || _q === void 0 ? void 0 : _q.mockApi) !== false) {
|
|
1319
|
+
written = writeApiConsole(outputFolder, hostVersion);
|
|
1320
|
+
apiConsole = written.written;
|
|
1321
|
+
if (written.warning)
|
|
1322
|
+
(_r = process.logger) === null || _r === void 0 ? void 0 : _r.warn(written.warning);
|
|
1323
|
+
}
|
|
1039
1324
|
indexPath = path_1.default.join(outputFolder, 'index.html');
|
|
1040
|
-
|
|
1325
|
+
_f = patchIndexHtml(fs_1.default.readFileSync(indexPath, 'utf8'), hostVersion, basePath, outputFolder), patched = _f.patched, warnings = _f.warnings;
|
|
1041
1326
|
fs_1.default.writeFileSync(indexPath, patched);
|
|
1042
1327
|
warnings.forEach(function (warning) { var _a; return (_a = process.logger) === null || _a === void 0 ? void 0 : _a.warn(warning); });
|
|
1043
1328
|
return [2 /*return*/, {
|
|
@@ -1047,6 +1332,8 @@ function buildMockSite(target) {
|
|
|
1047
1332
|
jsPrograms: jsPrograms,
|
|
1048
1333
|
webFileCount: webFileCount,
|
|
1049
1334
|
snapshot: hasSnapshot,
|
|
1335
|
+
snapshotShards: snapshotShards,
|
|
1336
|
+
apiConsole: apiConsole,
|
|
1050
1337
|
sasTranscript: hasTranscript,
|
|
1051
1338
|
identitySource: identity.source
|
|
1052
1339
|
}];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|