@aiot-nexus/plugin-sdk 1.0.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.
Files changed (47) hide show
  1. package/README.md +74 -0
  2. package/dev-host/host.html +344 -0
  3. package/dev-host/main.js +694 -0
  4. package/dev-host/preload.js +22 -0
  5. package/dist/.generated/plugin-framework-version.d.ts +11 -0
  6. package/dist/.generated/plugin-framework-version.js +39 -0
  7. package/dist/.generated/plugin-manifest.schema.json +106 -0
  8. package/dist/browser/frame-client.d.ts +26 -0
  9. package/dist/browser/frame-client.mjs +239 -0
  10. package/dist/src/browser/frame-client.d.ts +26 -0
  11. package/dist/src/browser/frame-client.js +244 -0
  12. package/dist/src/cli/run.d.ts +7 -0
  13. package/dist/src/cli/run.js +270 -0
  14. package/dist/src/cli.d.ts +2 -0
  15. package/dist/src/cli.js +9 -0
  16. package/dist/src/dev.d.ts +36 -0
  17. package/dist/src/dev.js +224 -0
  18. package/dist/src/index.d.ts +7 -0
  19. package/dist/src/index.js +27 -0
  20. package/dist/src/scaffold.d.ts +23 -0
  21. package/dist/src/scaffold.js +306 -0
  22. package/dist/src/types.d.ts +142 -0
  23. package/dist/src/types.js +2 -0
  24. package/dist/src/validation.d.ts +9 -0
  25. package/dist/src/validation.js +160 -0
  26. package/package.json +47 -0
  27. package/templates/minimal/.gitignore.tmpl +4 -0
  28. package/templates/minimal/.vscode/launch.json +22 -0
  29. package/templates/minimal/README.md.tmpl +22 -0
  30. package/templates/minimal/scripts/build.js.tmpl +83 -0
  31. package/templates/minimal/src/main.ts.tmpl +23 -0
  32. package/templates/minimal/src/renderer/app.ts.tmpl +194 -0
  33. package/templates/minimal/src/renderer/index.html +34 -0
  34. package/templates/minimal/src/renderer/plugin.css +164 -0
  35. package/templates/minimal/tsconfig.main.json +15 -0
  36. package/templates/minimal/tsconfig.renderer.json +17 -0
  37. package/templates/sqlite/.gitignore.tmpl +4 -0
  38. package/templates/sqlite/.vscode/launch.json +22 -0
  39. package/templates/sqlite/README.md.tmpl +22 -0
  40. package/templates/sqlite/scripts/build.js.tmpl +143 -0
  41. package/templates/sqlite/src/better-sqlite3.d.ts +22 -0
  42. package/templates/sqlite/src/main.ts.tmpl +190 -0
  43. package/templates/sqlite/src/renderer/app.ts.tmpl +428 -0
  44. package/templates/sqlite/src/renderer/index.html +57 -0
  45. package/templates/sqlite/src/renderer/plugin.css +275 -0
  46. package/templates/sqlite/tsconfig.main.json +15 -0
  47. package/templates/sqlite/tsconfig.renderer.json +17 -0
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.preparePluginDev = exports.runPluginDev = exports.PluginDevError = void 0;
27
+ const child_process_1 = require("child_process");
28
+ const fs = __importStar(require("fs"));
29
+ const module_1 = require("module");
30
+ const path = __importStar(require("path"));
31
+ const validation_1 = require("./validation");
32
+ class PluginDevError extends Error {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = "PluginDevError";
36
+ this.exitCode = 1;
37
+ }
38
+ }
39
+ exports.PluginDevError = PluginDevError;
40
+ const requiredElectronVersion = "22.3.27";
41
+ function runPluginDev(options, io) {
42
+ const projectDirectory = path.resolve(options.projectDirectory);
43
+ assertPluginProject(projectDirectory);
44
+ if (options.build) {
45
+ io.stdout("Building plugin with development source maps: " + projectDirectory);
46
+ runProjectBuild(projectDirectory);
47
+ }
48
+ const target = preparePluginDev({ ...options, projectDirectory });
49
+ io.stdout("Starting plugin dev host: " + target.pluginId
50
+ + "\nPlugin package: " + target.packageDirectory
51
+ + "\nContribution: " + target.surface + ":" + target.contributionId);
52
+ if (options.inspectorMode) {
53
+ io.stdout("Main process debugger: " + options.inspectorMode
54
+ + " on 127.0.0.1:" + String(options.inspectorPort || 9229));
55
+ }
56
+ const electronArgs = [];
57
+ if (options.inspectorMode) {
58
+ electronArgs.push("--" + options.inspectorMode + "=" + String(options.inspectorPort || 9229));
59
+ }
60
+ electronArgs.push(target.devHostMainPath, "--plugin-package=" + target.packageDirectory, "--project-directory=" + target.projectDirectory, "--surface=" + target.surface, "--contribution-id=" + target.contributionId, "--language=" + target.language, "--theme=" + target.themeMode);
61
+ if (!options.openDevTools) {
62
+ electronArgs.push("--no-devtools");
63
+ }
64
+ if (options.smoke) {
65
+ electronArgs.push("--smoke");
66
+ }
67
+ const result = (0, child_process_1.spawnSync)(target.electronExecutable, electronArgs, {
68
+ cwd: target.projectDirectory,
69
+ env: process.env,
70
+ stdio: "inherit"
71
+ });
72
+ if (result.error) {
73
+ throw new PluginDevError("Plugin dev host could not start: " + result.error.message);
74
+ }
75
+ if (result.status === null) {
76
+ throw new PluginDevError("Plugin dev host stopped without an exit code" + (result.signal ? " (" + result.signal + ")" : "") + ".");
77
+ }
78
+ return result.status;
79
+ }
80
+ exports.runPluginDev = runPluginDev;
81
+ function preparePluginDev(options) {
82
+ const projectDirectory = path.resolve(options.projectDirectory);
83
+ assertPluginProject(projectDirectory);
84
+ const sourceManifest = readManifest(path.join(projectDirectory, "src", "manifest.json"), "Plugin source manifest");
85
+ const packageDirectory = path.join(projectDirectory, "dist", sourceManifest.pluginId);
86
+ const validation = (0, validation_1.validatePluginPackage)(packageDirectory);
87
+ if (!validation.valid) {
88
+ throw new PluginDevError("Plugin package is not ready for debugging: " + packageDirectory
89
+ + "\n" + validation.errors.map((error) => " - " + error).join("\n"));
90
+ }
91
+ const manifest = readManifest(path.join(packageDirectory, "manifest.json"), "Built plugin manifest");
92
+ if (manifest.pluginId !== sourceManifest.pluginId) {
93
+ throw new PluginDevError("Built plugin id does not match src/manifest.json.");
94
+ }
95
+ const contribution = resolveContribution(manifest, options.surface, options.contributionId);
96
+ const electronExecutable = resolveElectronExecutable(projectDirectory);
97
+ const devHostMainPath = path.join(resolvePackageRoot(), "dev-host", "main.js");
98
+ if (!fs.existsSync(devHostMainPath) || !fs.statSync(devHostMainPath).isFile()) {
99
+ throw new PluginDevError("Plugin SDK dev host is missing: " + devHostMainPath);
100
+ }
101
+ return {
102
+ projectDirectory,
103
+ packageDirectory,
104
+ pluginId: manifest.pluginId,
105
+ manifest,
106
+ electronExecutable,
107
+ devHostMainPath,
108
+ surface: contribution.surface,
109
+ contributionId: contribution.contributionId,
110
+ language: normalizeLanguage(options.language, manifest),
111
+ themeMode: options.themeMode || "system"
112
+ };
113
+ }
114
+ exports.preparePluginDev = preparePluginDev;
115
+ function assertPluginProject(projectDirectory) {
116
+ if (!fs.existsSync(projectDirectory) || !fs.statSync(projectDirectory).isDirectory()) {
117
+ throw new PluginDevError("Plugin project directory does not exist: " + projectDirectory);
118
+ }
119
+ const packageJsonPath = path.join(projectDirectory, "package.json");
120
+ if (!fs.existsSync(packageJsonPath) || !fs.statSync(packageJsonPath).isFile()) {
121
+ throw new PluginDevError("Plugin project package.json is missing: " + packageJsonPath);
122
+ }
123
+ }
124
+ function runProjectBuild(projectDirectory) {
125
+ const command = process.platform === "win32" ? "npm.cmd" : "npm";
126
+ const result = (0, child_process_1.spawnSync)(command, ["run", "build"], {
127
+ cwd: projectDirectory,
128
+ env: { ...process.env, NEXUS_PLUGIN_DEV: "1" },
129
+ stdio: "inherit"
130
+ });
131
+ if (result.error) {
132
+ throw new PluginDevError("Plugin development build could not start: " + result.error.message);
133
+ }
134
+ if (result.status !== 0) {
135
+ throw new PluginDevError("Plugin development build failed with exit code " + String(result.status === null ? 1 : result.status) + ".");
136
+ }
137
+ }
138
+ function readManifest(filePath, label) {
139
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
140
+ throw new PluginDevError(label + " is missing: " + filePath);
141
+ }
142
+ try {
143
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
144
+ }
145
+ catch (error) {
146
+ throw new PluginDevError(label + " is not valid JSON: " + errorMessage(error));
147
+ }
148
+ }
149
+ function resolveContribution(manifest, requestedSurface, requestedContributionId) {
150
+ const contributions = pluginFrameContributions(manifest);
151
+ const matches = contributions.filter((contribution) => {
152
+ return (!requestedSurface || contribution.surface === requestedSurface)
153
+ && (!requestedContributionId || contribution.contributionId === requestedContributionId);
154
+ });
155
+ if (matches.length === 0) {
156
+ const selection = [requestedSurface, requestedContributionId].filter(Boolean).join(":") || "default";
157
+ throw new PluginDevError("No PluginFrame contribution matches the debug selection: " + selection + ".");
158
+ }
159
+ return matches[0];
160
+ }
161
+ function pluginFrameContributions(manifest) {
162
+ const contributions = [];
163
+ for (const entry of manifest.businessEntries || []) {
164
+ if (entry.component === "PluginFrame") {
165
+ contributions.push({ surface: "business", contributionId: entry.businessId });
166
+ }
167
+ }
168
+ for (const entry of manifest.tools || []) {
169
+ if (entry.component === "PluginFrame") {
170
+ contributions.push({ surface: "tool", contributionId: entry.toolId });
171
+ }
172
+ }
173
+ for (const entry of manifest.settingsSections || []) {
174
+ if (entry.component === "PluginFrame") {
175
+ contributions.push({ surface: "settings", contributionId: entry.sectionId });
176
+ }
177
+ }
178
+ return contributions;
179
+ }
180
+ function resolveElectronExecutable(projectDirectory) {
181
+ const projectRequire = (0, module_1.createRequire)(path.join(projectDirectory, "package.json"));
182
+ let electronPackagePath;
183
+ try {
184
+ electronPackagePath = projectRequire.resolve("electron/package.json");
185
+ }
186
+ catch {
187
+ throw new PluginDevError("electron@" + requiredElectronVersion + " is not installed in the plugin project. Run npm install.");
188
+ }
189
+ const electronPackage = readJsonRecord(electronPackagePath, "Electron package metadata");
190
+ if (electronPackage.version !== requiredElectronVersion) {
191
+ throw new PluginDevError("Plugin debugging requires electron@" + requiredElectronVersion
192
+ + ", found " + String(electronPackage.version || "unknown") + ".");
193
+ }
194
+ const electronValue = projectRequire("electron");
195
+ if (typeof electronValue !== "string" || !fs.existsSync(electronValue)) {
196
+ throw new PluginDevError("Electron executable is unavailable. Reinstall electron@" + requiredElectronVersion + ".");
197
+ }
198
+ return electronValue;
199
+ }
200
+ function readJsonRecord(filePath, label) {
201
+ try {
202
+ const value = JSON.parse(fs.readFileSync(filePath, "utf8"));
203
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
204
+ throw new Error("expected an object");
205
+ }
206
+ return value;
207
+ }
208
+ catch (error) {
209
+ throw new PluginDevError(label + " is invalid: " + errorMessage(error));
210
+ }
211
+ }
212
+ function normalizeLanguage(language, manifest) {
213
+ const value = language?.trim() || manifest.supportedCultures[0] || "en-US";
214
+ if (value.length > 40 || !/^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value)) {
215
+ throw new PluginDevError("Plugin debug language is invalid: " + value);
216
+ }
217
+ return value;
218
+ }
219
+ function resolvePackageRoot() {
220
+ return path.resolve(__dirname, "..", "..");
221
+ }
222
+ function errorMessage(error) {
223
+ return error instanceof Error ? error.message : String(error);
224
+ }
@@ -0,0 +1,7 @@
1
+ import pluginManifestSchema from "../.generated/plugin-manifest.schema.json";
2
+ export declare const PLUGIN_SDK_VERSION = "1.0.0";
3
+ export { pluginManifestSchema };
4
+ export * from "../.generated/plugin-framework-version";
5
+ export * from "./types";
6
+ export * from "./browser/frame-client";
7
+ export * from "./validation";
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.pluginManifestSchema = exports.PLUGIN_SDK_VERSION = void 0;
21
+ const plugin_manifest_schema_json_1 = __importDefault(require("../.generated/plugin-manifest.schema.json"));
22
+ exports.pluginManifestSchema = plugin_manifest_schema_json_1.default;
23
+ exports.PLUGIN_SDK_VERSION = "1.0.0";
24
+ __exportStar(require("../.generated/plugin-framework-version"), exports);
25
+ __exportStar(require("./types"), exports);
26
+ __exportStar(require("./browser/frame-client"), exports);
27
+ __exportStar(require("./validation"), exports);
@@ -0,0 +1,23 @@
1
+ export type PluginTemplateName = "minimal" | "sqlite";
2
+ export interface CreatePluginScaffoldOptions {
3
+ pluginId: string;
4
+ template: PluginTemplateName;
5
+ workingDirectory: string;
6
+ outputDirectory: string;
7
+ displayName: string;
8
+ sdkSource: string;
9
+ install: boolean;
10
+ }
11
+ export interface CreatePluginScaffoldResult {
12
+ pluginId: string;
13
+ template: PluginTemplateName;
14
+ pluginDirectory: string;
15
+ sdkSource: string;
16
+ installed: boolean;
17
+ }
18
+ export declare class PluginScaffoldError extends Error {
19
+ readonly exitCode: 1 | 2;
20
+ constructor(message: string, exitCode: 1 | 2);
21
+ }
22
+ export declare function createPluginScaffold(options: CreatePluginScaffoldOptions): CreatePluginScaffoldResult;
23
+ export declare function defaultPluginDisplayName(pluginId: string): string;
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.defaultPluginDisplayName = exports.createPluginScaffold = exports.PluginScaffoldError = void 0;
27
+ const child_process_1 = require("child_process");
28
+ const fs = __importStar(require("fs"));
29
+ const path = __importStar(require("path"));
30
+ const url_1 = require("url");
31
+ const index_1 = require("./index");
32
+ class PluginScaffoldError extends Error {
33
+ constructor(message, exitCode) {
34
+ super(message);
35
+ this.name = "PluginScaffoldError";
36
+ this.exitCode = exitCode;
37
+ }
38
+ }
39
+ exports.PluginScaffoldError = PluginScaffoldError;
40
+ const pluginIdPattern = /^[a-z0-9][a-z0-9._-]{1,63}$/;
41
+ const strictVersionPattern = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
42
+ const sdkPackageName = "@aiot-nexus/plugin-sdk";
43
+ const vendoredSdkDirectory = "vendor";
44
+ const vendoredSdkArchiveName = "aiot-nexus-plugin-sdk.tgz";
45
+ const vendoredSdkDependency = "file:" + vendoredSdkDirectory + "/" + vendoredSdkArchiveName;
46
+ function createPluginScaffold(options) {
47
+ validateOptions(options);
48
+ const outputDirectory = path.resolve(options.outputDirectory);
49
+ const pluginDirectory = path.join(outputDirectory, options.pluginId);
50
+ assertWritableTarget(pluginDirectory);
51
+ const sdkSource = normalizeSdkSource(options.sdkSource, options.workingDirectory);
52
+ const templateDirectory = path.join(resolvePackageRoot(), "templates", options.template);
53
+ if (!fs.existsSync(templateDirectory) || !fs.statSync(templateDirectory).isDirectory()) {
54
+ throw new PluginScaffoldError("Plugin template is missing: " + options.template, 1);
55
+ }
56
+ const targetExisted = fs.existsSync(pluginDirectory);
57
+ try {
58
+ fs.mkdirSync(pluginDirectory, { recursive: true });
59
+ copyTemplateDirectory(templateDirectory, pluginDirectory, {
60
+ "__PLUGIN_ID__": options.pluginId,
61
+ "__DISPLAY_NAME_MARKDOWN__": escapeMarkdown(options.displayName)
62
+ });
63
+ if (sdkSource.localArchivePath) {
64
+ vendorSdkArchive(pluginDirectory, sdkSource.localArchivePath);
65
+ }
66
+ writeJson(path.join(pluginDirectory, "package.json"), createPackageJson(options, sdkSource.dependencySpec));
67
+ writeJson(path.join(pluginDirectory, "src", "manifest.json"), createManifest(options));
68
+ }
69
+ catch (error) {
70
+ cleanupFailedTarget(pluginDirectory, targetExisted);
71
+ if (error instanceof PluginScaffoldError) {
72
+ throw error;
73
+ }
74
+ throw new PluginScaffoldError("Plugin scaffold could not be created: " + errorMessage(error), 1);
75
+ }
76
+ if (options.install) {
77
+ runNpmInstall(pluginDirectory);
78
+ }
79
+ return {
80
+ pluginId: options.pluginId,
81
+ template: options.template,
82
+ pluginDirectory,
83
+ sdkSource: sdkSource.dependencySpec,
84
+ installed: options.install
85
+ };
86
+ }
87
+ exports.createPluginScaffold = createPluginScaffold;
88
+ function defaultPluginDisplayName(pluginId) {
89
+ return pluginId
90
+ .split(/[._-]+/)
91
+ .filter(Boolean)
92
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
93
+ .join(" ");
94
+ }
95
+ exports.defaultPluginDisplayName = defaultPluginDisplayName;
96
+ function validateOptions(options) {
97
+ if (!pluginIdPattern.test(options.pluginId)) {
98
+ throw new PluginScaffoldError("plugin-id must match " + pluginIdPattern.source + ".", 2);
99
+ }
100
+ if (options.template !== "minimal" && options.template !== "sqlite") {
101
+ throw new PluginScaffoldError("template must be minimal or sqlite.", 2);
102
+ }
103
+ if (!options.displayName || options.displayName.length > 120 || /[\u0000-\u001f\u007f]/.test(options.displayName)) {
104
+ throw new PluginScaffoldError("display-name must contain 1 to 120 printable characters.", 2);
105
+ }
106
+ if (!options.sdkSource.trim()) {
107
+ throw new PluginScaffoldError("sdk-source cannot be empty.", 2);
108
+ }
109
+ }
110
+ function assertWritableTarget(pluginDirectory) {
111
+ if (!fs.existsSync(pluginDirectory)) {
112
+ return;
113
+ }
114
+ const stat = fs.lstatSync(pluginDirectory);
115
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
116
+ throw new PluginScaffoldError("Target path must be an empty directory: " + pluginDirectory, 2);
117
+ }
118
+ if (fs.readdirSync(pluginDirectory).length > 0) {
119
+ throw new PluginScaffoldError("Target directory is not empty: " + pluginDirectory, 2);
120
+ }
121
+ }
122
+ function normalizeSdkSource(value, cwd) {
123
+ const source = value.trim();
124
+ const packagePrefix = sdkPackageName + "@";
125
+ const version = source.startsWith(packagePrefix) ? source.slice(packagePrefix.length) : source;
126
+ if (strictVersionPattern.test(version)) {
127
+ return { dependencySpec: version };
128
+ }
129
+ let localPath;
130
+ if (source.startsWith("file:")) {
131
+ try {
132
+ localPath = (0, url_1.fileURLToPath)(source);
133
+ }
134
+ catch (error) {
135
+ throw new PluginScaffoldError("sdk-source file URL is invalid: " + errorMessage(error), 2);
136
+ }
137
+ }
138
+ else if (isPathLike(source)) {
139
+ localPath = path.resolve(cwd, source);
140
+ }
141
+ if (!localPath) {
142
+ throw new PluginScaffoldError("sdk-source must be an X.Y.Z version or a local .tgz path.", 2);
143
+ }
144
+ if (path.extname(localPath).toLowerCase() !== ".tgz" || !fs.existsSync(localPath)) {
145
+ throw new PluginScaffoldError("Local sdk-source .tgz does not exist: " + localPath, 2);
146
+ }
147
+ const stat = fs.lstatSync(localPath);
148
+ if (!stat.isFile() || stat.isSymbolicLink()) {
149
+ throw new PluginScaffoldError("Local sdk-source must be a regular .tgz file: " + localPath, 2);
150
+ }
151
+ return {
152
+ dependencySpec: vendoredSdkDependency,
153
+ localArchivePath: path.resolve(localPath)
154
+ };
155
+ }
156
+ function isPathLike(value) {
157
+ return path.isAbsolute(value)
158
+ || value.startsWith(".")
159
+ || value.includes("/")
160
+ || value.includes("\\")
161
+ || value.toLowerCase().endsWith(".tgz");
162
+ }
163
+ function resolvePackageRoot() {
164
+ return path.resolve(__dirname, "..", "..");
165
+ }
166
+ function copyTemplateDirectory(sourceDirectory, targetDirectory, substitutions) {
167
+ for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) {
168
+ if (entry.isSymbolicLink()) {
169
+ throw new PluginScaffoldError("Template entries cannot be symbolic links: " + entry.name, 1);
170
+ }
171
+ const isTemplateFile = entry.isFile() && entry.name.endsWith(".tmpl");
172
+ const targetName = isTemplateFile ? entry.name.slice(0, -".tmpl".length) : entry.name;
173
+ const sourcePath = path.join(sourceDirectory, entry.name);
174
+ const targetPath = path.join(targetDirectory, targetName);
175
+ if (entry.isDirectory()) {
176
+ fs.mkdirSync(targetPath, { recursive: true });
177
+ copyTemplateDirectory(sourcePath, targetPath, substitutions);
178
+ continue;
179
+ }
180
+ if (!entry.isFile()) {
181
+ throw new PluginScaffoldError("Unsupported template entry: " + sourcePath, 1);
182
+ }
183
+ if (!isTemplateFile) {
184
+ fs.copyFileSync(sourcePath, targetPath);
185
+ continue;
186
+ }
187
+ let content = fs.readFileSync(sourcePath, "utf8");
188
+ for (const token of Object.keys(substitutions)) {
189
+ content = content.split(token).join(substitutions[token]);
190
+ }
191
+ fs.writeFileSync(targetPath, content, "utf8");
192
+ }
193
+ }
194
+ function vendorSdkArchive(pluginDirectory, sourcePath) {
195
+ const vendorDirectory = path.join(pluginDirectory, vendoredSdkDirectory);
196
+ fs.mkdirSync(vendorDirectory, { recursive: true });
197
+ fs.copyFileSync(sourcePath, path.join(vendorDirectory, vendoredSdkArchiveName));
198
+ }
199
+ function createPackageJson(options, sdkSource) {
200
+ const scripts = {
201
+ build: "node scripts/build.js && npm run validate",
202
+ dev: "nexus-plugin dev .",
203
+ "dev:debug": "nexus-plugin dev . --inspect-brk=9229",
204
+ typecheck: "tsc -p tsconfig.main.json --noEmit && tsc -p tsconfig.renderer.json --noEmit",
205
+ validate: "nexus-plugin validate dist/" + options.pluginId
206
+ };
207
+ const devDependencies = {
208
+ [sdkPackageName]: sdkSource,
209
+ "@types/node": "16.18.126",
210
+ electron: "22.3.27",
211
+ typescript: "4.9.4"
212
+ };
213
+ const packageJson = {
214
+ name: options.pluginId,
215
+ version: "0.1.0",
216
+ private: true,
217
+ description: options.displayName + " independent plugin for AIoT Nexus.",
218
+ scripts
219
+ };
220
+ if (options.template === "sqlite") {
221
+ scripts.build = "npm run rebuild:native && node scripts/build.js && npm run validate";
222
+ scripts["rebuild:native"] = "electron-rebuild -v 22.3.27 -m . -w better-sqlite3";
223
+ packageJson.dependencies = { "better-sqlite3": "8.7.0" };
224
+ devDependencies["@electron/rebuild"] = "3.7.2";
225
+ }
226
+ packageJson.devDependencies = devDependencies;
227
+ packageJson.engines = { node: ">=16.20.0 <17" };
228
+ return packageJson;
229
+ }
230
+ function createManifest(options) {
231
+ const isSqlite = options.template === "sqlite";
232
+ const displayNameKey = "plugin.displayName";
233
+ const descriptionKey = "plugin.description";
234
+ const englishDescription = isSqlite
235
+ ? "Plugin-owned SQLite CRUD example generated by nexus-plugin."
236
+ : "PluginFrame echo example generated by nexus-plugin.";
237
+ const chineseDescription = isSqlite
238
+ ? "由 nexus-plugin 生成的插件独立 SQLite CRUD 示例。"
239
+ : "由 nexus-plugin 生成的 PluginFrame 回显示例。";
240
+ return {
241
+ schemaVersion: index_1.PLUGIN_MANIFEST_SCHEMA_VERSION,
242
+ pluginId: options.pluginId,
243
+ displayNameKey,
244
+ descriptionKey,
245
+ pluginVersion: "0.1.0",
246
+ hostApiVersion: index_1.PLUGIN_FRAMEWORK_VERSION,
247
+ minHostVersion: "0.0.0",
248
+ supportedCultures: ["zh-CN", "en-US"],
249
+ resources: {
250
+ "zh-CN": {
251
+ [displayNameKey]: options.displayName,
252
+ [descriptionKey]: chineseDescription
253
+ },
254
+ "en-US": {
255
+ [displayNameKey]: options.displayName,
256
+ [descriptionKey]: englishDescription
257
+ }
258
+ },
259
+ permissions: ["businessLog"],
260
+ mainEntry: "main.cjs",
261
+ rendererEntry: "renderer/index.html",
262
+ businessEntries: [
263
+ {
264
+ businessId: options.pluginId,
265
+ titleKey: displayNameKey,
266
+ descriptionKey,
267
+ iconKey: isSqlite ? "storage" : "extension",
268
+ section: "home.main",
269
+ order: 50,
270
+ component: "PluginFrame",
271
+ pageRoute: options.pluginId,
272
+ isFullBleed: true,
273
+ showBusinessLogToggle: true
274
+ }
275
+ ]
276
+ };
277
+ }
278
+ function writeJson(filePath, value) {
279
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
280
+ fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
281
+ }
282
+ function runNpmInstall(pluginDirectory) {
283
+ runCommand(pluginDirectory, ["install"], "npm install");
284
+ }
285
+ function runCommand(pluginDirectory, args, label) {
286
+ const command = process.platform === "win32" ? "npm.cmd" : "npm";
287
+ const result = (0, child_process_1.spawnSync)(command, args, { cwd: pluginDirectory, stdio: "inherit" });
288
+ if (result.error) {
289
+ throw new PluginScaffoldError(label + " could not start: " + result.error.message, 1);
290
+ }
291
+ if (result.status !== 0) {
292
+ throw new PluginScaffoldError(label + " failed with exit code " + String(result.status) + ".", 1);
293
+ }
294
+ }
295
+ function cleanupFailedTarget(pluginDirectory, targetExisted) {
296
+ fs.rmSync(pluginDirectory, { recursive: true, force: true });
297
+ if (targetExisted) {
298
+ fs.mkdirSync(pluginDirectory, { recursive: true });
299
+ }
300
+ }
301
+ function escapeMarkdown(value) {
302
+ return value.replace(/([\\`*_[\]<>])/g, "\\$1");
303
+ }
304
+ function errorMessage(error) {
305
+ return error instanceof Error ? error.message : String(error);
306
+ }