dsh-toolfold 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -1
- package/README.en.md +130 -90
- package/README.md +118 -63
- package/lib/client.js +1954 -1836
- package/lib/index.js +265 -128
- package/package.json +17 -3
- package/lib/build-dynamic.cjs +0 -69
- package/lib/dynamic-body.js +0 -1732
package/lib/index.js
CHANGED
|
@@ -1,137 +1,274 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import z from "schemastery";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
//#region src/host/index.js
|
|
1
6
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
7
|
+
* dsh-toolfold — HOST half.
|
|
8
|
+
*
|
|
9
|
+
* Registers the `toolfold` settings namespace in the DSH settings service
|
|
10
|
+
* (persisted in `~/.dsh/settings.yaml`, the same document every product and
|
|
11
|
+
* family plugin uses) and serves the browser half through one same-origin
|
|
12
|
+
* JSON route:
|
|
13
|
+
*
|
|
14
|
+
* GET /api/dsh-toolfold/settings → { ok, value: { value, revision, writable } }
|
|
15
|
+
* POST /api/dsh-toolfold/settings → body { op: 'set'|'unset', field, value?,
|
|
16
|
+
* expectedRevision? }
|
|
17
|
+
* → { ok, value: { value, revision, writable } }
|
|
18
|
+
*
|
|
19
|
+
* Every success response additionally carries `dsh: { version, state }` — the
|
|
20
|
+
* running DSH product version and its compatibility with the supported range
|
|
21
|
+
* ('ok' | 'old' | 'new' | 'unknown'). DSH does not enforce `engines.dsh`
|
|
22
|
+
* anywhere, so the host half reports the mismatch itself and the settings
|
|
23
|
+
* card warns the user (once-only console warning + hover-tip icon).
|
|
24
|
+
*
|
|
25
|
+
* The browser half prefers the official `settingsScope` transport when the
|
|
26
|
+
* deployment exposes this namespace, then this route, then browser
|
|
27
|
+
* localStorage as a degraded fallback. `value` is always the fully resolved
|
|
28
|
+
* section (schema defaults + composition base + the user's settings.yaml
|
|
29
|
+
* overrides), so the client never re-implements the resolution.
|
|
30
|
+
*
|
|
31
|
+
* The same package also declares `dsh.bundle.patch` (see cordis.patch.yml),
|
|
32
|
+
* which makes `dsh plugin --profile <name> add <this package>` install AND
|
|
33
|
+
* mount both halves in one command.
|
|
34
|
+
*/
|
|
35
|
+
const name = "toolfold";
|
|
36
|
+
const inject = ["settings", "webServer"];
|
|
37
|
+
const NS = "toolfold";
|
|
38
|
+
const API_PATH = "/api/dsh-toolfold/settings";
|
|
39
|
+
const FIELDS = [
|
|
40
|
+
"enabled",
|
|
41
|
+
"durMs",
|
|
42
|
+
"thinkMode",
|
|
43
|
+
"keepThink",
|
|
44
|
+
"thinkAuto",
|
|
45
|
+
"splitThink",
|
|
46
|
+
"stats"
|
|
47
|
+
];
|
|
34
48
|
const SCHEMA = z.object({
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
enabled: z.boolean().default(true),
|
|
50
|
+
durMs: z.number().step(10).min(0).max(2e3).default(240),
|
|
51
|
+
thinkMode: z.union([
|
|
52
|
+
z.const("auto"),
|
|
53
|
+
z.const("keep"),
|
|
54
|
+
z.const("hide")
|
|
55
|
+
]),
|
|
56
|
+
keepThink: z.boolean().default(false),
|
|
57
|
+
thinkAuto: z.boolean().default(true),
|
|
58
|
+
splitThink: z.boolean().default(true),
|
|
59
|
+
stats: z.boolean().default(false)
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the canonical think mode for one section. An explicitly set,
|
|
63
|
+
* valid thinkMode wins; otherwise derive it from the deprecated keys so
|
|
64
|
+
* pre-merge preferences survive the upgrade; default 'auto'. Total —
|
|
65
|
+
* never throws. (Client twin: resolveThinkMode in src/client/settings.js.)
|
|
66
|
+
*/
|
|
67
|
+
function resolveThinkMode(section) {
|
|
68
|
+
var mode = section !== void 0 && section !== null ? section.thinkMode : void 0;
|
|
69
|
+
if (mode === "keep" || mode === "hide" || mode === "auto") return mode;
|
|
70
|
+
if (section !== void 0 && section !== null && section.thinkAuto === false) return section.keepThink === true ? "keep" : "hide";
|
|
71
|
+
return "auto";
|
|
72
|
+
}
|
|
73
|
+
const DSH_MIN = "0.1.2-rc.1";
|
|
74
|
+
const DSH_MAX = "0.1.3";
|
|
75
|
+
/** Parse "v1.2.3-rc.4+build" into comparable parts; null on junk. */
|
|
76
|
+
function parseVersion(value) {
|
|
77
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(String(value));
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
return {
|
|
80
|
+
core: [
|
|
81
|
+
Number(match[1]),
|
|
82
|
+
Number(match[2]),
|
|
83
|
+
Number(match[3])
|
|
84
|
+
],
|
|
85
|
+
pre: match[4] === void 0 ? null : match[4].split(".")
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Compare two parsed versions (semver precedence, prerelease-aware): -1/0/1. */
|
|
89
|
+
function compareParsed(a, b) {
|
|
90
|
+
for (let i = 0; i < 3; i++) if (a.core[i] !== b.core[i]) return a.core[i] < b.core[i] ? -1 : 1;
|
|
91
|
+
if (a.pre === null && b.pre === null) return 0;
|
|
92
|
+
if (a.pre === null) return 1;
|
|
93
|
+
if (b.pre === null) return -1;
|
|
94
|
+
const len = Math.max(a.pre.length, b.pre.length);
|
|
95
|
+
for (let i = 0; i < len; i++) {
|
|
96
|
+
const x = a.pre[i];
|
|
97
|
+
const y = b.pre[i];
|
|
98
|
+
if (x === void 0) return -1;
|
|
99
|
+
if (y === void 0) return 1;
|
|
100
|
+
const xn = /^\d+$/.test(x);
|
|
101
|
+
const yn = /^\d+$/.test(y);
|
|
102
|
+
if (xn && yn) {
|
|
103
|
+
const dx = Number(x);
|
|
104
|
+
const dy = Number(y);
|
|
105
|
+
if (dx !== dy) return dx < dy ? -1 : 1;
|
|
106
|
+
} else if (xn !== yn) return xn ? -1 : 1;
|
|
107
|
+
else if (x !== y) return x < y ? -1 : 1;
|
|
108
|
+
}
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The @deepseek-ai/dsh version this process was launched from: process.argv[1]
|
|
113
|
+
* is the CLI's entry, and module resolution from that file reaches the
|
|
114
|
+
* @deepseek-ai/dsh package.json it belongs to. Null when undetectable
|
|
115
|
+
* (workers, dev launchers without a resolvable package).
|
|
116
|
+
*/
|
|
117
|
+
let cachedDshVersion;
|
|
118
|
+
function dshVersion() {
|
|
119
|
+
if (cachedDshVersion !== void 0) return cachedDshVersion;
|
|
120
|
+
cachedDshVersion = null;
|
|
121
|
+
try {
|
|
122
|
+
const entry = process.argv && process.argv[1];
|
|
123
|
+
if (!entry) return cachedDshVersion;
|
|
124
|
+
const pkgPath = createRequire(resolve(entry)).resolve("@deepseek-ai/dsh/package.json");
|
|
125
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
126
|
+
if (pkg && pkg.name === "@deepseek-ai/dsh" && typeof pkg.version === "string" && pkg.version !== "") cachedDshVersion = pkg.version;
|
|
127
|
+
} catch (error) {}
|
|
128
|
+
return cachedDshVersion;
|
|
129
|
+
}
|
|
130
|
+
/** Running DSH vs the supported range: 'ok' | 'old' | 'new' | 'unknown'. */
|
|
131
|
+
function dshCompat() {
|
|
132
|
+
const version = dshVersion();
|
|
133
|
+
if (version === null) return {
|
|
134
|
+
version: null,
|
|
135
|
+
state: "unknown"
|
|
136
|
+
};
|
|
137
|
+
const parsed = parseVersion(version);
|
|
138
|
+
if (parsed === null) return {
|
|
139
|
+
version,
|
|
140
|
+
state: "unknown"
|
|
141
|
+
};
|
|
142
|
+
const min = parseVersion(DSH_MIN);
|
|
143
|
+
const max = parseVersion(DSH_MAX);
|
|
144
|
+
let state = "ok";
|
|
145
|
+
if (compareParsed(parsed, min) < 0) state = "old";
|
|
146
|
+
else if (compareParsed(parsed, max) >= 0) state = "new";
|
|
147
|
+
return {
|
|
148
|
+
version,
|
|
149
|
+
state
|
|
150
|
+
};
|
|
151
|
+
}
|
|
41
152
|
/** Write one JSON response. */
|
|
42
153
|
function json(res, status, body) {
|
|
43
|
-
|
|
44
|
-
|
|
154
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
155
|
+
res.end(JSON.stringify(body));
|
|
45
156
|
}
|
|
46
|
-
|
|
47
157
|
/** Read a JSON request body (bounded). */
|
|
48
158
|
function readJsonBody(req) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
160
|
+
let size = 0;
|
|
161
|
+
const chunks = [];
|
|
162
|
+
req.on("data", (chunk) => {
|
|
163
|
+
size += chunk.length;
|
|
164
|
+
if (size > 65536) {
|
|
165
|
+
reject(/* @__PURE__ */ new Error("body-too-large"));
|
|
166
|
+
req.destroy();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
chunks.push(chunk);
|
|
170
|
+
});
|
|
171
|
+
req.on("end", () => {
|
|
172
|
+
if (chunks.length === 0) {
|
|
173
|
+
resolve({});
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
178
|
+
} catch {
|
|
179
|
+
reject(/* @__PURE__ */ new Error("invalid-json"));
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
req.on("error", reject);
|
|
183
|
+
});
|
|
74
184
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
185
|
+
function apply(ctx) {
|
|
186
|
+
ctx.settings.register(NS, SCHEMA, { base: {} });
|
|
187
|
+
/** Current resolved section + revision + writability, as one JSON view. */
|
|
188
|
+
const snapshot = () => {
|
|
189
|
+
let value = ctx.settings.get(NS);
|
|
190
|
+
let revision;
|
|
191
|
+
for (const descriptor of ctx.settings.describe()) if (descriptor.ns === NS) {
|
|
192
|
+
value = descriptor.value;
|
|
193
|
+
revision = descriptor.revision;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
ok: true,
|
|
198
|
+
value: {
|
|
199
|
+
value: {
|
|
200
|
+
...value,
|
|
201
|
+
thinkMode: resolveThinkMode(value)
|
|
202
|
+
},
|
|
203
|
+
...revision === void 0 ? {} : { revision },
|
|
204
|
+
writable: ctx.settings.writable
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const handler = (req, res) => {
|
|
209
|
+
if (req.method === "GET") {
|
|
210
|
+
try {
|
|
211
|
+
json(res, 200, {
|
|
212
|
+
...snapshot(),
|
|
213
|
+
dsh: dshCompat()
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
json(res, 500, {
|
|
217
|
+
ok: false,
|
|
218
|
+
error: error instanceof Error ? error.message : String(error)
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (req.method === "POST") {
|
|
224
|
+
readJsonBody(req).then((body) => {
|
|
225
|
+
const field = String(body?.field ?? "");
|
|
226
|
+
if (!FIELDS.includes(field)) {
|
|
227
|
+
json(res, 400, {
|
|
228
|
+
ok: false,
|
|
229
|
+
error: "invalid-field"
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const ops = (body?.op === "unset" ? "unset" : "set") === "unset" ? [{
|
|
234
|
+
op: "unset",
|
|
235
|
+
path: [field]
|
|
236
|
+
}] : [{
|
|
237
|
+
op: "set",
|
|
238
|
+
path: [field],
|
|
239
|
+
value: body.value
|
|
240
|
+
}];
|
|
241
|
+
const expectedRevision = typeof body?.expectedRevision === "number" ? body.expectedRevision : void 0;
|
|
242
|
+
return ctx.settings.mutate(NS, ops, expectedRevision).then(() => json(res, 200, {
|
|
243
|
+
...snapshot(),
|
|
244
|
+
dsh: dshCompat()
|
|
245
|
+
})).catch((error) => json(res, 400, {
|
|
246
|
+
ok: false,
|
|
247
|
+
error: error instanceof Error ? error.message : String(error)
|
|
248
|
+
}));
|
|
249
|
+
}, (error) => {
|
|
250
|
+
json(res, 400, {
|
|
251
|
+
ok: false,
|
|
252
|
+
error: error instanceof Error ? error.message : String(error)
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
json(res, 405, {
|
|
258
|
+
ok: false,
|
|
259
|
+
error: "method-not-allowed"
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
ctx.effect(() => {
|
|
263
|
+
const dispose = ctx.webServer.register({
|
|
264
|
+
kind: "exact",
|
|
265
|
+
path: API_PATH,
|
|
266
|
+
handler
|
|
267
|
+
});
|
|
268
|
+
return () => dispose();
|
|
269
|
+
});
|
|
137
270
|
}
|
|
271
|
+
//#endregion
|
|
272
|
+
export { apply, inject, name };
|
|
273
|
+
|
|
274
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-toolfold",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "工具调用与思考的折叠显示:连续工具调用折叠为最后一个调用(可展开),已完成的思考随组折叠(可保留、展开时按原顺序显示),进行中的思考保持独立显示。",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Minecraftbe/dsh-toolfold.git"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "lib/index.js",
|
|
7
11
|
"exports": {
|
|
@@ -23,19 +27,29 @@
|
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
"files": [
|
|
26
|
-
"lib",
|
|
30
|
+
"lib/index.js",
|
|
31
|
+
"lib/client.js",
|
|
27
32
|
"cordis.patch.yml",
|
|
28
33
|
"scripts",
|
|
29
34
|
"README.md",
|
|
30
35
|
"README.en.md",
|
|
31
36
|
"CHANGELOG.md"
|
|
32
37
|
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"dsh": ">=0.1.2-rc.1 <0.1.3"
|
|
40
|
+
},
|
|
33
41
|
"peerDependencies": {
|
|
34
|
-
"@deepseek-ai/dsh-settings": ">=0.1.0-rc.7 <0.1.1 || >=0.1.1-rc.0 <0.1.2",
|
|
35
42
|
"schemastery": "^3.18.0"
|
|
36
43
|
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"jsdom": "29.1.1",
|
|
46
|
+
"playwright": "1.61.1",
|
|
47
|
+
"tsdown": "^0.22.2"
|
|
48
|
+
},
|
|
37
49
|
"license": "MIT",
|
|
38
50
|
"scripts": {
|
|
51
|
+
"build": "tsdown",
|
|
52
|
+
"build:watch": "tsdown --watch",
|
|
39
53
|
"test:engine": "node tools/engine-smoke.mjs",
|
|
40
54
|
"probe:live": "node tools/live-probe.mjs",
|
|
41
55
|
"install:dsh": "node scripts/install-dsh.cjs",
|
package/lib/build-dynamic.cjs
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* build-dynamic.js — generate lib/dynamic-body.js (the `code.client` body for
|
|
3
|
-
* cordis_define) from lib/client.js.
|
|
4
|
-
*
|
|
5
|
-
* The dynamic runner wraps the body in `async () => { ... }` and evaluates it
|
|
6
|
-
* in a scope where `React` is a closure parameter, so the body must:
|
|
7
|
-
* - NOT redeclare `var React` (var hoisting would shadow the closure
|
|
8
|
-
* parameter and the settings card would silently never render);
|
|
9
|
-
* - end with `return plugin;` (a bare function body has no exports).
|
|
10
|
-
*
|
|
11
|
-
* Usage: node lib/build-dynamic.js
|
|
12
|
-
*/
|
|
13
|
-
'use strict';
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
const srcPath = path.join(__dirname, 'client.js');
|
|
19
|
-
const outPath = path.join(__dirname, 'dynamic-body.js');
|
|
20
|
-
|
|
21
|
-
let src = fs.readFileSync(srcPath, 'utf8');
|
|
22
|
-
|
|
23
|
-
// 1. Extract the factory function body: everything between
|
|
24
|
-
// "factory: function (require) {" and the final "\n});".
|
|
25
|
-
const factoryMarker = 'factory: function (require) {';
|
|
26
|
-
const start = src.indexOf(factoryMarker);
|
|
27
|
-
if (start === -1) throw new Error('factory marker not found in client.js');
|
|
28
|
-
const bodyStart = start + factoryMarker.length;
|
|
29
|
-
|
|
30
|
-
const endMarker = '\n});';
|
|
31
|
-
const end = src.lastIndexOf(endMarker);
|
|
32
|
-
if (end === -1) throw new Error('module end marker not found in client.js');
|
|
33
|
-
|
|
34
|
-
let body = src.slice(bodyStart, end);
|
|
35
|
-
// Drop the factory's own closing brace (the trailing " }").
|
|
36
|
-
body = body.replace(/\n[ \t]*\}\s*$/, '');
|
|
37
|
-
|
|
38
|
-
// 2. Remove the CommonJS export boilerplate.
|
|
39
|
-
const boilerplate =
|
|
40
|
-
' var module = { exports: {} };\n' +
|
|
41
|
-
' var exports = module.exports;\n' +
|
|
42
|
-
' Object.defineProperty(exports, Symbol.toStringTag, { value: \'Module\' });\n';
|
|
43
|
-
if (body.indexOf(boilerplate) === -1) throw new Error('module boilerplate not found');
|
|
44
|
-
body = body.replace(boilerplate, '');
|
|
45
|
-
|
|
46
|
-
// 3. Replace the React require block with an explanatory comment (see header).
|
|
47
|
-
const reactBlock =
|
|
48
|
-
' // React is a platform seed word in the web shell; the settings card\n' +
|
|
49
|
-
' // needs it, the folding engine does not.\n' +
|
|
50
|
-
' var React = null;\n' +
|
|
51
|
-
' try {\n' +
|
|
52
|
-
' React = require(\'react\');\n' +
|
|
53
|
-
' } catch (err) {\n' +
|
|
54
|
-
' React = null;\n' +
|
|
55
|
-
' }\n';
|
|
56
|
-
if (body.indexOf(reactBlock) === -1) throw new Error('React block not found');
|
|
57
|
-
body = body.replace(reactBlock,
|
|
58
|
-
' // React is the runner\'s closure parameter; the folding engine does not\n' +
|
|
59
|
-
' // need it directly, and redeclaring it here would shadow the parameter.\n');
|
|
60
|
-
|
|
61
|
-
// 4. Replace the module.exports tail with a bare `return plugin;`.
|
|
62
|
-
const tail =
|
|
63
|
-
' module.exports = plugin;\n' +
|
|
64
|
-
' return module.exports;';
|
|
65
|
-
if (body.indexOf(tail) === -1) throw new Error('export tail not found');
|
|
66
|
-
body = body.replace(tail, 'return plugin;\n');
|
|
67
|
-
|
|
68
|
-
fs.writeFileSync(outPath, body, 'utf8');
|
|
69
|
-
console.log('wrote', outPath, body.length, 'bytes');
|