@maka/meteor-sdk 0.2.8 → 0.2.9
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 +11 -5
- package/isopackets/combined/npm/node_modules/meteor/babel-compiler/node_modules/.package-lock.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/project.js +40 -0
- package/tools/isobuild/meteor-npm.js +129 -32
package/README.md
CHANGED
|
@@ -243,11 +243,17 @@ per-user cache of completed `node_modules` trees at `~/.meteor-npm-cache`,
|
|
|
243
243
|
keyed by shrinkwrap content + Node compatibility version + platform/arch.
|
|
244
244
|
On the first build after an SDK update, packages whose dependencies
|
|
245
245
|
didn't change restore from the cache by parallel directory copy instead
|
|
246
|
-
of spawning npm -- silently and near-instantly. Cache
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
of spawning npm -- silently and near-instantly. Cache *misses* are
|
|
247
|
+
handled in parallel too: once `loadProject()` knows the app's package
|
|
248
|
+
set (from `.meteor/versions`), the remaining installs run through a
|
|
249
|
+
small npm worker pool ahead of the build's serial per-package walk
|
|
250
|
+
(bounded to 3 processes -- concurrent npm invocations contend on npm's
|
|
251
|
+
shared content cache), which also keeps those installs' `.npm` churn
|
|
252
|
+
from re-firing the app runner's file watcher mid-build. Cache writes
|
|
253
|
+
happen in the background off the build's critical path, entries are
|
|
254
|
+
pruned to the 3 newest per package, and everything is best-effort: any
|
|
255
|
+
cache problem falls back to a normal npm install, and a stale restore is
|
|
256
|
+
caught by the existing shrinkwrap-vs-installed verification.
|
|
251
257
|
|
|
252
258
|
Control it with `METEOR_PACKAGE_NPM_CACHE`: unset means enabled at the
|
|
253
259
|
default location, `0`/`false`/`no`/`off` disables it, and any other
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/meteor-sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
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/project.js
CHANGED
|
@@ -65,6 +65,19 @@ async function loadProject(projectDir) {
|
|
|
65
65
|
projectDir: standardProjectDir,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
// Second-stage npm-deps prewarm (bootstrap.js fired the restore-only
|
|
69
|
+
// first stage): now that an app is known, its .meteor/versions names
|
|
70
|
+
// the exact core-package set the build will need, so cache MISSES can
|
|
71
|
+
// be npm-installed too -- in parallel (small pool; concurrent npm
|
|
72
|
+
// processes contend on npm's shared content cache) and BEFORE the
|
|
73
|
+
// build's serial per-package walk, which also keeps those installs'
|
|
74
|
+
// .npm churn from re-firing the app runner's file watcher mid-build.
|
|
75
|
+
// Un-awaited: every prewarm operation holds the same cross-process
|
|
76
|
+
// per-package lock updateDependencies() takes, so the build below
|
|
77
|
+
// simply waits on (or wins) each package's lock. Best-effort by
|
|
78
|
+
// design -- on any failure the build installs serially as before.
|
|
79
|
+
startAppNpmDepsPrewarm(standardProjectDir);
|
|
80
|
+
|
|
68
81
|
await runCaptured("preparing project", () =>
|
|
69
82
|
projectContext.prepareProjectForBuild()
|
|
70
83
|
);
|
|
@@ -72,4 +85,31 @@ async function loadProject(projectDir) {
|
|
|
72
85
|
return new Project(projectContext);
|
|
73
86
|
}
|
|
74
87
|
|
|
88
|
+
function startAppNpmDepsPrewarm(standardProjectDir) {
|
|
89
|
+
try {
|
|
90
|
+
const files = require("../tools/fs/files");
|
|
91
|
+
const versionsFile = files.pathJoin(
|
|
92
|
+
standardProjectDir, ".meteor", "versions");
|
|
93
|
+
// name@version lines; author:package names never match the bundled
|
|
94
|
+
// core-package directory names, so no special-casing needed. A
|
|
95
|
+
// missing versions file (brand-new app) just skips this stage.
|
|
96
|
+
const names = files.readFile(versionsFile, "utf8")
|
|
97
|
+
.split("\n")
|
|
98
|
+
.map((line) => line.trim().split("@")[0])
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
if (!names.length) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const meteorNpm = require("../tools/isobuild/meteor-npm.js");
|
|
104
|
+
Promise.resolve(
|
|
105
|
+
meteorNpm.prewarmNpmDepsCache(
|
|
106
|
+
path.resolve(__dirname, "..", "packages"),
|
|
107
|
+
{ onlyPackages: names, installOnMiss: true }
|
|
108
|
+
)
|
|
109
|
+
).catch(() => {});
|
|
110
|
+
} catch (e) {
|
|
111
|
+
// Best-effort: the build's own per-package install path still works.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
75
115
|
module.exports = { loadProject, Project };
|
|
@@ -893,6 +893,71 @@ function pruneNpmDepsCache(packageCacheDir, keepDir) {
|
|
|
893
893
|
} catch (e) {}
|
|
894
894
|
}
|
|
895
895
|
|
|
896
|
+
// Install a package's npm deps purely from its shipped
|
|
897
|
+
// npm-shrinkwrap.json -- no evaluated Npm.depends needed, which is what
|
|
898
|
+
// makes prewarm-time (pre-package.js) installs possible. Mirrors
|
|
899
|
+
// updateExistingNpmDirectory's preserved-shrinkwrap branch exactly:
|
|
900
|
+
// mapped shrinkwrap + minimal package.json into a temp dir, `npm
|
|
901
|
+
// install`, then completeNpmDirectory (which also seeds the cache).
|
|
902
|
+
// The top-level dependency map is derived from the shrinkwrap itself
|
|
903
|
+
// (exact versions; resolved URLs for non-registry deps) -- a superset
|
|
904
|
+
// of the real Npm.depends, which npm accepts since every entry is
|
|
905
|
+
// pinned to what the lock installs. If the shipped shrinkwrap turns
|
|
906
|
+
// out stale relative to the package's actual Npm.depends, the build's
|
|
907
|
+
// own subtree verification catches it and reinstalls normally.
|
|
908
|
+
// Callers must hold the npm package lock for packageNpmDir.
|
|
909
|
+
async function preinstallNpmDepsFromShrinkwrap(packageName, packageNpmDir) {
|
|
910
|
+
const shrinkwrappedDependenciesTree =
|
|
911
|
+
getShrinkwrappedDependenciesTree(packageNpmDir);
|
|
912
|
+
const npmDependencies = treeToDependencies(
|
|
913
|
+
minimizeDependencyTree(shrinkwrappedDependenciesTree));
|
|
914
|
+
if (_.isEmpty(npmDependencies)) {
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
const cacheKey = npmDepsCacheKey(packageNpmDir);
|
|
918
|
+
|
|
919
|
+
const newPackageNpmDir =
|
|
920
|
+
convertColonsInPath(packageNpmDir) + '-prewarm-' + utils.randomToken();
|
|
921
|
+
tmpDirs.push(newPackageNpmDir);
|
|
922
|
+
try {
|
|
923
|
+
makeNewPackageNpmDir(newPackageNpmDir);
|
|
924
|
+
|
|
925
|
+
const mappedDependencies = Object.entries(
|
|
926
|
+
shrinkwrappedDependenciesTree.dependencies
|
|
927
|
+
).reduce((acc, [name, info]) => {
|
|
928
|
+
return { ...acc, [`node_modules/${name}`]: info };
|
|
929
|
+
}, {});
|
|
930
|
+
|
|
931
|
+
files.writeFile(
|
|
932
|
+
files.pathJoin(newPackageNpmDir, 'npm-shrinkwrap.json'),
|
|
933
|
+
JSON.stringify({
|
|
934
|
+
...shrinkwrappedDependenciesTree,
|
|
935
|
+
dependencies: mappedDependencies,
|
|
936
|
+
}, null, 2)
|
|
937
|
+
);
|
|
938
|
+
files.writeFile(
|
|
939
|
+
files.pathJoin(newPackageNpmDir, 'package.json'),
|
|
940
|
+
JSON.stringify({ dependencies: npmDependencies }, null, 2)
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
logUpdateDependencies(packageName, npmDependencies);
|
|
944
|
+
await installFromShrinkwrap(newPackageNpmDir);
|
|
945
|
+
|
|
946
|
+
files.unlink(files.pathJoin(newPackageNpmDir, 'npm-shrinkwrap.json'));
|
|
947
|
+
files.unlink(files.pathJoin(newPackageNpmDir, 'package.json'));
|
|
948
|
+
|
|
949
|
+
await completeNpmDirectory(
|
|
950
|
+
packageName, newPackageNpmDir, packageNpmDir, npmDependencies,
|
|
951
|
+
cacheKey);
|
|
952
|
+
return true;
|
|
953
|
+
} finally {
|
|
954
|
+
if (files.exists(newPackageNpmDir)) {
|
|
955
|
+
await files.rm_recursive_deferred(newPackageNpmDir);
|
|
956
|
+
}
|
|
957
|
+
tmpDirs = _.without(tmpDirs, newPackageNpmDir);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
896
961
|
// Restore every restorable package under packagesDir from the cache, in
|
|
897
962
|
// parallel, before the (serial) per-package build pipeline reaches
|
|
898
963
|
// them. Purely filesystem-driven: a candidate is any package dir with a
|
|
@@ -900,17 +965,34 @@ function pruneNpmDepsCache(packageCacheDir, keepDir) {
|
|
|
900
965
|
// package.js evaluation is needed. Safe to run concurrently with a
|
|
901
966
|
// build -- each restore holds the same cross-process lock
|
|
902
967
|
// updateDependencies() takes, and re-checks its precondition under it.
|
|
903
|
-
//
|
|
968
|
+
//
|
|
969
|
+
// Options:
|
|
970
|
+
// - onlyPackages: array of package names to consider (REQUIRED for
|
|
971
|
+
// installOnMiss -- without a scope, cold-cache installs would cover
|
|
972
|
+
// every bundled package instead of just the app's).
|
|
973
|
+
// - installOnMiss: after the restore phase, npm-install the remaining
|
|
974
|
+
// misses from their shipped shrinkwraps, in parallel.
|
|
975
|
+
// - concurrency (restore pool, default 8), installConcurrency (npm
|
|
976
|
+
// pool, default 3 -- kept small because concurrent npm processes
|
|
977
|
+
// contend on npm's shared content cache).
|
|
978
|
+
//
|
|
979
|
+
// Returns {restored, installed}.
|
|
904
980
|
meteorNpm.prewarmNpmDepsCache = async function (packagesDir, options) {
|
|
905
981
|
options = options || {};
|
|
982
|
+
const none = { restored: 0, installed: 0 };
|
|
906
983
|
if (npmDepsCacheDisabled()) {
|
|
907
|
-
return
|
|
984
|
+
return none;
|
|
908
985
|
}
|
|
909
986
|
packagesDir = files.convertToStandardPath(packagesDir);
|
|
910
987
|
|
|
988
|
+
const only = options.onlyPackages &&
|
|
989
|
+
new Set(options.onlyPackages);
|
|
911
990
|
const candidates = [];
|
|
912
991
|
try {
|
|
913
992
|
files.readdir(packagesDir).forEach(name => {
|
|
993
|
+
if (only && ! only.has(name)) {
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
914
996
|
const packageNpmDir =
|
|
915
997
|
files.pathJoin(packagesDir, name, ".npm", "package");
|
|
916
998
|
if (! files.exists(
|
|
@@ -924,45 +1006,60 @@ meteorNpm.prewarmNpmDepsCache = async function (packagesDir, options) {
|
|
|
924
1006
|
candidates.push({ name, packageNpmDir });
|
|
925
1007
|
});
|
|
926
1008
|
} catch (e) {
|
|
927
|
-
return
|
|
1009
|
+
return none;
|
|
928
1010
|
}
|
|
929
1011
|
if (! candidates.length) {
|
|
930
|
-
return
|
|
1012
|
+
return none;
|
|
931
1013
|
}
|
|
932
1014
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1015
|
+
function installedAlready(candidate) {
|
|
1016
|
+
return files.exists(files.pathJoin(
|
|
1017
|
+
candidate.packageNpmDir, "node_modules", ".package-lock.json"));
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
async function runPool(queue, size, fn) {
|
|
1021
|
+
let nextIndex = 0;
|
|
1022
|
+
let completed = 0;
|
|
1023
|
+
async function worker() {
|
|
1024
|
+
while (nextIndex < queue.length) {
|
|
1025
|
+
const candidate = queue[nextIndex++];
|
|
1026
|
+
try {
|
|
1027
|
+
// Re-check under the lock: a concurrent build (or the other
|
|
1028
|
+
// prewarm phase) may have handled this package already.
|
|
1029
|
+
const ok = await withNpmPackageLock(
|
|
1030
|
+
candidate.packageNpmDir,
|
|
1031
|
+
async () => ! installedAlready(candidate) && fn(candidate)
|
|
1032
|
+
);
|
|
1033
|
+
if (ok) {
|
|
1034
|
+
completed++;
|
|
1035
|
+
}
|
|
1036
|
+
} catch (e) {
|
|
1037
|
+
// Best-effort; the build's own updateDependencies() will
|
|
1038
|
+
// handle this package the normal way.
|
|
1039
|
+
if (options.debugThrow) {
|
|
1040
|
+
console.error("prewarm error for " + candidate.name + ":",
|
|
1041
|
+
(e && e.stack) || e);
|
|
951
1042
|
}
|
|
952
|
-
);
|
|
953
|
-
if (ok) {
|
|
954
|
-
restored++;
|
|
955
1043
|
}
|
|
956
|
-
} catch (e) {
|
|
957
|
-
// Best-effort; the build's own updateDependencies() will handle
|
|
958
|
-
// this package the normal way.
|
|
959
1044
|
}
|
|
960
1045
|
}
|
|
1046
|
+
await Promise.all(
|
|
1047
|
+
_.times(Math.min(size, queue.length || 1), worker));
|
|
1048
|
+
return completed;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const restored = await runPool(
|
|
1052
|
+
candidates, options.concurrency || 8,
|
|
1053
|
+
c => restoreNodeModulesFromNpmDepsCache(c.name, c.packageNpmDir));
|
|
1054
|
+
|
|
1055
|
+
let installed = 0;
|
|
1056
|
+
if (options.installOnMiss && only) {
|
|
1057
|
+
const misses = candidates.filter(c => ! installedAlready(c));
|
|
1058
|
+
installed = await runPool(
|
|
1059
|
+
misses, options.installConcurrency || 3,
|
|
1060
|
+
c => preinstallNpmDepsFromShrinkwrap(c.name, c.packageNpmDir));
|
|
961
1061
|
}
|
|
962
|
-
|
|
963
|
-
options.concurrency || 8, candidates.length);
|
|
964
|
-
await Promise.all(_.times(concurrency, worker));
|
|
965
|
-
return restored;
|
|
1062
|
+
return { restored, installed };
|
|
966
1063
|
};
|
|
967
1064
|
|
|
968
1065
|
var updateExistingNpmDirectory = async function (packageName, newPackageNpmDir,
|