@elementspanel/epanel-plugin-instance 1.0.0 → 1.0.1
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/daemon/backend/index.cjs +576 -66
- package/daemon/plugin.json +1 -4
- package/package.json +13 -1
- package/panel/backend/index.cjs +582 -72
- package/panel/plugin.json +2 -11
package/panel/backend/index.cjs
CHANGED
|
@@ -109,7 +109,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
109
109
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
110
110
|
};
|
|
111
111
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
112
|
-
exports.StorageSubsystem = exports.MCServerStatus = exports.InstanceStreamListener = exports.GlobalVariable = exports.normalizeDockerPlatform = exports.normalizeDockerOS = exports.normalizeDockerArchitecture = exports.removeTrail = exports.uninstallNpmPlugin = exports.npmPluginRoots = exports.listNpmPlugins = exports.installNpmPlugin = exports.currentProjectRoot = exports.currentPackageRoot = exports.sortPlugins = exports.resolvePluginEntry = exports.readPluginManifest = exports.createFrontendPluginMetadata = exports.discoverPluginsFromRoots = exports.discoverPlugins = exports.arrayUnique = exports.toText = exports.toNumber = exports.toBoolean = exports.supposeValue = exports.isEmpty = exports.configureEntityParams = exports.systemInfo = exports.QueryWrapper = exports.QueryMapWrapper = exports.MySqlSource = exports.LocalFileSource = exports.killProcess = exports.ProcessWrapper = void 0;
|
|
112
|
+
exports.StorageSubsystem = exports.MCServerStatus = exports.InstanceStreamListener = exports.GlobalVariable = exports.normalizeDockerPlatform = exports.normalizeDockerOS = exports.normalizeDockerArchitecture = exports.removeTrail = exports.uninstallNpmPlugin = exports.isEssentialNpmPlugin = exports.npmPluginOperationPending = exports.npmPluginRoots = exports.listNpmPlugins = exports.installNpmPlugin = exports.currentProjectRoot = exports.currentPackageRoot = exports.sortPlugins = exports.resolvePluginEntry = exports.readPluginManifest = exports.createFrontendPluginMetadata = exports.discoverPluginsFromRoots = exports.discoverPlugins = exports.arrayUnique = exports.toText = exports.toNumber = exports.toBoolean = exports.supposeValue = exports.isEmpty = exports.configureEntityParams = exports.systemInfo = exports.QueryWrapper = exports.QueryMapWrapper = exports.MySqlSource = exports.LocalFileSource = exports.killProcess = exports.ProcessWrapper = void 0;
|
|
113
113
|
const global_variable_1 = __importDefault(__webpack_require__("../common/src/global_variable.ts"));
|
|
114
114
|
exports.GlobalVariable = global_variable_1.default;
|
|
115
115
|
const instance_stream_1 = __importDefault(__webpack_require__("../common/src/instance_stream.ts"));
|
|
@@ -141,6 +141,8 @@ __exportStar(__webpack_require__("../common/src/plugin_contract.ts"), exports);
|
|
|
141
141
|
__exportStar(__webpack_require__("../common/src/plugin_overrides.ts"), exports);
|
|
142
142
|
__exportStar(__webpack_require__("../common/src/plugin_lifecycle.ts"), exports);
|
|
143
143
|
__exportStar(__webpack_require__("../common/src/plugin_revision.ts"), exports);
|
|
144
|
+
__exportStar(__webpack_require__("../common/src/plugin_modules.ts"), exports);
|
|
145
|
+
__exportStar(__webpack_require__("../common/src/plugin_watch.ts"), exports);
|
|
144
146
|
__exportStar(__webpack_require__("../common/src/runtime_paths.ts"), exports);
|
|
145
147
|
var plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
|
|
146
148
|
Object.defineProperty(exports, "discoverPlugins", ({ enumerable: true, get: function () { return plugin_manifest_1.discoverPlugins; } }));
|
|
@@ -155,6 +157,8 @@ Object.defineProperty(exports, "currentProjectRoot", ({ enumerable: true, get: f
|
|
|
155
157
|
Object.defineProperty(exports, "installNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.installNpmPlugin; } }));
|
|
156
158
|
Object.defineProperty(exports, "listNpmPlugins", ({ enumerable: true, get: function () { return plugin_npm_1.listNpmPlugins; } }));
|
|
157
159
|
Object.defineProperty(exports, "npmPluginRoots", ({ enumerable: true, get: function () { return plugin_npm_1.npmPluginRoots; } }));
|
|
160
|
+
Object.defineProperty(exports, "npmPluginOperationPending", ({ enumerable: true, get: function () { return plugin_npm_1.npmPluginOperationPending; } }));
|
|
161
|
+
Object.defineProperty(exports, "isEssentialNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.isEssentialNpmPlugin; } }));
|
|
158
162
|
Object.defineProperty(exports, "uninstallNpmPlugin", ({ enumerable: true, get: function () { return plugin_npm_1.uninstallNpmPlugin; } }));
|
|
159
163
|
var string_utils_1 = __webpack_require__("../common/src/string_utils.ts");
|
|
160
164
|
Object.defineProperty(exports, "removeTrail", ({ enumerable: true, get: function () { return string_utils_1.removeTrail; } }));
|
|
@@ -671,7 +675,45 @@ function createFrontendPluginMetadata(manifest, extras = {}) {
|
|
|
671
675
|
};
|
|
672
676
|
}
|
|
673
677
|
const MANIFEST_FILES = ["plugin.json", "manifest.json", "package.json"];
|
|
674
|
-
|
|
678
|
+
const ENTRY_FIELDS = ["backend", "frontend", "panel", "daemon", "main", "entry", "ui"];
|
|
679
|
+
function objectValue(value) {
|
|
680
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
681
|
+
? value
|
|
682
|
+
: {};
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Source plugins keep package.json beside plugin.json. Published npm plugins
|
|
686
|
+
* keep it at the package root, with runtime metadata under elementspanel.<side>.
|
|
687
|
+
* Stop at the first package boundary so a host's metadata cannot reach a plugin.
|
|
688
|
+
*/
|
|
689
|
+
function readPackageMetadata(directory, id) {
|
|
690
|
+
const pluginDirectory = node_path_1.default.resolve(directory);
|
|
691
|
+
let current = pluginDirectory;
|
|
692
|
+
while (true) {
|
|
693
|
+
const filename = node_path_1.default.join(current, "package.json");
|
|
694
|
+
if (node_fs_1.default.existsSync(filename)) {
|
|
695
|
+
const { elementspanel, ...metadata } = objectValue(JSON.parse(node_fs_1.default.readFileSync(filename, "utf8")));
|
|
696
|
+
const { id: packageId, panel, daemon, ...shared } = objectValue(elementspanel);
|
|
697
|
+
const definition = [panel, daemon]
|
|
698
|
+
.map(objectValue)
|
|
699
|
+
.find((side) => typeof side.directory === "string" &&
|
|
700
|
+
node_path_1.default.resolve(current, side.directory) === pluginDirectory);
|
|
701
|
+
if (current !== pluginDirectory && (packageId !== id || !definition))
|
|
702
|
+
return {};
|
|
703
|
+
const { directory: _directory, folder: _folder, ...sideMetadata } = definition !== null && definition !== void 0 ? definition : {};
|
|
704
|
+
const result = { ...metadata, ...shared, ...sideMetadata };
|
|
705
|
+
// npm's main/entry fields do not define a side's execution entry.
|
|
706
|
+
for (const field of ["dual", ...ENTRY_FIELDS])
|
|
707
|
+
delete result[field];
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
const parent = node_path_1.default.dirname(current);
|
|
711
|
+
if (parent === current)
|
|
712
|
+
return {};
|
|
713
|
+
current = parent;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/** Merges package metadata with a plugin's identity and entries. Legacy manifests still load. */
|
|
675
717
|
function readPluginManifest(directory, onWarning, fallbackId = node_path_1.default.basename(directory)) {
|
|
676
718
|
for (const file of MANIFEST_FILES) {
|
|
677
719
|
const filePath = node_path_1.default.join(directory, file);
|
|
@@ -684,7 +726,18 @@ function readPluginManifest(directory, onWarning, fallbackId = node_path_1.defau
|
|
|
684
726
|
const id = typeof value.id === "string" ? value.id : fallbackId;
|
|
685
727
|
if (!id.trim())
|
|
686
728
|
return null;
|
|
687
|
-
|
|
729
|
+
const manifest = {
|
|
730
|
+
...value,
|
|
731
|
+
...readPackageMetadata(directory, id.trim()),
|
|
732
|
+
id: id.trim()
|
|
733
|
+
};
|
|
734
|
+
// Identity and execution paths always belong to plugin.json. Package
|
|
735
|
+
// metadata wins for descriptions, priority, configuration and dependencies.
|
|
736
|
+
for (const field of ["version", "dual", ...ENTRY_FIELDS]) {
|
|
737
|
+
if (value[field] !== undefined)
|
|
738
|
+
manifest[field] = value[field];
|
|
739
|
+
}
|
|
740
|
+
return manifest;
|
|
688
741
|
}
|
|
689
742
|
catch (error) {
|
|
690
743
|
onWarning === null || onWarning === void 0 ? void 0 : onWarning(`Failed to read plugin manifest: ${filePath}`, error);
|
|
@@ -786,6 +839,13 @@ function discoverPluginsFromRoots(roots, options) {
|
|
|
786
839
|
discovered.push({
|
|
787
840
|
...plugin,
|
|
788
841
|
manifest,
|
|
842
|
+
...(root.packageRoot
|
|
843
|
+
? {
|
|
844
|
+
packageRoot: root.packageRoot,
|
|
845
|
+
packageName: root.packageName,
|
|
846
|
+
npmSource: root.npmSource
|
|
847
|
+
}
|
|
848
|
+
: {}),
|
|
789
849
|
...(root.folder ? { folder: root.folder } : {})
|
|
790
850
|
});
|
|
791
851
|
}
|
|
@@ -799,6 +859,178 @@ function sortPlugins(plugins) {
|
|
|
799
859
|
}
|
|
800
860
|
|
|
801
861
|
|
|
862
|
+
/***/ },
|
|
863
|
+
|
|
864
|
+
/***/ "../common/src/plugin_modules.ts"
|
|
865
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
869
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
870
|
+
};
|
|
871
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
872
|
+
exports.pluginBackendRevision = pluginBackendRevision;
|
|
873
|
+
exports.forgetPluginModules = forgetPluginModules;
|
|
874
|
+
exports.loadPluginModule = loadPluginModule;
|
|
875
|
+
const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
|
|
876
|
+
const node_path_1 = __importDefault(__webpack_require__("node:path"));
|
|
877
|
+
const node_url_1 = __webpack_require__("node:url");
|
|
878
|
+
const node_crypto_1 = __webpack_require__("node:crypto");
|
|
879
|
+
const node_module_1 = __webpack_require__("node:module");
|
|
880
|
+
const plugin_revision_1 = __webpack_require__("../common/src/plugin_revision.ts");
|
|
881
|
+
const REVISION = "epanelPluginRevision";
|
|
882
|
+
const graphs = new Map();
|
|
883
|
+
const previousRoots = new Map();
|
|
884
|
+
let hooksInstalled = false;
|
|
885
|
+
const sharedModules = new Set([
|
|
886
|
+
"cordis",
|
|
887
|
+
"@cordisjs/core",
|
|
888
|
+
"@cordisjs/logger",
|
|
889
|
+
"@cordisjs/timer",
|
|
890
|
+
"@cordisjs/schema",
|
|
891
|
+
"cosmokit",
|
|
892
|
+
"schemastery",
|
|
893
|
+
"reggol"
|
|
894
|
+
]);
|
|
895
|
+
function dependencyRoots(directory) {
|
|
896
|
+
const visited = new Set([directory]);
|
|
897
|
+
const walk = (root) => {
|
|
898
|
+
const filename = node_path_1.default.join(root, "package.json");
|
|
899
|
+
if (!node_fs_1.default.existsSync(filename))
|
|
900
|
+
return;
|
|
901
|
+
const metadata = JSON.parse(node_fs_1.default.readFileSync(filename, "utf8"));
|
|
902
|
+
const localRequire = (0, node_module_1.createRequire)(filename);
|
|
903
|
+
for (const name of Object.keys({
|
|
904
|
+
...metadata.dependencies,
|
|
905
|
+
...metadata.optionalDependencies
|
|
906
|
+
})) {
|
|
907
|
+
if (sharedModules.has(name))
|
|
908
|
+
continue;
|
|
909
|
+
const target = (localRequire.resolve.paths(name) || [])
|
|
910
|
+
.map((directory) => node_path_1.default.join(directory, name, "package.json"))
|
|
911
|
+
.find((candidate) => node_fs_1.default.existsSync(candidate));
|
|
912
|
+
if (!target)
|
|
913
|
+
continue;
|
|
914
|
+
const dependency = node_fs_1.default.realpathSync(node_path_1.default.dirname(target));
|
|
915
|
+
if (visited.has(dependency))
|
|
916
|
+
continue;
|
|
917
|
+
visited.add(dependency);
|
|
918
|
+
walk(dependency);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
walk(directory);
|
|
922
|
+
visited.delete(directory);
|
|
923
|
+
return [...visited];
|
|
924
|
+
}
|
|
925
|
+
/** Include transitive dependency versions so npm updates replace the affected graph. */
|
|
926
|
+
function pluginBackendRevision(entry, directory) {
|
|
927
|
+
const hash = (0, node_crypto_1.createHash)("sha256").update((0, plugin_revision_1.pluginDirectoryRevision)(node_path_1.default.dirname(entry)));
|
|
928
|
+
for (const root of [directory, ...dependencyRoots(directory)]) {
|
|
929
|
+
const filename = node_path_1.default.join(root, "package.json");
|
|
930
|
+
if (node_fs_1.default.existsSync(filename))
|
|
931
|
+
hash.update(root).update(node_fs_1.default.readFileSync(filename));
|
|
932
|
+
}
|
|
933
|
+
return hash.digest("hex").slice(0, 24);
|
|
934
|
+
}
|
|
935
|
+
function within(root, filename) {
|
|
936
|
+
const relative = node_path_1.default.relative(root, filename);
|
|
937
|
+
return (relative === "" ||
|
|
938
|
+
(!relative.startsWith(`..${node_path_1.default.sep}`) && relative !== ".." && !node_path_1.default.isAbsolute(relative)));
|
|
939
|
+
}
|
|
940
|
+
/** Clear the plugin's complete CJS graph, keeping shared host modules intact. */
|
|
941
|
+
function forgetPluginModules(directory) {
|
|
942
|
+
const runtimeRequire = eval("require");
|
|
943
|
+
const owned = [directory, ...(previousRoots.get(directory) || []), ...dependencyRoots(directory)];
|
|
944
|
+
previousRoots.delete(directory);
|
|
945
|
+
const belongs = (filename) => {
|
|
946
|
+
const parts = filename.split(`${node_path_1.default.sep}node_modules${node_path_1.default.sep}`);
|
|
947
|
+
if (parts.slice(1).some((part) => {
|
|
948
|
+
const name = part.startsWith("@")
|
|
949
|
+
? part.split(node_path_1.default.sep).slice(0, 2).join("/")
|
|
950
|
+
: part.split(node_path_1.default.sep)[0];
|
|
951
|
+
return sharedModules.has(name);
|
|
952
|
+
}))
|
|
953
|
+
return false;
|
|
954
|
+
return owned.some((root) => within(root, filename));
|
|
955
|
+
};
|
|
956
|
+
for (const filename of Object.keys(runtimeRequire.cache)) {
|
|
957
|
+
if (belongs(filename))
|
|
958
|
+
delete runtimeRequire.cache[filename];
|
|
959
|
+
}
|
|
960
|
+
for (const mod of Object.values(runtimeRequire.cache)) {
|
|
961
|
+
if (mod)
|
|
962
|
+
mod.children = mod.children.filter((child) => !belongs(child.filename));
|
|
963
|
+
}
|
|
964
|
+
// npm can move a dependency or change its main entry. Node's CJS resolver
|
|
965
|
+
// retains those paths separately from require.cache.
|
|
966
|
+
const pathCache = runtimeRequire("node:module")._pathCache;
|
|
967
|
+
for (const [key, filename] of Object.entries(pathCache)) {
|
|
968
|
+
if (belongs(filename) || owned.some((root) => key.includes(root)))
|
|
969
|
+
delete pathCache[key];
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function isEsm(entry) {
|
|
973
|
+
if (entry.endsWith(".mjs"))
|
|
974
|
+
return true;
|
|
975
|
+
if (entry.endsWith(".cjs"))
|
|
976
|
+
return false;
|
|
977
|
+
let directory = node_path_1.default.dirname(entry);
|
|
978
|
+
while (true) {
|
|
979
|
+
const filename = node_path_1.default.join(directory, "package.json");
|
|
980
|
+
if (node_fs_1.default.existsSync(filename))
|
|
981
|
+
return JSON.parse(node_fs_1.default.readFileSync(filename, "utf8")).type === "module";
|
|
982
|
+
const parent = node_path_1.default.dirname(directory);
|
|
983
|
+
if (parent === directory)
|
|
984
|
+
return false;
|
|
985
|
+
directory = parent;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/** Give an ESM entry and its local imports the same immutable revision URL. */
|
|
989
|
+
async function loadPluginModule(entry, directory, revision) {
|
|
990
|
+
const runtimeRequire = eval("require");
|
|
991
|
+
previousRoots.set(directory, dependencyRoots(directory));
|
|
992
|
+
if (!isEsm(entry)) {
|
|
993
|
+
try {
|
|
994
|
+
return runtimeRequire(entry);
|
|
995
|
+
}
|
|
996
|
+
catch (error) {
|
|
997
|
+
if ((error === null || error === void 0 ? void 0 : error.code) !== "ERR_REQUIRE_ESM")
|
|
998
|
+
throw error;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
graphs.set(revision, [directory, ...dependencyRoots(directory)]);
|
|
1002
|
+
if (!hooksInstalled) {
|
|
1003
|
+
const { registerHooks } = runtimeRequire("node:module");
|
|
1004
|
+
if (typeof registerHooks !== "function")
|
|
1005
|
+
throw new Error("ESM plugin updates require Node.js 22.15 or newer.");
|
|
1006
|
+
registerHooks({
|
|
1007
|
+
resolve(specifier, context, nextResolve) {
|
|
1008
|
+
var _a;
|
|
1009
|
+
const resolved = nextResolve(specifier, context);
|
|
1010
|
+
if (!((_a = context.parentURL) === null || _a === void 0 ? void 0 : _a.startsWith("file:")) || !resolved.url.startsWith("file:"))
|
|
1011
|
+
return resolved;
|
|
1012
|
+
const parent = new URL(context.parentURL);
|
|
1013
|
+
const parentRevision = parent.searchParams.get(REVISION);
|
|
1014
|
+
if (!parentRevision)
|
|
1015
|
+
return resolved;
|
|
1016
|
+
const roots = graphs.get(parentRevision);
|
|
1017
|
+
if (!(roots === null || roots === void 0 ? void 0 : roots.some((root) => within(root, (0, node_url_1.fileURLToPath)(parent)))) ||
|
|
1018
|
+
!roots.some((root) => within(root, (0, node_url_1.fileURLToPath)(resolved.url))))
|
|
1019
|
+
return resolved;
|
|
1020
|
+
const url = new URL(resolved.url);
|
|
1021
|
+
url.searchParams.set(REVISION, parentRevision);
|
|
1022
|
+
return { ...resolved, url: url.href };
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
hooksInstalled = true;
|
|
1026
|
+
}
|
|
1027
|
+
const url = (0, node_url_1.pathToFileURL)(entry);
|
|
1028
|
+
url.searchParams.set(REVISION, revision);
|
|
1029
|
+
const dynamicImport = new Function("specifier", "return import(specifier)");
|
|
1030
|
+
return dynamicImport(url.href);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
|
|
802
1034
|
/***/ },
|
|
803
1035
|
|
|
804
1036
|
/***/ "../common/src/plugin_npm.ts"
|
|
@@ -808,20 +1040,25 @@ function sortPlugins(plugins) {
|
|
|
808
1040
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
809
1041
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
810
1042
|
};
|
|
1043
|
+
var _a;
|
|
811
1044
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
812
1045
|
exports.currentPackageRoot = currentPackageRoot;
|
|
813
1046
|
exports.currentProjectRoot = currentProjectRoot;
|
|
814
1047
|
exports.npmPluginRoots = npmPluginRoots;
|
|
815
1048
|
exports.listNpmPlugins = listNpmPlugins;
|
|
1049
|
+
exports.npmPluginOperationPending = npmPluginOperationPending;
|
|
1050
|
+
exports.isEssentialNpmPlugin = isEssentialNpmPlugin;
|
|
816
1051
|
exports.installNpmPlugin = installNpmPlugin;
|
|
817
1052
|
exports.uninstallNpmPlugin = uninstallNpmPlugin;
|
|
818
1053
|
const child_process_1 = __webpack_require__("child_process");
|
|
819
1054
|
const fs_extra_1 = __importDefault(__webpack_require__("fs-extra"));
|
|
820
1055
|
const path_1 = __importDefault(__webpack_require__("path"));
|
|
821
1056
|
const module_1 = __webpack_require__("module");
|
|
1057
|
+
const plugin_manifest_1 = __webpack_require__("../common/src/plugin_manifest.ts");
|
|
1058
|
+
const plugin_contract_1 = __webpack_require__("../common/src/plugin_contract.ts");
|
|
822
1059
|
const PLUGIN_ID = /^[a-z][a-z0-9_-]{1,63}$/;
|
|
823
1060
|
const PACKAGE_NAME = /^(?:@[a-z0-9_.-]+\/)?epanel-plugin-[a-z0-9_.-]+$/;
|
|
824
|
-
const VERSION = /^[
|
|
1061
|
+
const VERSION = /^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/;
|
|
825
1062
|
function readJson(filename) {
|
|
826
1063
|
try {
|
|
827
1064
|
const parsed = JSON.parse(fs_extra_1.default.readFileSync(filename, "utf8"));
|
|
@@ -891,8 +1128,20 @@ function pluginRecord(pluginId, name, source, packageRoot, directory, folder, ve
|
|
|
891
1128
|
return { pluginId, name, version, sides: [], directory, folder, packageRoot, source };
|
|
892
1129
|
}
|
|
893
1130
|
function readPackageEntry(packageRoot, name) {
|
|
894
|
-
|
|
895
|
-
|
|
1131
|
+
// Read package boundaries directly: Node caches require.resolve() paths even
|
|
1132
|
+
// after npm moves an installation between nested and hoisted node_modules.
|
|
1133
|
+
let manifestPath;
|
|
1134
|
+
for (const root of [packageRoot, currentProjectRoot(packageRoot)]) {
|
|
1135
|
+
const localRequire = (0, module_1.createRequire)(path_1.default.join(root, "package.json"));
|
|
1136
|
+
manifestPath = (localRequire.resolve.paths(name) || [])
|
|
1137
|
+
.map((directory) => path_1.default.join(directory, name, "package.json"))
|
|
1138
|
+
.find((filename) => fs_extra_1.default.existsSync(filename));
|
|
1139
|
+
if (manifestPath)
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
if (!manifestPath)
|
|
1143
|
+
throw new Error(`Missing npm plugin package: ${name}.`);
|
|
1144
|
+
manifestPath = fs_extra_1.default.realpathSync(manifestPath);
|
|
896
1145
|
const metadata = readJson(manifestPath);
|
|
897
1146
|
if (!metadata)
|
|
898
1147
|
throw new Error(`Missing package metadata for ${name}.`);
|
|
@@ -906,7 +1155,10 @@ function sideDirectory(packageRoot, metadata, side) {
|
|
|
906
1155
|
return undefined;
|
|
907
1156
|
const target = path_1.default.resolve(packageRoot, directory);
|
|
908
1157
|
const relative = path_1.default.relative(packageRoot, target);
|
|
909
|
-
if (relative === "" ||
|
|
1158
|
+
if (relative === "" ||
|
|
1159
|
+
relative === ".." ||
|
|
1160
|
+
relative.startsWith(`..${path_1.default.sep}`) ||
|
|
1161
|
+
path_1.default.isAbsolute(relative))
|
|
910
1162
|
return undefined;
|
|
911
1163
|
return target;
|
|
912
1164
|
}
|
|
@@ -924,7 +1176,7 @@ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side)
|
|
|
924
1176
|
return undefined;
|
|
925
1177
|
const directory = sideDirectory(packageRoot, metadata, target);
|
|
926
1178
|
const folder = stringValue((_a = objectValue(elements[target])) === null || _a === void 0 ? void 0 : _a.folder);
|
|
927
|
-
if (!directory || !folder)
|
|
1179
|
+
if (!directory || !PLUGIN_ID.test(folder))
|
|
928
1180
|
return undefined;
|
|
929
1181
|
return {
|
|
930
1182
|
...pluginRecord(pluginId, name, source, packageRoot, directory, folder, version),
|
|
@@ -940,14 +1192,29 @@ function recordFromPackage(pluginId, name, source, packageRoot, metadata, side)
|
|
|
940
1192
|
* is what lets a two-sided project share one dependency list between sides.
|
|
941
1193
|
*/
|
|
942
1194
|
function listNpmPlugins(side, options = {}) {
|
|
943
|
-
var _a;
|
|
1195
|
+
var _a, _b;
|
|
944
1196
|
const { packageRoot, projectRoot } = npmPluginRoots(options);
|
|
945
1197
|
const host = readJson(path_1.default.join(packageRoot, "package.json"));
|
|
946
1198
|
if (!host)
|
|
947
1199
|
throw new Error(`Missing runtime package metadata: ${packageRoot}`);
|
|
948
|
-
const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
|
|
1200
|
+
const project = path_1.default.resolve(projectRoot) === path_1.default.resolve(packageRoot)
|
|
1201
|
+
? null
|
|
1202
|
+
: readProjectManifest(projectRoot);
|
|
949
1203
|
const hostMappings = mappingsOf(host);
|
|
950
|
-
const projectMappings = mappingsOf(project);
|
|
1204
|
+
const projectMappings = { ...mappingsOf(project) };
|
|
1205
|
+
for (const name of Object.keys(dependenciesOf(project))) {
|
|
1206
|
+
if (!PACKAGE_NAME.test(name) || Object.values(projectMappings).includes(name))
|
|
1207
|
+
continue;
|
|
1208
|
+
try {
|
|
1209
|
+
const entry = readPackageEntry(projectRoot, name);
|
|
1210
|
+
const id = stringValue((_a = objectValue(entry.metadata.elementspanel)) === null || _a === void 0 ? void 0 : _a.id);
|
|
1211
|
+
if (PLUGIN_ID.test(id) && projectMappings[id] === undefined)
|
|
1212
|
+
projectMappings[id] = name;
|
|
1213
|
+
}
|
|
1214
|
+
catch (_c) {
|
|
1215
|
+
/* npm may still be writing the package. */
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
951
1218
|
const mappings = new Map();
|
|
952
1219
|
for (const [pluginId, name] of Object.entries(projectMappings)) {
|
|
953
1220
|
if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
|
|
@@ -957,6 +1224,8 @@ function listNpmPlugins(side, options = {}) {
|
|
|
957
1224
|
for (const [pluginId, name] of Object.entries(hostMappings)) {
|
|
958
1225
|
if (!PLUGIN_ID.test(pluginId) || typeof name !== "string" || !PACKAGE_NAME.test(name))
|
|
959
1226
|
continue;
|
|
1227
|
+
if (dependenciesOf(host)[name] === undefined)
|
|
1228
|
+
continue;
|
|
960
1229
|
mappings.set(pluginId, { name, source: "bundled" });
|
|
961
1230
|
}
|
|
962
1231
|
const hostDependencies = dependenciesOf(host);
|
|
@@ -964,7 +1233,7 @@ function listNpmPlugins(side, options = {}) {
|
|
|
964
1233
|
const folders = new Set();
|
|
965
1234
|
const records = [];
|
|
966
1235
|
for (const [pluginId, mapping] of mappings) {
|
|
967
|
-
const declared = (
|
|
1236
|
+
const declared = (_b = hostDependencies[mapping.name]) !== null && _b !== void 0 ? _b : projectDependencies[mapping.name];
|
|
968
1237
|
if (declared === undefined) {
|
|
969
1238
|
if (options.skipInvalid)
|
|
970
1239
|
continue;
|
|
@@ -972,7 +1241,7 @@ function listNpmPlugins(side, options = {}) {
|
|
|
972
1241
|
}
|
|
973
1242
|
let entry;
|
|
974
1243
|
try {
|
|
975
|
-
entry = readPackageEntry(packageRoot, mapping.name);
|
|
1244
|
+
entry = readPackageEntry(mapping.source === "project" ? projectRoot : packageRoot, mapping.name);
|
|
976
1245
|
}
|
|
977
1246
|
catch (error) {
|
|
978
1247
|
if (options.skipInvalid)
|
|
@@ -1005,7 +1274,12 @@ function runNpm(args, cwd) {
|
|
|
1005
1274
|
: process.platform === "win32"
|
|
1006
1275
|
? {
|
|
1007
1276
|
command: process.env.ComSpec || "cmd.exe",
|
|
1008
|
-
args: [
|
|
1277
|
+
args: [
|
|
1278
|
+
"/d",
|
|
1279
|
+
"/s",
|
|
1280
|
+
"/c",
|
|
1281
|
+
`npm ${args.map((item) => `"${item.replace(/"/g, '""')}"`).join(" ")}`
|
|
1282
|
+
],
|
|
1009
1283
|
shell: false
|
|
1010
1284
|
}
|
|
1011
1285
|
: { command: "npm", args, shell: false };
|
|
@@ -1024,10 +1298,10 @@ function runNpm(args, cwd) {
|
|
|
1024
1298
|
};
|
|
1025
1299
|
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", append);
|
|
1026
1300
|
(_b = child.stderr) === null || _b === void 0 ? void 0 : _b.on("data", append);
|
|
1301
|
+
const action = args.find((argument) => argument === "install" || argument === "uninstall") || "operation";
|
|
1027
1302
|
const timer = setTimeout(() => {
|
|
1028
|
-
var _a;
|
|
1029
1303
|
child.kill("SIGKILL");
|
|
1030
|
-
reject(new Error(`npm ${
|
|
1304
|
+
reject(new Error(`npm ${action} timed out.`));
|
|
1031
1305
|
}, 10 * 60 * 1000);
|
|
1032
1306
|
(_c = timer.unref) === null || _c === void 0 ? void 0 : _c.call(timer);
|
|
1033
1307
|
child.once("error", (error) => {
|
|
@@ -1035,20 +1309,74 @@ function runNpm(args, cwd) {
|
|
|
1035
1309
|
reject(error);
|
|
1036
1310
|
});
|
|
1037
1311
|
child.once("close", (code) => {
|
|
1038
|
-
var _a;
|
|
1039
1312
|
clearTimeout(timer);
|
|
1040
1313
|
if (code === 0)
|
|
1041
1314
|
return resolve(output);
|
|
1042
|
-
reject(new Error(`npm ${
|
|
1315
|
+
reject(new Error(`npm ${action} failed (${code}): ${output.trim()}`));
|
|
1043
1316
|
});
|
|
1044
1317
|
});
|
|
1045
1318
|
}
|
|
1046
|
-
const
|
|
1319
|
+
const operationsKey = Symbol.for("elementspanel.npm.operations");
|
|
1320
|
+
const shared = globalThis;
|
|
1321
|
+
const operations = ((_a = shared[operationsKey]) !== null && _a !== void 0 ? _a : (shared[operationsKey] = new Map()));
|
|
1322
|
+
const LOCK_NAME = ".elementspanel-npm.lock";
|
|
1323
|
+
async function acquireProjectLock(projectRoot) {
|
|
1324
|
+
var _a, _b;
|
|
1325
|
+
await fs_extra_1.default.ensureDir(projectRoot);
|
|
1326
|
+
const directory = path_1.default.join(projectRoot, LOCK_NAME);
|
|
1327
|
+
const deadline = Date.now() + 11 * 60 * 1000;
|
|
1328
|
+
while (true) {
|
|
1329
|
+
try {
|
|
1330
|
+
await fs_extra_1.default.mkdir(directory);
|
|
1331
|
+
await fs_extra_1.default.writeJson(path_1.default.join(directory, "owner.json"), { pid: process.pid });
|
|
1332
|
+
return () => fs_extra_1.default.remove(directory);
|
|
1333
|
+
}
|
|
1334
|
+
catch (error) {
|
|
1335
|
+
if (error.code !== "EEXIST")
|
|
1336
|
+
throw error;
|
|
1337
|
+
try {
|
|
1338
|
+
const owner = await fs_extra_1.default.readJson(path_1.default.join(directory, "owner.json"));
|
|
1339
|
+
try {
|
|
1340
|
+
process.kill(owner.pid, 0);
|
|
1341
|
+
}
|
|
1342
|
+
catch (failure) {
|
|
1343
|
+
if (failure.code === "ESRCH") {
|
|
1344
|
+
await fs_extra_1.default.remove(directory);
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
catch (_c) {
|
|
1350
|
+
if (Date.now() - ((_b = (_a = (await fs_extra_1.default.stat(directory).catch(() => null))) === null || _a === void 0 ? void 0 : _a.mtimeMs) !== null && _b !== void 0 ? _b : Date.now()) >
|
|
1351
|
+
60000) {
|
|
1352
|
+
await fs_extra_1.default.remove(directory);
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (Date.now() > deadline)
|
|
1357
|
+
throw new Error("Another npm plugin operation is still running.");
|
|
1358
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
function npmPluginOperationPending() {
|
|
1363
|
+
const { packageRoot, projectRoot } = npmPluginRoots();
|
|
1364
|
+
return (operations.size > 0 ||
|
|
1365
|
+
[packageRoot, projectRoot].some((root) => fs_extra_1.default.existsSync(path_1.default.join(root, LOCK_NAME))));
|
|
1366
|
+
}
|
|
1047
1367
|
function withProjectLock(projectRoot, operation) {
|
|
1048
1368
|
var _a;
|
|
1049
1369
|
const key = process.platform === "win32" ? projectRoot.toLowerCase() : projectRoot;
|
|
1050
1370
|
const previous = (_a = operations.get(key)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
1051
|
-
const result = previous.then(
|
|
1371
|
+
const result = previous.then(async () => {
|
|
1372
|
+
const release = await acquireProjectLock(projectRoot);
|
|
1373
|
+
try {
|
|
1374
|
+
return await operation();
|
|
1375
|
+
}
|
|
1376
|
+
finally {
|
|
1377
|
+
await release();
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1052
1380
|
const settled = result.then(() => { }, () => { });
|
|
1053
1381
|
operations.set(key, settled);
|
|
1054
1382
|
return result.finally(() => {
|
|
@@ -1056,85 +1384,174 @@ function withProjectLock(projectRoot, operation) {
|
|
|
1056
1384
|
operations.delete(key);
|
|
1057
1385
|
});
|
|
1058
1386
|
}
|
|
1059
|
-
/**
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
*/
|
|
1387
|
+
/** Core services keep the connection and management interface available. */
|
|
1388
|
+
function isEssentialNpmPlugin(pluginId) {
|
|
1389
|
+
return ["i18n", "storage", "runtime", "server", "console", "config"].includes(pluginId);
|
|
1390
|
+
}
|
|
1391
|
+
/** npm changes dependencies and lockfiles; only append our plugin mapping afterwards. */
|
|
1065
1392
|
async function installNpmPlugin(options) {
|
|
1066
|
-
const { name,
|
|
1067
|
-
|
|
1068
|
-
throw new Error(`Invalid plugin id: ${pluginId}`);
|
|
1393
|
+
const { name, side } = options;
|
|
1394
|
+
const version = options.version || "latest";
|
|
1069
1395
|
if (!PACKAGE_NAME.test(name))
|
|
1070
1396
|
throw new Error(`Invalid npm plugin package: ${name}`);
|
|
1397
|
+
if (options.pluginId && !PLUGIN_ID.test(options.pluginId))
|
|
1398
|
+
throw new Error(`Invalid plugin id: ${options.pluginId}`);
|
|
1071
1399
|
if (!VERSION.test(version))
|
|
1072
1400
|
throw new Error(`Invalid plugin version: ${version}`);
|
|
1073
1401
|
const { packageRoot, projectRoot } = npmPluginRoots(options);
|
|
1074
1402
|
return withProjectLock(projectRoot, async () => {
|
|
1075
|
-
var _a;
|
|
1403
|
+
var _a, _b;
|
|
1076
1404
|
const host = readJson(path_1.default.join(packageRoot, "package.json"));
|
|
1077
1405
|
if (!host)
|
|
1078
1406
|
throw new Error(`Missing runtime package metadata: ${packageRoot}`);
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1407
|
+
const hostId = (_a = Object.entries(mappingsOf(host)).find(([, value]) => value === name && dependenciesOf(host)[name] !== undefined)) === null || _a === void 0 ? void 0 : _a[0];
|
|
1408
|
+
const targetRoot = hostId ? packageRoot : projectRoot;
|
|
1409
|
+
if (hostId && isEssentialNpmPlugin(hostId))
|
|
1410
|
+
throw new Error(`The essential plugin "${hostId}" cannot be replaced while running.`);
|
|
1411
|
+
if (options.pluginId &&
|
|
1412
|
+
mappingsOf(host)[options.pluginId] &&
|
|
1413
|
+
dependenciesOf(host)[String(mappingsOf(host)[options.pluginId])] !== undefined &&
|
|
1414
|
+
mappingsOf(host)[options.pluginId] !== name)
|
|
1415
|
+
throw new Error(`BUNDLED:${options.pluginId}`);
|
|
1416
|
+
const original = readProjectManifest(targetRoot);
|
|
1417
|
+
const previous = dependenciesOf(original)[name];
|
|
1418
|
+
let previousVersion = previous;
|
|
1419
|
+
if (previous) {
|
|
1420
|
+
try {
|
|
1421
|
+
previousVersion = readPackageEntry(targetRoot, name).metadata.version || previous;
|
|
1422
|
+
}
|
|
1423
|
+
catch (_c) {
|
|
1424
|
+
/* Keep the declared spec if the previous package is incomplete. */
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
const backups = new Map();
|
|
1428
|
+
for (const filename of ["package.json", "package-lock.json", "npm-shrinkwrap.json"]) {
|
|
1429
|
+
const target = path_1.default.join(targetRoot, filename);
|
|
1430
|
+
backups.set(target, fs_extra_1.default.existsSync(target) ? fs_extra_1.default.readFileSync(target) : null);
|
|
1431
|
+
}
|
|
1087
1432
|
try {
|
|
1088
|
-
|
|
1433
|
+
await runNpm([
|
|
1434
|
+
"--prefix",
|
|
1435
|
+
targetRoot,
|
|
1436
|
+
"install",
|
|
1437
|
+
`${name}@${version}`,
|
|
1438
|
+
"--save",
|
|
1439
|
+
"--save-exact",
|
|
1440
|
+
"--no-fund",
|
|
1441
|
+
"--no-audit",
|
|
1442
|
+
"--ignore-scripts"
|
|
1443
|
+
], targetRoot);
|
|
1444
|
+
const entry = readPackageEntry(targetRoot, name);
|
|
1445
|
+
const pluginId = options.pluginId || stringValue((_b = objectValue(entry.metadata.elementspanel)) === null || _b === void 0 ? void 0 : _b.id);
|
|
1446
|
+
if (!PLUGIN_ID.test(pluginId))
|
|
1447
|
+
throw new Error(`Invalid plugin id in ${name}.`);
|
|
1448
|
+
if (mappingsOf(host)[pluginId] &&
|
|
1449
|
+
dependenciesOf(host)[String(mappingsOf(host)[pluginId])] !== undefined &&
|
|
1450
|
+
mappingsOf(host)[pluginId] !== name)
|
|
1451
|
+
throw new Error(`Plugin id ${pluginId} belongs to a bundled dependency.`);
|
|
1452
|
+
if (hostId && pluginId !== hostId)
|
|
1453
|
+
throw new Error(`The package ${name} changed its plugin id from ${hostId} to ${pluginId}.`);
|
|
1454
|
+
const mapped = mappingsOf(readProjectManifest(targetRoot))[pluginId];
|
|
1455
|
+
if (mapped !== undefined && mapped !== name)
|
|
1456
|
+
throw new Error(`Plugin id ${pluginId} is already installed as ${String(mapped)}.`);
|
|
1457
|
+
const record = recordFromPackage(pluginId, name, hostId ? "bundled" : "project", entry.root, entry.metadata, side);
|
|
1458
|
+
if (!record)
|
|
1459
|
+
throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
|
|
1460
|
+
if (/^[0-9]/.test(version) && record.version !== version)
|
|
1461
|
+
throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
|
|
1462
|
+
if (!process.env.EPANEL_PACKAGE_ROOT &&
|
|
1463
|
+
(0, plugin_manifest_1.discoverPlugins)(path_1.default.join(packageRoot, "plugins"), {
|
|
1464
|
+
entryFields: [],
|
|
1465
|
+
includeDisabled: true
|
|
1466
|
+
}).some((plugin) => plugin.manifest.id === pluginId))
|
|
1467
|
+
throw new Error(`Source plugin ${pluginId} cannot be replaced with an npm dependency.`);
|
|
1468
|
+
const manifest = (0, plugin_manifest_1.readPluginManifest)(record.directory);
|
|
1469
|
+
if (!manifest || manifest.id !== pluginId || manifest.version !== record.version)
|
|
1470
|
+
throw new Error(`The plugin manifest in ${name} does not match its package.`);
|
|
1471
|
+
(0, plugin_contract_1.validatePluginCompatibility)(manifest.elements);
|
|
1472
|
+
for (const field of ["backend", "frontend", "panel", "daemon", "main", "entry", "ui"]) {
|
|
1473
|
+
if (typeof manifest[field] === "string" &&
|
|
1474
|
+
!(0, plugin_manifest_1.resolvePluginEntry)(record.directory, manifest, { entryFields: [field] }))
|
|
1475
|
+
throw new Error(`Invalid ${field} entry in ${name}.`);
|
|
1476
|
+
}
|
|
1477
|
+
const others = listNpmPlugins(side, { packageRoot, projectRoot, skipInvalid: true });
|
|
1478
|
+
if (others.some((plugin) => plugin.pluginId === pluginId && plugin.name !== name))
|
|
1479
|
+
throw new Error(`Plugin id ${pluginId} is already provided by another dependency.`);
|
|
1480
|
+
const collision = others.find((plugin) => plugin.pluginId !== pluginId && plugin.folder === record.folder);
|
|
1481
|
+
if (collision)
|
|
1482
|
+
throw new Error(`Plugin asset directory ${record.folder} is already used by ${collision.name}.`);
|
|
1483
|
+
const updated = readProjectManifest(targetRoot);
|
|
1484
|
+
updated.dependencies = { ...dependenciesOf(updated), [name]: record.version };
|
|
1485
|
+
updated.elementspanel = {
|
|
1486
|
+
...objectValue(updated.elementspanel),
|
|
1487
|
+
plugins: { ...mappingsOf(updated), [pluginId]: name }
|
|
1488
|
+
};
|
|
1489
|
+
writeProjectManifest(targetRoot, updated);
|
|
1490
|
+
return record;
|
|
1089
1491
|
}
|
|
1090
1492
|
catch (error) {
|
|
1091
|
-
|
|
1493
|
+
try {
|
|
1494
|
+
await runNpm(previous
|
|
1495
|
+
? [
|
|
1496
|
+
"--prefix",
|
|
1497
|
+
targetRoot,
|
|
1498
|
+
"install",
|
|
1499
|
+
`${name}@${String(previousVersion)}`,
|
|
1500
|
+
"--save",
|
|
1501
|
+
"--no-fund",
|
|
1502
|
+
"--no-audit",
|
|
1503
|
+
"--ignore-scripts"
|
|
1504
|
+
]
|
|
1505
|
+
: [
|
|
1506
|
+
"--prefix",
|
|
1507
|
+
targetRoot,
|
|
1508
|
+
"uninstall",
|
|
1509
|
+
name,
|
|
1510
|
+
"--no-fund",
|
|
1511
|
+
"--no-audit",
|
|
1512
|
+
"--ignore-scripts"
|
|
1513
|
+
], targetRoot);
|
|
1514
|
+
}
|
|
1515
|
+
catch (rollback) {
|
|
1516
|
+
throw new Error(`${String(error)}; npm rollback failed: ${String(rollback)}`);
|
|
1517
|
+
}
|
|
1518
|
+
finally {
|
|
1519
|
+
for (const [filename, content] of backups) {
|
|
1520
|
+
if (content === null)
|
|
1521
|
+
await fs_extra_1.default.remove(filename);
|
|
1522
|
+
else
|
|
1523
|
+
await fs_extra_1.default.writeFile(filename, content);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1092
1526
|
throw error;
|
|
1093
1527
|
}
|
|
1094
|
-
const record = recordFromPackage(pluginId, name, "project", entry.root, entry.metadata, side);
|
|
1095
|
-
if (!record) {
|
|
1096
|
-
await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot).catch(() => { });
|
|
1097
|
-
throw new Error(`The package ${name} does not provide a ${side} plugin named ${pluginId}.`);
|
|
1098
|
-
}
|
|
1099
|
-
if (record.version !== version) {
|
|
1100
|
-
await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], projectRoot).catch(() => { });
|
|
1101
|
-
throw new Error(`The installed package ${name} is ${record.version}, expected ${version}.`);
|
|
1102
|
-
}
|
|
1103
|
-
const manifest = readProjectManifest(projectRoot);
|
|
1104
|
-
const dependencies = { ...dependenciesOf(manifest), [name]: version };
|
|
1105
|
-
const elementspanel = { ...((_a = objectValue(manifest.elementspanel)) !== null && _a !== void 0 ? _a : {}) };
|
|
1106
|
-
const plugins = { ...mappingsOf(manifest), [pluginId]: name };
|
|
1107
|
-
manifest.dependencies = dependencies;
|
|
1108
|
-
manifest.elementspanel = { ...elementspanel, plugins };
|
|
1109
|
-
writeProjectManifest(projectRoot, manifest);
|
|
1110
|
-
return record;
|
|
1111
1528
|
});
|
|
1112
1529
|
}
|
|
1113
|
-
/**
|
|
1530
|
+
/** Remove the dependency from the manifest that owns it, preserving user configuration. */
|
|
1114
1531
|
async function uninstallNpmPlugin(pluginId, options = {}) {
|
|
1115
1532
|
if (!PLUGIN_ID.test(pluginId))
|
|
1116
1533
|
throw new Error(`Invalid plugin id: ${pluginId}`);
|
|
1534
|
+
if (isEssentialNpmPlugin(pluginId))
|
|
1535
|
+
throw new Error(`The essential plugin "${pluginId}" cannot be uninstalled.`);
|
|
1117
1536
|
const { packageRoot, projectRoot } = npmPluginRoots(options);
|
|
1118
1537
|
return withProjectLock(projectRoot, async () => {
|
|
1119
|
-
var _a;
|
|
1538
|
+
var _a, _b;
|
|
1120
1539
|
const host = readJson(path_1.default.join(packageRoot, "package.json"));
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1540
|
+
const bundledName = mappingsOf(host)[pluginId];
|
|
1541
|
+
const targetRoot = typeof bundledName === "string" && dependenciesOf(host)[bundledName] !== undefined
|
|
1542
|
+
? packageRoot
|
|
1543
|
+
: projectRoot;
|
|
1544
|
+
const manifest = readProjectManifest(targetRoot);
|
|
1545
|
+
const name = (_a = mappingsOf(manifest)[pluginId]) !== null && _a !== void 0 ? _a : (_b = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId)) === null || _b === void 0 ? void 0 : _b.name;
|
|
1125
1546
|
if (typeof name !== "string" || !PACKAGE_NAME.test(name))
|
|
1126
1547
|
return null;
|
|
1127
1548
|
const record = listNpmPlugins(undefined, { packageRoot, projectRoot, skipInvalid: true }).find((item) => item.pluginId === pluginId);
|
|
1128
|
-
await runNpm(["uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"],
|
|
1129
|
-
const
|
|
1549
|
+
await runNpm(["--prefix", targetRoot, "uninstall", name, "--no-fund", "--no-audit", "--ignore-scripts"], targetRoot);
|
|
1550
|
+
const updated = readProjectManifest(targetRoot);
|
|
1551
|
+
const plugins = { ...mappingsOf(updated) };
|
|
1130
1552
|
delete plugins[pluginId];
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
elementspanel.plugins = plugins;
|
|
1134
|
-
else
|
|
1135
|
-
delete elementspanel.plugins;
|
|
1136
|
-
manifest.elementspanel = elementspanel;
|
|
1137
|
-
writeProjectManifest(projectRoot, manifest);
|
|
1553
|
+
updated.elementspanel = { ...objectValue(updated.elementspanel), plugins };
|
|
1554
|
+
writeProjectManifest(targetRoot, updated);
|
|
1138
1555
|
return record !== null && record !== void 0 ? record : null;
|
|
1139
1556
|
});
|
|
1140
1557
|
}
|
|
@@ -1283,6 +1700,82 @@ function pluginDirectoryRevision(directory) {
|
|
|
1283
1700
|
}
|
|
1284
1701
|
|
|
1285
1702
|
|
|
1703
|
+
/***/ },
|
|
1704
|
+
|
|
1705
|
+
/***/ "../common/src/plugin_watch.ts"
|
|
1706
|
+
(__unused_webpack_module, exports, __webpack_require__) {
|
|
1707
|
+
|
|
1708
|
+
|
|
1709
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
1710
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
1711
|
+
};
|
|
1712
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1713
|
+
exports.watchNpmPlugins = watchNpmPlugins;
|
|
1714
|
+
const node_fs_1 = __importDefault(__webpack_require__("node:fs"));
|
|
1715
|
+
const node_path_1 = __importDefault(__webpack_require__("node:path"));
|
|
1716
|
+
const plugin_npm_1 = __webpack_require__("../common/src/plugin_npm.ts");
|
|
1717
|
+
/** Poll package boundaries, allowing npm to finish replacing files before reconciliation. */
|
|
1718
|
+
function watchNpmPlugins(ctx, side, reload, changed) {
|
|
1719
|
+
const stamp = () => {
|
|
1720
|
+
const { packageRoot, projectRoot } = (0, plugin_npm_1.npmPluginRoots)();
|
|
1721
|
+
if (!node_fs_1.default.existsSync(node_path_1.default.join(packageRoot, "package.json")))
|
|
1722
|
+
return "";
|
|
1723
|
+
const files = [];
|
|
1724
|
+
for (const root of [packageRoot, projectRoot])
|
|
1725
|
+
for (const name of [
|
|
1726
|
+
"package.json",
|
|
1727
|
+
"package-lock.json",
|
|
1728
|
+
"npm-shrinkwrap.json",
|
|
1729
|
+
"node_modules/.package-lock.json"
|
|
1730
|
+
])
|
|
1731
|
+
files.push(node_path_1.default.join(root, name));
|
|
1732
|
+
for (const plugin of (0, plugin_npm_1.listNpmPlugins)(side, { skipInvalid: true }))
|
|
1733
|
+
files.push(node_path_1.default.join(plugin.packageRoot, "package.json"), node_path_1.default.join(plugin.directory, "plugin.json"));
|
|
1734
|
+
return JSON.stringify(files.map((filename) => {
|
|
1735
|
+
try {
|
|
1736
|
+
const stat = node_fs_1.default.statSync(filename);
|
|
1737
|
+
return [filename, stat.size, stat.mtimeMs, stat.ctimeMs];
|
|
1738
|
+
}
|
|
1739
|
+
catch (_a) {
|
|
1740
|
+
return [filename, null];
|
|
1741
|
+
}
|
|
1742
|
+
}));
|
|
1743
|
+
};
|
|
1744
|
+
let applied = stamp();
|
|
1745
|
+
let candidate = applied;
|
|
1746
|
+
let busy = false;
|
|
1747
|
+
ctx.setInterval(() => {
|
|
1748
|
+
if (busy || (0, plugin_npm_1.npmPluginOperationPending)())
|
|
1749
|
+
return;
|
|
1750
|
+
try {
|
|
1751
|
+
const next = stamp();
|
|
1752
|
+
if (next === applied) {
|
|
1753
|
+
candidate = next;
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
if (next !== candidate) {
|
|
1757
|
+
candidate = next;
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
busy = true;
|
|
1761
|
+
void reload()
|
|
1762
|
+
.then(() => {
|
|
1763
|
+
applied = next;
|
|
1764
|
+
candidate = next;
|
|
1765
|
+
changed === null || changed === void 0 ? void 0 : changed();
|
|
1766
|
+
})
|
|
1767
|
+
.catch((error) => ctx.logger.warn("Failed to synchronize npm plugins", error))
|
|
1768
|
+
.finally(() => {
|
|
1769
|
+
busy = false;
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
catch (error) {
|
|
1773
|
+
ctx.logger.warn("Failed to inspect npm plugins", error);
|
|
1774
|
+
}
|
|
1775
|
+
}, 2000);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
|
|
1286
1779
|
/***/ },
|
|
1287
1780
|
|
|
1288
1781
|
/***/ "../common/src/process_tools.ts"
|
|
@@ -1595,6 +2088,9 @@ function npmPluginRoots(side) {
|
|
|
1595
2088
|
const { resolveNpmPlugins } = runtimeRequire(resolver);
|
|
1596
2089
|
return resolveNpmPlugins(packagePath(), side, { skipInvalid: true }).map((record) => ({
|
|
1597
2090
|
directory: record.directory,
|
|
2091
|
+
packageRoot: record.packageRoot,
|
|
2092
|
+
packageName: record.name,
|
|
2093
|
+
npmSource: record.source,
|
|
1598
2094
|
...(record.folder ? { folder: record.folder } : {}),
|
|
1599
2095
|
// Built-in packages are part of the runtime's default configuration. A
|
|
1600
2096
|
// project dependency is installed but remains dormant until the user
|
|
@@ -3797,6 +4293,13 @@ module.exports = require("node:fs");
|
|
|
3797
4293
|
|
|
3798
4294
|
/***/ },
|
|
3799
4295
|
|
|
4296
|
+
/***/ "node:module"
|
|
4297
|
+
(module) {
|
|
4298
|
+
|
|
4299
|
+
module.exports = require("node:module");
|
|
4300
|
+
|
|
4301
|
+
/***/ },
|
|
4302
|
+
|
|
3800
4303
|
/***/ "node:os"
|
|
3801
4304
|
(module) {
|
|
3802
4305
|
|
|
@@ -3811,6 +4314,13 @@ module.exports = require("node:path");
|
|
|
3811
4314
|
|
|
3812
4315
|
/***/ },
|
|
3813
4316
|
|
|
4317
|
+
/***/ "node:url"
|
|
4318
|
+
(module) {
|
|
4319
|
+
|
|
4320
|
+
module.exports = require("node:url");
|
|
4321
|
+
|
|
4322
|
+
/***/ },
|
|
4323
|
+
|
|
3814
4324
|
/***/ "os"
|
|
3815
4325
|
(module) {
|
|
3816
4326
|
|