@maka/meteor-sdk 0.2.7 → 0.2.8
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/README.md +20 -0
- package/isopackets/combined/isopacket-buildinfo.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.npm-shrinkwrap.json +3 -3
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.package-lock.json +4 -4
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/full-chromium-versions.js +2 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/full-chromium-versions.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/full-versions.js +1 -0
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/full-versions.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/package.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/versions.js +1 -0
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/electron-to-chromium/versions.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/boilerplate-generator/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/constraint-solver/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ddp-client/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-client/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/ecmascript-runtime-server/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/fetch/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/inter-process-messaging/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/logging/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/logic-solver/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/meteor/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/modules/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/modules-runtime/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/npm-mongo/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/package-version-parser/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/promise/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/react-fast-refresh/node_modules/.package-lock.json +1 -1
- package/isopackets/combined/npm/node_modules/meteor/socket-stream-client/node_modules/.package-lock.json +1 -1
- package/package.json +1 -1
- package/src/bootstrap.js +26 -0
- package/src/bundle.js +5 -0
- package/tools/isobuild/meteor-npm.js +315 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/meteor-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Programmatic Node API over the Meteor build tool (bundle/run/create/etc.) -- for driving Meteor's build system from plain Node code instead of shelling out to a meteor CLI (there isn't one anymore; see README.md). \"dependencies\" is generated by scripts/write-tool-package-json.js from scripts/dev-bundle-tool-package.js at the monorepo root -- do not hand-edit that field, edit that file and re-run the script instead.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"exports": {
|
package/src/bootstrap.js
CHANGED
|
@@ -120,6 +120,31 @@ function ensureMainModuleShim() {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Kick off the persistent npm-deps cache prewarm (see tools/isobuild/
|
|
124
|
+
// meteor-npm.js): restore every bundled package's node_modules from
|
|
125
|
+
// ~/.meteor-npm-cache in parallel, so the first build after a fresh SDK
|
|
126
|
+
// install doesn't pay a serial `npm install` per package. Deliberately
|
|
127
|
+
// NOT awaited -- each restore holds the same cross-process lock
|
|
128
|
+
// updateDependencies() takes, so a build racing ahead of the prewarm
|
|
129
|
+
// simply waits on (or wins) the per-package lock; firing it before
|
|
130
|
+
// ensureRuntimeReady() lets even the isopacket builds overlap with it.
|
|
131
|
+
let npmDepsCachePrewarm = null;
|
|
132
|
+
function startNpmDepsCachePrewarm() {
|
|
133
|
+
if (npmDepsCachePrewarm) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const meteorNpm = require("../tools/isobuild/meteor-npm.js");
|
|
138
|
+
const path = require("path");
|
|
139
|
+
npmDepsCachePrewarm = Promise.resolve(
|
|
140
|
+
meteorNpm.prewarmNpmDepsCache(path.resolve(__dirname, "..", "packages"))
|
|
141
|
+
).catch(() => {});
|
|
142
|
+
} catch (e) {
|
|
143
|
+
// Best-effort: the build's own per-package install path still works.
|
|
144
|
+
npmDepsCachePrewarm = Promise.resolve();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
123
148
|
// Idempotent: safe to call from every public SDK entry point. The actual
|
|
124
149
|
// Babel require() hook installed by install-babel.js is itself naturally
|
|
125
150
|
// idempotent too (Node's own require() cache means requiring the same
|
|
@@ -152,6 +177,7 @@ async function bootstrap(options) {
|
|
|
152
177
|
require("../tools/tool-env/install-babel.js");
|
|
153
178
|
ensureAsyncLocalStorage();
|
|
154
179
|
consoleAdapter.patch();
|
|
180
|
+
startNpmDepsCachePrewarm();
|
|
155
181
|
await ensureRuntimeReady(options && options.offline);
|
|
156
182
|
bootstrapped = true;
|
|
157
183
|
}
|
package/src/bundle.js
CHANGED
|
@@ -45,6 +45,11 @@ async function bundle(project, options) {
|
|
|
45
45
|
throwIfHasMessages(result.errors);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Background npm-deps cache writes (see meteor-npm.js) ran concurrently
|
|
49
|
+
// with the build; wait for them here so a build-and-exit process still
|
|
50
|
+
// gets its cache populated. Usually a no-op by this point.
|
|
51
|
+
await require("../tools/isobuild/meteor-npm.js").flushNpmDepsCacheWrites();
|
|
52
|
+
|
|
48
53
|
return {
|
|
49
54
|
outputPath: options.outputPath,
|
|
50
55
|
starManifest: result.starManifest,
|
|
@@ -676,6 +676,295 @@ var makeNewPackageNpmDir = function (newPackageNpmDir) {
|
|
|
676
676
|
''/*git diff complains without trailing newline*/].join('\n'));
|
|
677
677
|
};
|
|
678
678
|
|
|
679
|
+
// --- Persistent npm-deps cache ---------------------------------------
|
|
680
|
+
//
|
|
681
|
+
// A package's .npm/package/node_modules is fully determined by three
|
|
682
|
+
// things: the npm-shrinkwrap.json content, the Node compatibility
|
|
683
|
+
// version, and the platform/arch. Yet every fresh install of the tool
|
|
684
|
+
// (e.g. `npm i -g` of a new SDK version) starts with empty node_modules
|
|
685
|
+
// for every bundled package and pays a serial `npm install` per package
|
|
686
|
+
// on the first build. Since shrinkwraps rarely change between releases,
|
|
687
|
+
// keep a per-user cache of completed node_modules trees keyed by a hash
|
|
688
|
+
// of those three inputs, and restore by directory copy instead of
|
|
689
|
+
// spawning npm.
|
|
690
|
+
//
|
|
691
|
+
// Everything here is best-effort: any failure falls through to the
|
|
692
|
+
// normal npm install path, and a stale/corrupt restore is caught by the
|
|
693
|
+
// existing shrinkwrap-vs-installed subtree comparison (which then
|
|
694
|
+
// triggers a real reinstall over it).
|
|
695
|
+
//
|
|
696
|
+
// METEOR_PACKAGE_NPM_CACHE: unset -> enabled at ~/.meteor-npm-cache;
|
|
697
|
+
// "0"/"false"/"no"/"off" -> disabled; any other value -> cache root.
|
|
698
|
+
|
|
699
|
+
function npmDepsCacheDisabled() {
|
|
700
|
+
const v = process.env.METEOR_PACKAGE_NPM_CACHE;
|
|
701
|
+
return typeof v === "string" && /^\s*(0|false|no|off)\s*$/i.test(v);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function npmDepsCacheRoot() {
|
|
705
|
+
const v = process.env.METEOR_PACKAGE_NPM_CACHE;
|
|
706
|
+
if (v && ! npmDepsCacheDisabled()) {
|
|
707
|
+
return files.convertToStandardPath(v);
|
|
708
|
+
}
|
|
709
|
+
return files.pathJoin(
|
|
710
|
+
files.convertToStandardPath(require("os").homedir()),
|
|
711
|
+
".meteor-npm-cache"
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// JSON.stringify with deterministically sorted object keys, so the hash
|
|
716
|
+
// is insensitive to key order, whitespace, and line endings of the
|
|
717
|
+
// on-disk shrinkwrap file.
|
|
718
|
+
function stableStringify(value) {
|
|
719
|
+
if (Array.isArray(value)) {
|
|
720
|
+
return "[" + value.map(stableStringify).join(",") + "]";
|
|
721
|
+
}
|
|
722
|
+
if (value && typeof value === "object") {
|
|
723
|
+
return "{" + Object.keys(value).sort().map(
|
|
724
|
+
key => JSON.stringify(key) + ":" + stableStringify(value[key])
|
|
725
|
+
).join(",") + "}";
|
|
726
|
+
}
|
|
727
|
+
return JSON.stringify(value);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Hash of the package's current npm-shrinkwrap.json plus everything
|
|
731
|
+
// else that determines node_modules content. Returns null if the
|
|
732
|
+
// shrinkwrap is missing or unparsable.
|
|
733
|
+
function npmDepsCacheKey(packageNpmDir) {
|
|
734
|
+
let shrinkwrap;
|
|
735
|
+
try {
|
|
736
|
+
shrinkwrap = JSON.parse(files.readFile(
|
|
737
|
+
files.pathJoin(packageNpmDir, "npm-shrinkwrap.json"), "utf8"));
|
|
738
|
+
} catch (e) {
|
|
739
|
+
return null;
|
|
740
|
+
}
|
|
741
|
+
return require("crypto").createHash("sha256").update(stableStringify({
|
|
742
|
+
shrinkwrap,
|
|
743
|
+
node: currentNodeCompatibilityVersion(),
|
|
744
|
+
platform: process.platform,
|
|
745
|
+
arch: process.arch,
|
|
746
|
+
})).digest("hex").slice(0, 32);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function npmDepsCacheEntryDir(packageName, key) {
|
|
750
|
+
// Meteor package names may contain characters that are not valid in
|
|
751
|
+
// paths everywhere (e.g. ":" on Windows); the hash disambiguates, so
|
|
752
|
+
// a lossy sanitization is fine.
|
|
753
|
+
const safeName = packageName.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
754
|
+
return files.pathJoin(npmDepsCacheRoot(), safeName, key);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Copy a cached node_modules into packageNpmDir (via temp dir + rename,
|
|
758
|
+
// so a crash never leaves a half-restored node_modules in place).
|
|
759
|
+
// Returns true on success. Callers must hold the npm package lock for
|
|
760
|
+
// packageNpmDir.
|
|
761
|
+
async function restoreNodeModulesFromNpmDepsCache(packageName, packageNpmDir) {
|
|
762
|
+
if (npmDepsCacheDisabled()) {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
const key = npmDepsCacheKey(packageNpmDir);
|
|
766
|
+
if (! key) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
const cachedNodeModules = files.pathJoin(
|
|
770
|
+
npmDepsCacheEntryDir(packageName, key), "node_modules");
|
|
771
|
+
if (! files.exists(
|
|
772
|
+
files.pathJoin(cachedNodeModules, ".package-lock.json"))) {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Sweep temp dirs left behind by a crashed earlier restore (safe: we
|
|
777
|
+
// hold the package lock).
|
|
778
|
+
try {
|
|
779
|
+
files.readdir(packageNpmDir).forEach(entry => {
|
|
780
|
+
if (entry.startsWith(".node_modules-from-cache-")) {
|
|
781
|
+
files.rm_recursive(files.pathJoin(packageNpmDir, entry));
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
} catch (e) {}
|
|
785
|
+
|
|
786
|
+
const nodeModulesDir = files.pathJoin(packageNpmDir, "node_modules");
|
|
787
|
+
const tmpDir = files.pathJoin(
|
|
788
|
+
packageNpmDir, ".node_modules-from-cache-" + utils.randomToken());
|
|
789
|
+
tmpDirs.push(tmpDir);
|
|
790
|
+
try {
|
|
791
|
+
await files.cp_r(cachedNodeModules, tmpDir, { preserveSymlinks: true });
|
|
792
|
+
await files.renameDirAlmostAtomically(tmpDir, nodeModulesDir);
|
|
793
|
+
dirtyNodeModulesDirectory(nodeModulesDir);
|
|
794
|
+
return true;
|
|
795
|
+
} catch (e) {
|
|
796
|
+
try {
|
|
797
|
+
if (files.exists(tmpDir)) {
|
|
798
|
+
await files.rm_recursive(tmpDir);
|
|
799
|
+
}
|
|
800
|
+
} catch (cleanupError) {}
|
|
801
|
+
return false;
|
|
802
|
+
} finally {
|
|
803
|
+
tmpDirs = _.without(tmpDirs, tmpDir);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Cache writes run in the background (fire-and-forget with an atomic
|
|
808
|
+
// temp-dir + rename publish) so they never sit on the build's critical
|
|
809
|
+
// path. flushNpmDepsCacheWrites() lets one-shot processes (e.g. a
|
|
810
|
+
// build-and-exit embedder) wait for them before exiting; if a process
|
|
811
|
+
// dies first, the orphaned .tmp-* dir is swept by a later prune.
|
|
812
|
+
const pendingNpmDepsCacheWrites = [];
|
|
813
|
+
|
|
814
|
+
meteorNpm.flushNpmDepsCacheWrites = function () {
|
|
815
|
+
return Promise.allSettled(pendingNpmDepsCacheWrites);
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
function scheduleNpmDepsCachePopulate(packageName, packageNpmDir, key) {
|
|
819
|
+
if (npmDepsCacheDisabled()) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
pendingNpmDepsCacheWrites.push(
|
|
823
|
+
populateNpmDepsCache(packageName, packageNpmDir, key).catch(e => {})
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async function populateNpmDepsCache(packageName, packageNpmDir, key) {
|
|
828
|
+
key = key || npmDepsCacheKey(packageNpmDir);
|
|
829
|
+
if (! key) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const entryDir = npmDepsCacheEntryDir(packageName, key);
|
|
833
|
+
if (files.exists(entryDir)) {
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
const packageCacheDir = files.pathDirname(entryDir);
|
|
837
|
+
files.mkdir_p(packageCacheDir);
|
|
838
|
+
const tmpDir = files.pathJoin(
|
|
839
|
+
packageCacheDir, ".tmp-" + utils.randomToken());
|
|
840
|
+
try {
|
|
841
|
+
await files.cp_r(
|
|
842
|
+
files.pathJoin(packageNpmDir, "node_modules"),
|
|
843
|
+
files.pathJoin(tmpDir, "node_modules"),
|
|
844
|
+
{ preserveSymlinks: true }
|
|
845
|
+
);
|
|
846
|
+
files.rename(tmpDir, entryDir);
|
|
847
|
+
} catch (e) {
|
|
848
|
+
// Concurrent populate may have won the rename race, or the copy
|
|
849
|
+
// failed midway; either way discard our temp dir and move on.
|
|
850
|
+
try {
|
|
851
|
+
if (files.exists(tmpDir)) {
|
|
852
|
+
await files.rm_recursive(tmpDir);
|
|
853
|
+
}
|
|
854
|
+
} catch (cleanupError) {}
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
pruneNpmDepsCache(packageCacheDir, entryDir);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// Keep the 3 newest entries per package (different keys = different
|
|
861
|
+
// shrinkwrap/node/platform combos) and sweep stale temp dirs, so the
|
|
862
|
+
// cache stays bounded without any global bookkeeping.
|
|
863
|
+
function pruneNpmDepsCache(packageCacheDir, keepDir) {
|
|
864
|
+
try {
|
|
865
|
+
const keepBase = files.pathBasename(keepDir);
|
|
866
|
+
const entries = [];
|
|
867
|
+
files.readdir(packageCacheDir).forEach(entry => {
|
|
868
|
+
const fullPath = files.pathJoin(packageCacheDir, entry);
|
|
869
|
+
if (entry.startsWith(".tmp-")) {
|
|
870
|
+
// Only sweep temp dirs that are clearly abandoned; a concurrent
|
|
871
|
+
// process may still be filling a fresh one.
|
|
872
|
+
try {
|
|
873
|
+
const ageMs = Date.now() - files.stat(fullPath).mtime.getTime();
|
|
874
|
+
if (ageMs > 60 * 60 * 1000) {
|
|
875
|
+
files.rm_recursive(fullPath);
|
|
876
|
+
}
|
|
877
|
+
} catch (e) {}
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (entry === keepBase) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
entries.push({ fullPath, mtimeMs: files.stat(fullPath).mtime.getTime() });
|
|
885
|
+
} catch (e) {}
|
|
886
|
+
});
|
|
887
|
+
entries.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
888
|
+
entries.slice(2).forEach(entry => {
|
|
889
|
+
try {
|
|
890
|
+
files.rm_recursive(entry.fullPath);
|
|
891
|
+
} catch (e) {}
|
|
892
|
+
});
|
|
893
|
+
} catch (e) {}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Restore every restorable package under packagesDir from the cache, in
|
|
897
|
+
// parallel, before the (serial) per-package build pipeline reaches
|
|
898
|
+
// them. Purely filesystem-driven: a candidate is any package dir with a
|
|
899
|
+
// .npm/package/npm-shrinkwrap.json but no installed node_modules, so no
|
|
900
|
+
// package.js evaluation is needed. Safe to run concurrently with a
|
|
901
|
+
// build -- each restore holds the same cross-process lock
|
|
902
|
+
// updateDependencies() takes, and re-checks its precondition under it.
|
|
903
|
+
// Returns the number of packages restored.
|
|
904
|
+
meteorNpm.prewarmNpmDepsCache = async function (packagesDir, options) {
|
|
905
|
+
options = options || {};
|
|
906
|
+
if (npmDepsCacheDisabled()) {
|
|
907
|
+
return 0;
|
|
908
|
+
}
|
|
909
|
+
packagesDir = files.convertToStandardPath(packagesDir);
|
|
910
|
+
|
|
911
|
+
const candidates = [];
|
|
912
|
+
try {
|
|
913
|
+
files.readdir(packagesDir).forEach(name => {
|
|
914
|
+
const packageNpmDir =
|
|
915
|
+
files.pathJoin(packagesDir, name, ".npm", "package");
|
|
916
|
+
if (! files.exists(
|
|
917
|
+
files.pathJoin(packageNpmDir, "npm-shrinkwrap.json"))) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
if (files.exists(files.pathJoin(
|
|
921
|
+
packageNpmDir, "node_modules", ".package-lock.json"))) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
candidates.push({ name, packageNpmDir });
|
|
925
|
+
});
|
|
926
|
+
} catch (e) {
|
|
927
|
+
return 0;
|
|
928
|
+
}
|
|
929
|
+
if (! candidates.length) {
|
|
930
|
+
return 0;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
let restored = 0;
|
|
934
|
+
let nextIndex = 0;
|
|
935
|
+
async function worker() {
|
|
936
|
+
while (nextIndex < candidates.length) {
|
|
937
|
+
const candidate = candidates[nextIndex++];
|
|
938
|
+
try {
|
|
939
|
+
const ok = await withNpmPackageLock(
|
|
940
|
+
candidate.packageNpmDir,
|
|
941
|
+
async () => {
|
|
942
|
+
// Re-check under the lock: a concurrent build may have
|
|
943
|
+
// installed (or restored) this package already.
|
|
944
|
+
if (files.exists(files.pathJoin(
|
|
945
|
+
candidate.packageNpmDir,
|
|
946
|
+
"node_modules", ".package-lock.json"))) {
|
|
947
|
+
return false;
|
|
948
|
+
}
|
|
949
|
+
return restoreNodeModulesFromNpmDepsCache(
|
|
950
|
+
candidate.name, candidate.packageNpmDir);
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
if (ok) {
|
|
954
|
+
restored++;
|
|
955
|
+
}
|
|
956
|
+
} catch (e) {
|
|
957
|
+
// Best-effort; the build's own updateDependencies() will handle
|
|
958
|
+
// this package the normal way.
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const concurrency = Math.min(
|
|
963
|
+
options.concurrency || 8, candidates.length);
|
|
964
|
+
await Promise.all(_.times(concurrency, worker));
|
|
965
|
+
return restored;
|
|
966
|
+
};
|
|
967
|
+
|
|
679
968
|
var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|
|
680
969
|
packageNpmDir, npmDependencies,
|
|
681
970
|
quiet) {
|
|
@@ -713,6 +1002,14 @@ var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|
|
|
713
1002
|
}
|
|
714
1003
|
}
|
|
715
1004
|
|
|
1005
|
+
// No installed tree yet (fresh tool install, node-version wipe above,
|
|
1006
|
+
// or first build of a cloned package)? Try the persistent cache
|
|
1007
|
+
// before paying for an npm install.
|
|
1008
|
+
if (! _.isEmpty(npmDependencies) &&
|
|
1009
|
+
! files.exists(files.pathJoin(nodeModulesDir, ".package-lock.json"))) {
|
|
1010
|
+
await restoreNodeModulesFromNpmDepsCache(packageName, packageNpmDir);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
716
1013
|
// Make sure node_modules is present (fix for #1761). Prevents npm install
|
|
717
1014
|
// from installing to an existing node_modules dir higher up in the
|
|
718
1015
|
// filesystem. node_modules may be absent due to a change in Node version or
|
|
@@ -759,6 +1056,16 @@ var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|
|
|
759
1056
|
|
|
760
1057
|
let preservedShrinkwrap;
|
|
761
1058
|
|
|
1059
|
+
// Key the eventual cache entry by the shrinkwrap that PRODUCED the
|
|
1060
|
+
// install, captured before completeNpmDirectory() regenerates the
|
|
1061
|
+
// file. When installing from the existing (shipped) shrinkwrap, that
|
|
1062
|
+
// is the same content the next fresh install of this tool version
|
|
1063
|
+
// will probe with -- the regenerated file can differ (e.g.
|
|
1064
|
+
// platform-specific optional deps), which would make every probe
|
|
1065
|
+
// miss. When NOT installing from the shipped shrinkwrap, leave this
|
|
1066
|
+
// null so the populate step keys off the regenerated file instead.
|
|
1067
|
+
let npmDepsCachePopulateKey = null;
|
|
1068
|
+
|
|
762
1069
|
if (_.isEmpty(npmDependencies)) {
|
|
763
1070
|
// If there are no npmDependencies, make sure nothing is installed.
|
|
764
1071
|
preservedShrinkwrap = { dependencies: {} };
|
|
@@ -767,6 +1074,7 @@ var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|
|
|
767
1074
|
// If the top-level npm dependencies are already encompassed by the
|
|
768
1075
|
// npm-shrinkwrap.json file, then reuse that file.
|
|
769
1076
|
preservedShrinkwrap = shrinkwrappedDependenciesTree;
|
|
1077
|
+
npmDepsCachePopulateKey = npmDepsCacheKey(packageNpmDir);
|
|
770
1078
|
|
|
771
1079
|
} else {
|
|
772
1080
|
// Otherwise install npmTree.dependencies as if we were creating a new
|
|
@@ -837,7 +1145,7 @@ var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|
|
|
837
1145
|
}
|
|
838
1146
|
|
|
839
1147
|
await completeNpmDirectory(packageName, newPackageNpmDir, packageNpmDir,
|
|
840
|
-
npmDependencies);
|
|
1148
|
+
npmDependencies, npmDepsCachePopulateKey);
|
|
841
1149
|
};
|
|
842
1150
|
|
|
843
1151
|
// Git-URL dependencies could never satisfy the strict string comparison
|
|
@@ -969,6 +1277,7 @@ async function completeNpmDirectory(
|
|
|
969
1277
|
newPackageNpmDir,
|
|
970
1278
|
packageNpmDir,
|
|
971
1279
|
npmDependencies,
|
|
1280
|
+
npmDepsCachePopulateKey,
|
|
972
1281
|
) {
|
|
973
1282
|
// Create a shrinkwrap file.
|
|
974
1283
|
shrinkwrap(newPackageNpmDir);
|
|
@@ -984,6 +1293,11 @@ async function completeNpmDirectory(
|
|
|
984
1293
|
await files.renameDirAlmostAtomically(newPackageNpmDir, packageNpmDir);
|
|
985
1294
|
|
|
986
1295
|
dirtyNodeModulesDirectory(files.pathJoin(packageNpmDir, "node_modules"));
|
|
1296
|
+
|
|
1297
|
+
if (! _.isEmpty(npmDependencies)) {
|
|
1298
|
+
scheduleNpmDepsCachePopulate(
|
|
1299
|
+
packageName, packageNpmDir, npmDepsCachePopulateKey);
|
|
1300
|
+
}
|
|
987
1301
|
}
|
|
988
1302
|
|
|
989
1303
|
var createReadme = function (newPackageNpmDir) {
|