@soeditor/plugin-tools 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.
- package/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin-check.d.ts +16 -0
- package/dist/plugin-check.d.ts.map +1 -0
- package/dist/scaffold-BD62-b7s.js +360 -0
- package/dist/scaffold-BD62-b7s.js.map +1 -0
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SoEditor contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @soeditor/plugin-tools
|
|
2
|
+
|
|
3
|
+
Public 1.0 offline plugin scaffolding and package audit tools.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
soeditor-plugin create ./my-plugin --name @example/my-plugin --id example.my-plugin
|
|
7
|
+
soeditor-plugin check ./my-plugin --packed
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The checker reads source and metadata without importing plugin code. Packed
|
|
11
|
+
inspection uses `npm pack --dry-run --ignore-scripts`.
|
|
12
|
+
|
|
13
|
+
See [`docs/plugin-tooling.md`](../../docs/plugin-tooling.md).
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { argv, stdout, stderr } from "node:process";
|
|
3
|
+
import { s as scaffoldPluginPackage, c as checkPluginPackage } from "./scaffold-BD62-b7s.js";
|
|
4
|
+
try {
|
|
5
|
+
const [command, target, ...flags] = argv.slice(2);
|
|
6
|
+
if (command === "create" && target !== void 0) {
|
|
7
|
+
const packageName = flag(flags, "--name");
|
|
8
|
+
const pluginId = flag(flags, "--id");
|
|
9
|
+
const directory = await scaffoldPluginPackage({
|
|
10
|
+
directory: target,
|
|
11
|
+
packageName,
|
|
12
|
+
pluginId
|
|
13
|
+
});
|
|
14
|
+
stdout.write(`Created SoEditor plugin package at ${directory}.
|
|
15
|
+
`);
|
|
16
|
+
} else if (command === "check" && target !== void 0) {
|
|
17
|
+
const report = await checkPluginPackage(target, {
|
|
18
|
+
packed: flags.includes("--packed")
|
|
19
|
+
});
|
|
20
|
+
for (const issue of report.issues) {
|
|
21
|
+
stderr.write(`[${issue.code}] ${issue.message}
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
stdout.write(
|
|
25
|
+
`${report.valid ? "Valid" : "Invalid"} SoEditor plugin package${report.packageName === void 0 ? "" : ` ${report.packageName}`}.
|
|
26
|
+
`
|
|
27
|
+
);
|
|
28
|
+
if (!report.valid) process.exitCode = 1;
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"Usage: soeditor-plugin create <directory> --name <package> --id <plugin-id> | check <directory> [--packed]"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
} catch (error) {
|
|
35
|
+
stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
36
|
+
`);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
}
|
|
39
|
+
function flag(values, name) {
|
|
40
|
+
const index = values.indexOf(name);
|
|
41
|
+
const value = index < 0 ? void 0 : values[index + 1];
|
|
42
|
+
if (value === void 0) throw new Error(`Missing ${name}.`);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { argv, stderr, stdout } from 'node:process';\n\nimport { checkPluginPackage } from './plugin-check.js';\nimport { scaffoldPluginPackage } from './scaffold.js';\n\ntry {\n const [command, target, ...flags] = argv.slice(2);\n if (command === 'create' && target !== undefined) {\n const packageName = flag(flags, '--name');\n const pluginId = flag(flags, '--id');\n const directory = await scaffoldPluginPackage({\n directory: target,\n packageName,\n pluginId,\n });\n stdout.write(`Created SoEditor plugin package at ${directory}.\\n`);\n } else if (command === 'check' && target !== undefined) {\n const report = await checkPluginPackage(target, {\n packed: flags.includes('--packed'),\n });\n for (const issue of report.issues) {\n stderr.write(`[${issue.code}] ${issue.message}\\n`);\n }\n stdout.write(\n `${report.valid ? 'Valid' : 'Invalid'} SoEditor plugin package${report.packageName === undefined ? '' : ` ${report.packageName}`}.\\n`,\n );\n if (!report.valid) process.exitCode = 1;\n } else {\n throw new Error(\n 'Usage: soeditor-plugin create <directory> --name <package> --id <plugin-id> | check <directory> [--packed]',\n );\n }\n} catch (error: unknown) {\n stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n}\n\nfunction flag(values: string[], name: string): string {\n const index = values.indexOf(name);\n const value = index < 0 ? undefined : values[index + 1];\n if (value === undefined) throw new Error(`Missing ${name}.`);\n return value;\n}\n"],"names":[],"mappings":";;;AAMA,IAAI;AACA,QAAM,CAAC,SAAS,QAAQ,GAAG,KAAK,IAAI,KAAK,MAAM,CAAC;AAChD,MAAI,YAAY,YAAY,WAAW,QAAW;AAC9C,UAAM,cAAc,KAAK,OAAO,QAAQ;AACxC,UAAM,WAAW,KAAK,OAAO,MAAM;AACnC,UAAM,YAAY,MAAM,sBAAsB;AAAA,MAC1C,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IAAA,CACH;AACD,WAAO,MAAM,sCAAsC,SAAS;AAAA,CAAK;AAAA,EACrE,WAAW,YAAY,WAAW,WAAW,QAAW;AACpD,UAAM,SAAS,MAAM,mBAAmB,QAAQ;AAAA,MAC5C,QAAQ,MAAM,SAAS,UAAU;AAAA,IAAA,CACpC;AACD,eAAW,SAAS,OAAO,QAAQ;AAC/B,aAAO,MAAM,IAAI,MAAM,IAAI,KAAK,MAAM,OAAO;AAAA,CAAI;AAAA,IACrD;AACA,WAAO;AAAA,MACH,GAAG,OAAO,QAAQ,UAAU,SAAS,2BAA2B,OAAO,gBAAgB,SAAY,KAAK,IAAI,OAAO,WAAW,EAAE;AAAA;AAAA,IAAA;AAEpI,QAAI,CAAC,OAAO,MAAO,SAAQ,WAAW;AAAA,EAC1C,OAAO;AACH,UAAM,IAAI;AAAA,MACN;AAAA,IAAA;AAAA,EAER;AACJ,SAAS,OAAgB;AACrB,SAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AAC1E,UAAQ,WAAW;AACvB;AAEA,SAAS,KAAK,QAAkB,MAAsB;AAClD,QAAM,QAAQ,OAAO,QAAQ,IAAI;AACjC,QAAM,QAAQ,QAAQ,IAAI,SAAY,OAAO,QAAQ,CAAC;AACtD,MAAI,UAAU,OAAW,OAAM,IAAI,MAAM,WAAW,IAAI,GAAG;AAC3D,SAAO;AACX;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { checkPluginPackage } from './plugin-check.js';
|
|
2
|
+
export { scaffoldPluginPackage } from './scaffold.js';
|
|
3
|
+
export type { PluginCheckIssue, PluginCheckOptions, PluginCheckReport, } from './plugin-check.js';
|
|
4
|
+
export type { ScaffoldPluginOptions } from './scaffold.js';
|
|
5
|
+
export declare const pluginTemplateVersion = 2;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EACR,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3D,eAAO,MAAM,qBAAqB,IAAI,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["export { checkPluginPackage } from './plugin-check.js';\nexport { scaffoldPluginPackage } from './scaffold.js';\nexport type {\n PluginCheckIssue,\n PluginCheckOptions,\n PluginCheckReport,\n} from './plugin-check.js';\nexport type { ScaffoldPluginOptions } from './scaffold.js';\n\nexport const pluginTemplateVersion = 2;\n"],"names":[],"mappings":";AASO,MAAM,wBAAwB;"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface PluginCheckOptions {
|
|
2
|
+
readonly packed?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface PluginCheckIssue {
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginCheckReport {
|
|
9
|
+
readonly issues: readonly PluginCheckIssue[];
|
|
10
|
+
readonly packageName: string | undefined;
|
|
11
|
+
readonly pluginIds: readonly string[];
|
|
12
|
+
readonly valid: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Audits a plugin package without importing or executing its source. */
|
|
15
|
+
export declare function checkPluginPackage(directory: string, options?: PluginCheckOptions): Promise<PluginCheckReport>;
|
|
16
|
+
//# sourceMappingURL=plugin-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-check.d.ts","sourceRoot":"","sources":["../src/plugin-check.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CAC3B;AAED,yEAAyE;AACzE,wBAAsB,kBAAkB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAuF5B"}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFile, readdir, mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve, basename } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
const execute = promisify(execFile);
|
|
6
|
+
async function checkPluginPackage(directory, options = {}) {
|
|
7
|
+
const root = resolve(directory);
|
|
8
|
+
const issues = [];
|
|
9
|
+
const manifest2 = await jsonRecord(resolve(root, "package.json"), issues);
|
|
10
|
+
checkManifest(manifest2, issues);
|
|
11
|
+
const sourceFiles = await sourcePaths(resolve(root, "src"));
|
|
12
|
+
const sources = await Promise.all(
|
|
13
|
+
sourceFiles.map(async (path) => ({
|
|
14
|
+
path,
|
|
15
|
+
source: await readFile(path, "utf8")
|
|
16
|
+
}))
|
|
17
|
+
);
|
|
18
|
+
const pluginIds = sources.flatMap(
|
|
19
|
+
({ source }) => [
|
|
20
|
+
...source.matchAll(
|
|
21
|
+
/static\s+readonly\s+id\s*=\s*['"]([^'"]+)['"]/gu
|
|
22
|
+
)
|
|
23
|
+
].map((match) => match[1])
|
|
24
|
+
);
|
|
25
|
+
if (pluginIds.length === 0) {
|
|
26
|
+
issue(
|
|
27
|
+
issues,
|
|
28
|
+
"PLUGIN_ID_MISSING",
|
|
29
|
+
"No static readonly plugin ID was found."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
const duplicates = pluginIds.filter(
|
|
33
|
+
(id, index) => pluginIds.indexOf(id) !== index
|
|
34
|
+
);
|
|
35
|
+
for (const id of pluginIds) {
|
|
36
|
+
if (!/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u.test(id)) {
|
|
37
|
+
issue(
|
|
38
|
+
issues,
|
|
39
|
+
"PLUGIN_ID_FORMAT",
|
|
40
|
+
`Plugin ID "${id}" must use lowercase letters, numbers, dots, underscores, or hyphens.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
for (const id of new Set(duplicates)) {
|
|
45
|
+
issue(
|
|
46
|
+
issues,
|
|
47
|
+
"PLUGIN_ID_DUPLICATE",
|
|
48
|
+
`Plugin ID "${id}" is duplicated.`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
for (const { path, source } of sources) {
|
|
52
|
+
if (/['"]@soeditor\/[^/'"]+\/(?:src|dist|internal)(?:\/[^'"]*)?['"]/u.test(
|
|
53
|
+
source
|
|
54
|
+
)) {
|
|
55
|
+
issue(
|
|
56
|
+
issues,
|
|
57
|
+
"INTERNAL_IMPORT",
|
|
58
|
+
`${path} imports an unsupported SoEditor internal path.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!sources.some(
|
|
63
|
+
({ source }) => /from\s+['"]@soeditor\/plugin-sdk['"]/u.test(source)
|
|
64
|
+
)) {
|
|
65
|
+
issue(
|
|
66
|
+
issues,
|
|
67
|
+
"SDK_CONTRIBUTION_MISSING",
|
|
68
|
+
"Plugin source must consume contributions from the public @soeditor/plugin-sdk root."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const rootSource = sources.find(
|
|
72
|
+
({ path }) => path.endsWith("/src/index.ts")
|
|
73
|
+
)?.source;
|
|
74
|
+
if (rootSource === void 0 || !/export\s+\{/u.test(rootSource)) {
|
|
75
|
+
issue(
|
|
76
|
+
issues,
|
|
77
|
+
"ROOT_EXPORT_MISSING",
|
|
78
|
+
"src/index.ts must explicitly export the plugin contribution."
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (options.packed === true) await checkPacked(root, issues);
|
|
82
|
+
return Object.freeze({
|
|
83
|
+
issues: Object.freeze(issues.map((value) => Object.freeze(value))),
|
|
84
|
+
packageName: typeof manifest2.name === "string" ? manifest2.name : void 0,
|
|
85
|
+
pluginIds: Object.freeze(pluginIds),
|
|
86
|
+
valid: issues.length === 0
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function checkManifest(manifest2, issues) {
|
|
90
|
+
if (typeof manifest2.name !== "string" || manifest2.name.length === 0) {
|
|
91
|
+
issue(issues, "MANIFEST_NAME", "package.json requires a package name.");
|
|
92
|
+
}
|
|
93
|
+
if (typeof manifest2.version !== "string" || !/^\d+\.\d+\.\d+(?:-|$)/u.test(manifest2.version)) {
|
|
94
|
+
issue(
|
|
95
|
+
issues,
|
|
96
|
+
"MANIFEST_VERSION",
|
|
97
|
+
"package.json requires a SemVer version."
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (manifest2.type !== "module") {
|
|
101
|
+
issue(issues, "ESM_REQUIRED", "Plugin packages must use type: module.");
|
|
102
|
+
}
|
|
103
|
+
if (manifest2.sideEffects !== false) {
|
|
104
|
+
issue(
|
|
105
|
+
issues,
|
|
106
|
+
"TREE_SHAKING",
|
|
107
|
+
"Plugin packages must declare sideEffects: false."
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const exports = record(manifest2.exports);
|
|
111
|
+
const rootExport = record(exports["."]);
|
|
112
|
+
if (typeof rootExport.import !== "string" || !rootExport.import.startsWith("./dist/") || typeof rootExport.types !== "string" || !rootExport.types.startsWith("./dist/")) {
|
|
113
|
+
issue(
|
|
114
|
+
issues,
|
|
115
|
+
"EXPORT_MAP",
|
|
116
|
+
"The package root must expose dist import and types entries."
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const peers = record(manifest2.peerDependencies);
|
|
120
|
+
const sdkRange = peers["@soeditor/plugin-sdk"];
|
|
121
|
+
if (typeof sdkRange !== "string" || !/(?:\^1\.|>=\s*1\.)/u.test(sdkRange)) {
|
|
122
|
+
issue(
|
|
123
|
+
issues,
|
|
124
|
+
"SDK_PEER_RANGE",
|
|
125
|
+
"Declare @soeditor/plugin-sdk as a compatible peer dependency."
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function checkPacked(root, issues) {
|
|
130
|
+
try {
|
|
131
|
+
const { stdout } = await execute(
|
|
132
|
+
"npm",
|
|
133
|
+
["pack", "--dry-run", "--ignore-scripts", "--json"],
|
|
134
|
+
{ cwd: root, encoding: "utf8", maxBuffer: 2e6 }
|
|
135
|
+
);
|
|
136
|
+
const result = JSON.parse(stdout);
|
|
137
|
+
const entry = Array.isArray(result) ? record(result[0]) : {};
|
|
138
|
+
const files = Array.isArray(entry.files) ? entry.files.flatMap((value) => {
|
|
139
|
+
const path = record(value).path;
|
|
140
|
+
return typeof path === "string" ? [path] : [];
|
|
141
|
+
}) : [];
|
|
142
|
+
for (const required of [
|
|
143
|
+
"dist/index.js",
|
|
144
|
+
"dist/index.d.ts",
|
|
145
|
+
"package.json"
|
|
146
|
+
]) {
|
|
147
|
+
if (!files.includes(required)) {
|
|
148
|
+
issue(
|
|
149
|
+
issues,
|
|
150
|
+
"PACKED_FILE_MISSING",
|
|
151
|
+
`Packed artifact is missing ${required}.`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (files.some((path) => path.startsWith("src/"))) {
|
|
156
|
+
issue(
|
|
157
|
+
issues,
|
|
158
|
+
"PACKED_SOURCE",
|
|
159
|
+
"Packed artifact must not expose src files."
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
issue(
|
|
164
|
+
issues,
|
|
165
|
+
"PACK_FAILED",
|
|
166
|
+
`Unable to inspect packed artifact: ${error instanceof Error ? error.message : String(error)}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function sourcePaths(directory) {
|
|
171
|
+
let entries;
|
|
172
|
+
try {
|
|
173
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
174
|
+
} catch {
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
const paths = await Promise.all(
|
|
178
|
+
entries.map(async (entry) => {
|
|
179
|
+
const path = resolve(directory, entry.name);
|
|
180
|
+
return entry.isDirectory() ? sourcePaths(path) : /\.[cm]?tsx?$/u.test(entry.name) ? [path] : [];
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
return paths.flat().sort();
|
|
184
|
+
}
|
|
185
|
+
async function jsonRecord(path, issues) {
|
|
186
|
+
try {
|
|
187
|
+
return record(JSON.parse(await readFile(path, "utf8")));
|
|
188
|
+
} catch (error) {
|
|
189
|
+
issue(
|
|
190
|
+
issues,
|
|
191
|
+
"MANIFEST_READ",
|
|
192
|
+
`Unable to read package.json: ${error instanceof Error ? error.message : String(error)}`
|
|
193
|
+
);
|
|
194
|
+
return {};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function record(value) {
|
|
198
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
const result = {};
|
|
202
|
+
for (const key of Object.keys(value)) result[key] = Reflect.get(value, key);
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
function issue(issues, code, message) {
|
|
206
|
+
issues.push(Object.freeze({ code, message }));
|
|
207
|
+
}
|
|
208
|
+
async function scaffoldPluginPackage(options) {
|
|
209
|
+
const directory = resolve(nonEmpty(options.directory, "directory"));
|
|
210
|
+
const packageName = packageValue(options.packageName);
|
|
211
|
+
const pluginId = pluginIdValue(options.pluginId);
|
|
212
|
+
await mkdir(directory);
|
|
213
|
+
await mkdir(resolve(directory, "src"));
|
|
214
|
+
await mkdir(resolve(directory, "tests"));
|
|
215
|
+
const className = classNameFrom(packageName);
|
|
216
|
+
await Promise.all([
|
|
217
|
+
write(directory, "package.json", manifest(packageName)),
|
|
218
|
+
write(directory, "tsconfig.json", tsconfig()),
|
|
219
|
+
write(directory, "vite.config.ts", viteConfig()),
|
|
220
|
+
write(
|
|
221
|
+
directory,
|
|
222
|
+
"pnpm-workspace.yaml",
|
|
223
|
+
"allowBuilds:\n esbuild: true\n\nonlyBuiltDependencies:\n - esbuild\n"
|
|
224
|
+
),
|
|
225
|
+
write(
|
|
226
|
+
directory,
|
|
227
|
+
"README.md",
|
|
228
|
+
`# ${packageName}
|
|
229
|
+
|
|
230
|
+
SoEditor plugin package.
|
|
231
|
+
`
|
|
232
|
+
),
|
|
233
|
+
write(
|
|
234
|
+
directory,
|
|
235
|
+
"src/index.ts",
|
|
236
|
+
`export { ${className} } from './plugin.js';
|
|
237
|
+
`
|
|
238
|
+
),
|
|
239
|
+
write(
|
|
240
|
+
directory,
|
|
241
|
+
"src/plugin.ts",
|
|
242
|
+
`import { Plugin } from '@soeditor/plugin-sdk';
|
|
243
|
+
|
|
244
|
+
export class ${className} extends Plugin {
|
|
245
|
+
static readonly id = '${escapeSingle(pluginId)}';
|
|
246
|
+
}
|
|
247
|
+
`
|
|
248
|
+
)
|
|
249
|
+
]);
|
|
250
|
+
return directory;
|
|
251
|
+
}
|
|
252
|
+
function manifest(name) {
|
|
253
|
+
return `${JSON.stringify(
|
|
254
|
+
{
|
|
255
|
+
name,
|
|
256
|
+
version: "0.1.0",
|
|
257
|
+
license: "MIT",
|
|
258
|
+
packageManager: "pnpm@11.20.0",
|
|
259
|
+
type: "module",
|
|
260
|
+
sideEffects: false,
|
|
261
|
+
files: ["dist"],
|
|
262
|
+
exports: {
|
|
263
|
+
".": { types: "./dist/index.d.ts", import: "./dist/index.js" }
|
|
264
|
+
},
|
|
265
|
+
scripts: {
|
|
266
|
+
build: "vite build && tsc -p tsconfig.json --emitDeclarationOnly --declaration --outDir dist",
|
|
267
|
+
check: "soeditor-plugin check . --packed"
|
|
268
|
+
},
|
|
269
|
+
peerDependencies: { "@soeditor/plugin-sdk": "^1.0.0" },
|
|
270
|
+
devDependencies: {
|
|
271
|
+
"@soeditor/comments": "^1.0.0",
|
|
272
|
+
"@soeditor/core": "^1.0.0",
|
|
273
|
+
"@soeditor/engine": "^1.0.0",
|
|
274
|
+
"@soeditor/file-manager": "^1.0.0",
|
|
275
|
+
"@soeditor/html-tools": "^1.0.0",
|
|
276
|
+
"@soeditor/html": "^1.0.0",
|
|
277
|
+
"@soeditor/layout": "^1.0.0",
|
|
278
|
+
"@soeditor/plugin-sdk": "^1.0.0",
|
|
279
|
+
"@soeditor/plugin-tools": "^1.0.0",
|
|
280
|
+
"@soeditor/projections": "^1.0.0",
|
|
281
|
+
"@soeditor/revisions": "^1.0.0",
|
|
282
|
+
"@soeditor/rich-text": "^1.0.0",
|
|
283
|
+
"@soeditor/ui": "^1.0.0",
|
|
284
|
+
typescript: "^5.9.0",
|
|
285
|
+
vite: "^7.0.0"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
void 0,
|
|
289
|
+
4
|
|
290
|
+
)}
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
function tsconfig() {
|
|
294
|
+
return `${JSON.stringify(
|
|
295
|
+
{
|
|
296
|
+
compilerOptions: {
|
|
297
|
+
declaration: true,
|
|
298
|
+
declarationMap: true,
|
|
299
|
+
emitDeclarationOnly: true,
|
|
300
|
+
exactOptionalPropertyTypes: true,
|
|
301
|
+
module: "ESNext",
|
|
302
|
+
moduleResolution: "Bundler",
|
|
303
|
+
outDir: "dist",
|
|
304
|
+
strict: true,
|
|
305
|
+
target: "ES2022"
|
|
306
|
+
},
|
|
307
|
+
include: ["src/**/*.ts"]
|
|
308
|
+
},
|
|
309
|
+
void 0,
|
|
310
|
+
4
|
|
311
|
+
)}
|
|
312
|
+
`;
|
|
313
|
+
}
|
|
314
|
+
function viteConfig() {
|
|
315
|
+
return "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n build: {\n lib: { entry: 'src/index.ts', formats: ['es'], fileName: 'index' },\n rollupOptions: { external: ['@soeditor/plugin-sdk'] },\n sourcemap: true,\n },\n});\n";
|
|
316
|
+
}
|
|
317
|
+
async function write(root, path, source) {
|
|
318
|
+
await writeFile(resolve(root, path), source, {
|
|
319
|
+
encoding: "utf8",
|
|
320
|
+
flag: "wx"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function packageValue(value) {
|
|
324
|
+
const name = nonEmpty(value, "package name");
|
|
325
|
+
if (!/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u.test(name)) {
|
|
326
|
+
throw new TypeError(`Invalid npm package name "${name}".`);
|
|
327
|
+
}
|
|
328
|
+
return name;
|
|
329
|
+
}
|
|
330
|
+
function pluginIdValue(value) {
|
|
331
|
+
const id = nonEmpty(value, "plugin ID");
|
|
332
|
+
if (!/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u.test(id)) {
|
|
333
|
+
throw new TypeError(
|
|
334
|
+
`Invalid plugin ID "${id}"; use lowercase letters, numbers, dots, underscores, or hyphens.`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return id;
|
|
338
|
+
}
|
|
339
|
+
function classNameFrom(name) {
|
|
340
|
+
const stem = basename(name).replace(
|
|
341
|
+
/[^a-zA-Z0-9]+(.)/gu,
|
|
342
|
+
(_match, next) => next.toUpperCase()
|
|
343
|
+
);
|
|
344
|
+
const normalized = stem.replace(/^[^a-zA-Z]+/u, "");
|
|
345
|
+
return `${normalized.length === 0 ? "SoEditor" : normalized[0].toUpperCase() + normalized.slice(1)}Plugin`;
|
|
346
|
+
}
|
|
347
|
+
function nonEmpty(value, label) {
|
|
348
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
349
|
+
throw new TypeError(`Plugin ${label} must be a non-empty string.`);
|
|
350
|
+
}
|
|
351
|
+
return value.trim();
|
|
352
|
+
}
|
|
353
|
+
function escapeSingle(value) {
|
|
354
|
+
return value.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
|
355
|
+
}
|
|
356
|
+
export {
|
|
357
|
+
checkPluginPackage as c,
|
|
358
|
+
scaffoldPluginPackage as s
|
|
359
|
+
};
|
|
360
|
+
//# sourceMappingURL=scaffold-BD62-b7s.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-BD62-b7s.js","sources":["../src/plugin-check.ts","../src/scaffold.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { readFile, readdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execute = promisify(execFile);\n\nexport interface PluginCheckOptions {\n readonly packed?: boolean;\n}\n\nexport interface PluginCheckIssue {\n readonly code: string;\n readonly message: string;\n}\n\nexport interface PluginCheckReport {\n readonly issues: readonly PluginCheckIssue[];\n readonly packageName: string | undefined;\n readonly pluginIds: readonly string[];\n readonly valid: boolean;\n}\n\n/** Audits a plugin package without importing or executing its source. */\nexport async function checkPluginPackage(\n directory: string,\n options: PluginCheckOptions = {},\n): Promise<PluginCheckReport> {\n const root = resolve(directory);\n const issues: PluginCheckIssue[] = [];\n const manifest = await jsonRecord(resolve(root, 'package.json'), issues);\n checkManifest(manifest, issues);\n const sourceFiles = await sourcePaths(resolve(root, 'src'));\n const sources = await Promise.all(\n sourceFiles.map(async (path) => ({\n path,\n source: await readFile(path, 'utf8'),\n })),\n );\n const pluginIds = sources.flatMap(({ source }) =>\n [\n ...source.matchAll(\n /static\\s+readonly\\s+id\\s*=\\s*['\"]([^'\"]+)['\"]/gu,\n ),\n ].map((match) => match[1]!),\n );\n if (pluginIds.length === 0) {\n issue(\n issues,\n 'PLUGIN_ID_MISSING',\n 'No static readonly plugin ID was found.',\n );\n }\n const duplicates = pluginIds.filter(\n (id, index) => pluginIds.indexOf(id) !== index,\n );\n for (const id of pluginIds) {\n if (!/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u.test(id)) {\n issue(\n issues,\n 'PLUGIN_ID_FORMAT',\n `Plugin ID \"${id}\" must use lowercase letters, numbers, dots, underscores, or hyphens.`,\n );\n }\n }\n for (const id of new Set(duplicates)) {\n issue(\n issues,\n 'PLUGIN_ID_DUPLICATE',\n `Plugin ID \"${id}\" is duplicated.`,\n );\n }\n for (const { path, source } of sources) {\n if (\n /['\"]@soeditor\\/[^/'\"]+\\/(?:src|dist|internal)(?:\\/[^'\"]*)?['\"]/u.test(\n source,\n )\n ) {\n issue(\n issues,\n 'INTERNAL_IMPORT',\n `${path} imports an unsupported SoEditor internal path.`,\n );\n }\n }\n if (\n !sources.some(({ source }) =>\n /from\\s+['\"]@soeditor\\/plugin-sdk['\"]/u.test(source),\n )\n ) {\n issue(\n issues,\n 'SDK_CONTRIBUTION_MISSING',\n 'Plugin source must consume contributions from the public @soeditor/plugin-sdk root.',\n );\n }\n const rootSource = sources.find(({ path }) =>\n path.endsWith('/src/index.ts'),\n )?.source;\n if (rootSource === undefined || !/export\\s+\\{/u.test(rootSource)) {\n issue(\n issues,\n 'ROOT_EXPORT_MISSING',\n 'src/index.ts must explicitly export the plugin contribution.',\n );\n }\n if (options.packed === true) await checkPacked(root, issues);\n return Object.freeze({\n issues: Object.freeze(issues.map((value) => Object.freeze(value))),\n packageName:\n typeof manifest.name === 'string' ? manifest.name : undefined,\n pluginIds: Object.freeze(pluginIds),\n valid: issues.length === 0,\n });\n}\n\nfunction checkManifest(\n manifest: Record<string, unknown>,\n issues: PluginCheckIssue[],\n): void {\n if (typeof manifest.name !== 'string' || manifest.name.length === 0) {\n issue(issues, 'MANIFEST_NAME', 'package.json requires a package name.');\n }\n if (\n typeof manifest.version !== 'string' ||\n !/^\\d+\\.\\d+\\.\\d+(?:-|$)/u.test(manifest.version)\n ) {\n issue(\n issues,\n 'MANIFEST_VERSION',\n 'package.json requires a SemVer version.',\n );\n }\n if (manifest.type !== 'module') {\n issue(issues, 'ESM_REQUIRED', 'Plugin packages must use type: module.');\n }\n if (manifest.sideEffects !== false) {\n issue(\n issues,\n 'TREE_SHAKING',\n 'Plugin packages must declare sideEffects: false.',\n );\n }\n const exports = record(manifest.exports);\n const rootExport = record(exports['.']);\n if (\n typeof rootExport.import !== 'string' ||\n !rootExport.import.startsWith('./dist/') ||\n typeof rootExport.types !== 'string' ||\n !rootExport.types.startsWith('./dist/')\n ) {\n issue(\n issues,\n 'EXPORT_MAP',\n 'The package root must expose dist import and types entries.',\n );\n }\n const peers = record(manifest.peerDependencies);\n const sdkRange = peers['@soeditor/plugin-sdk'];\n if (typeof sdkRange !== 'string' || !/(?:\\^1\\.|>=\\s*1\\.)/u.test(sdkRange)) {\n issue(\n issues,\n 'SDK_PEER_RANGE',\n 'Declare @soeditor/plugin-sdk as a compatible peer dependency.',\n );\n }\n}\n\nasync function checkPacked(\n root: string,\n issues: PluginCheckIssue[],\n): Promise<void> {\n try {\n const { stdout } = await execute(\n 'npm',\n ['pack', '--dry-run', '--ignore-scripts', '--json'],\n { cwd: root, encoding: 'utf8', maxBuffer: 2_000_000 },\n );\n const result: unknown = JSON.parse(stdout);\n const entry = Array.isArray(result) ? record(result[0]) : {};\n const files = Array.isArray(entry.files)\n ? entry.files.flatMap((value) => {\n const path = record(value).path;\n return typeof path === 'string' ? [path] : [];\n })\n : [];\n for (const required of [\n 'dist/index.js',\n 'dist/index.d.ts',\n 'package.json',\n ]) {\n if (!files.includes(required)) {\n issue(\n issues,\n 'PACKED_FILE_MISSING',\n `Packed artifact is missing ${required}.`,\n );\n }\n }\n if (files.some((path) => path.startsWith('src/'))) {\n issue(\n issues,\n 'PACKED_SOURCE',\n 'Packed artifact must not expose src files.',\n );\n }\n } catch (error: unknown) {\n issue(\n issues,\n 'PACK_FAILED',\n `Unable to inspect packed artifact: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n}\n\nasync function sourcePaths(directory: string): Promise<string[]> {\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch {\n return [];\n }\n const paths = await Promise.all(\n entries.map(async (entry) => {\n const path = resolve(directory, entry.name);\n return entry.isDirectory()\n ? sourcePaths(path)\n : /\\.[cm]?tsx?$/u.test(entry.name)\n ? [path]\n : [];\n }),\n );\n return paths.flat().sort();\n}\n\nasync function jsonRecord(\n path: string,\n issues: PluginCheckIssue[],\n): Promise<Record<string, unknown>> {\n try {\n return record(JSON.parse(await readFile(path, 'utf8')));\n } catch (error: unknown) {\n issue(\n issues,\n 'MANIFEST_READ',\n `Unable to read package.json: ${error instanceof Error ? error.message : String(error)}`,\n );\n return {};\n }\n}\n\nfunction record(value: unknown): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return {};\n }\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value)) result[key] = Reflect.get(value, key);\n return result;\n}\n\nfunction issue(\n issues: PluginCheckIssue[],\n code: string,\n message: string,\n): void {\n issues.push(Object.freeze({ code, message }));\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { basename, resolve } from 'node:path';\n\nexport interface ScaffoldPluginOptions {\n readonly directory: string;\n readonly packageName: string;\n readonly pluginId: string;\n}\n\n/** Creates a versioned, strict plugin package without overwriting a directory. */\nexport async function scaffoldPluginPackage(\n options: ScaffoldPluginOptions,\n): Promise<string> {\n const directory = resolve(nonEmpty(options.directory, 'directory'));\n const packageName = packageValue(options.packageName);\n const pluginId = pluginIdValue(options.pluginId);\n await mkdir(directory);\n await mkdir(resolve(directory, 'src'));\n await mkdir(resolve(directory, 'tests'));\n const className = classNameFrom(packageName);\n await Promise.all([\n write(directory, 'package.json', manifest(packageName)),\n write(directory, 'tsconfig.json', tsconfig()),\n write(directory, 'vite.config.ts', viteConfig()),\n write(\n directory,\n 'pnpm-workspace.yaml',\n 'allowBuilds:\\n esbuild: true\\n\\nonlyBuiltDependencies:\\n - esbuild\\n',\n ),\n write(\n directory,\n 'README.md',\n `# ${packageName}\\n\\nSoEditor plugin package.\\n`,\n ),\n write(\n directory,\n 'src/index.ts',\n `export { ${className} } from './plugin.js';\\n`,\n ),\n write(\n directory,\n 'src/plugin.ts',\n `import { Plugin } from '@soeditor/plugin-sdk';\\n\\nexport class ${className} extends Plugin {\\n static readonly id = '${escapeSingle(pluginId)}';\\n}\\n`,\n ),\n ]);\n return directory;\n}\n\nfunction manifest(name: string): string {\n return `${JSON.stringify(\n {\n name,\n version: '0.1.0',\n license: 'MIT',\n packageManager: 'pnpm@11.20.0',\n type: 'module',\n sideEffects: false,\n files: ['dist'],\n exports: {\n '.': { types: './dist/index.d.ts', import: './dist/index.js' },\n },\n scripts: {\n build: 'vite build && tsc -p tsconfig.json --emitDeclarationOnly --declaration --outDir dist',\n check: 'soeditor-plugin check . --packed',\n },\n peerDependencies: { '@soeditor/plugin-sdk': '^1.0.0' },\n devDependencies: {\n '@soeditor/comments': '^1.0.0',\n '@soeditor/core': '^1.0.0',\n '@soeditor/engine': '^1.0.0',\n '@soeditor/file-manager': '^1.0.0',\n '@soeditor/html-tools': '^1.0.0',\n '@soeditor/html': '^1.0.0',\n '@soeditor/layout': '^1.0.0',\n '@soeditor/plugin-sdk': '^1.0.0',\n '@soeditor/plugin-tools': '^1.0.0',\n '@soeditor/projections': '^1.0.0',\n '@soeditor/revisions': '^1.0.0',\n '@soeditor/rich-text': '^1.0.0',\n '@soeditor/ui': '^1.0.0',\n typescript: '^5.9.0',\n vite: '^7.0.0',\n },\n },\n undefined,\n 4,\n )}\\n`;\n}\n\nfunction tsconfig(): string {\n return `${JSON.stringify(\n {\n compilerOptions: {\n declaration: true,\n declarationMap: true,\n emitDeclarationOnly: true,\n exactOptionalPropertyTypes: true,\n module: 'ESNext',\n moduleResolution: 'Bundler',\n outDir: 'dist',\n strict: true,\n target: 'ES2022',\n },\n include: ['src/**/*.ts'],\n },\n undefined,\n 4,\n )}\\n`;\n}\n\nfunction viteConfig(): string {\n return \"import { defineConfig } from 'vite';\\n\\nexport default defineConfig({\\n build: {\\n lib: { entry: 'src/index.ts', formats: ['es'], fileName: 'index' },\\n rollupOptions: { external: ['@soeditor/plugin-sdk'] },\\n sourcemap: true,\\n },\\n});\\n\";\n}\n\nasync function write(\n root: string,\n path: string,\n source: string,\n): Promise<void> {\n await writeFile(resolve(root, path), source, {\n encoding: 'utf8',\n flag: 'wx',\n });\n}\n\nfunction packageValue(value: string): string {\n const name = nonEmpty(value, 'package name');\n if (!/^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$/u.test(name)) {\n throw new TypeError(`Invalid npm package name \"${name}\".`);\n }\n return name;\n}\n\nfunction pluginIdValue(value: string): string {\n const id = nonEmpty(value, 'plugin ID');\n if (!/^[a-z0-9](?:[a-z0-9._-]*[a-z0-9])?$/u.test(id)) {\n throw new TypeError(\n `Invalid plugin ID \"${id}\"; use lowercase letters, numbers, dots, underscores, or hyphens.`,\n );\n }\n return id;\n}\n\nfunction classNameFrom(name: string): string {\n const stem = basename(name).replace(\n /[^a-zA-Z0-9]+(.)/gu,\n (_match, next: string) => next.toUpperCase(),\n );\n const normalized = stem.replace(/^[^a-zA-Z]+/u, '');\n return `${normalized.length === 0 ? 'SoEditor' : normalized[0]!.toUpperCase() + normalized.slice(1)}Plugin`;\n}\n\nfunction nonEmpty(value: unknown, label: string): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new TypeError(`Plugin ${label} must be a non-empty string.`);\n }\n return value.trim();\n}\n\nfunction escapeSingle(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll(\"'\", \"\\\\'\");\n}\n"],"names":["manifest"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ScaffoldPluginOptions {
|
|
2
|
+
readonly directory: string;
|
|
3
|
+
readonly packageName: string;
|
|
4
|
+
readonly pluginId: string;
|
|
5
|
+
}
|
|
6
|
+
/** Creates a versioned, strict plugin package without overwriting a directory. */
|
|
7
|
+
export declare function scaffoldPluginPackage(options: ScaffoldPluginOptions): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC7B;AAED,kFAAkF;AAClF,wBAAsB,qBAAqB,CACvC,OAAO,EAAE,qBAAqB,GAC/B,OAAO,CAAC,MAAM,CAAC,CAkCjB"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@soeditor/plugin-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Offline plugin scaffolding and package checks for SoEditor.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"content-editor",
|
|
8
|
+
"plugin",
|
|
9
|
+
"typescript"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/sohophp/soeditor#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/sohophp/soeditor/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/sohophp/soeditor.git",
|
|
18
|
+
"directory": "packages/plugin-tools"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public",
|
|
22
|
+
"provenance": true,
|
|
23
|
+
"registry": "https://registry.npmjs.org/"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"bin": {
|
|
31
|
+
"soeditor-plugin": "./dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22.14.0 <23"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.18.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
49
|
+
}
|
|
50
|
+
}
|