@vobs/cli 1.7.8 → 1.8.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/dist/banner.cjs +22 -3
- package/dist/banner.cjs.map +1 -1
- package/dist/banner.d.cts +6 -2
- package/dist/banner.d.ts +6 -2
- package/dist/banner.js +20 -3
- package/dist/banner.js.map +1 -1
- package/dist/cli.cjs +361 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +10 -1
- package/dist/cli.d.ts +10 -1
- package/dist/cli.js +351 -12
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.cjs +2 -2
- package/dist/commands/add.cjs.map +1 -1
- package/dist/commands/add.js +2 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/build.cjs +2 -2
- package/dist/commands/build.cjs.map +1 -1
- package/dist/commands/build.js +2 -2
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.cjs +382 -0
- package/dist/commands/check.cjs.map +1 -0
- package/dist/commands/check.d.cts +34 -0
- package/dist/commands/check.d.ts +34 -0
- package/dist/commands/check.js +343 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/dev.cjs +2 -2
- package/dist/commands/dev.cjs.map +1 -1
- package/dist/commands/dev.js +2 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/dsh.cjs +2 -2
- package/dist/commands/dsh.cjs.map +1 -1
- package/dist/commands/dsh.js +2 -2
- package/dist/commands/dsh.js.map +1 -1
- package/dist/commands/generate.cjs +2 -2
- package/dist/commands/generate.cjs.map +1 -1
- package/dist/commands/generate.js +2 -2
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/index.cjs +34 -17
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.js +25 -8
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/init.cjs +20 -3
- package/dist/commands/init.cjs.map +1 -1
- package/dist/commands/init.js +20 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/index.cjs +399 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +388 -14
- package/dist/index.js.map +1 -1
- package/dist/repl.cjs +34 -17
- package/dist/repl.cjs.map +1 -1
- package/dist/repl.js +25 -8
- package/dist/repl.js.map +1 -1
- package/dist/start.cjs +399 -25
- package/dist/start.cjs.map +1 -1
- package/dist/start.d.cts +8 -1
- package/dist/start.d.ts +8 -1
- package/dist/start.js +388 -14
- package/dist/start.js.map +1 -1
- package/dist/utils/logger.cjs +2 -2
- package/dist/utils/logger.cjs.map +1 -1
- package/dist/utils/logger.js +2 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/version.cjs +47 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.d.cts +3 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -2
- package/src/banner.ts +11 -5
- package/src/cli-process.test.ts +139 -0
- package/src/cli.ts +30 -2
- package/src/commands/check.test.ts +238 -0
- package/src/commands/check.ts +461 -0
- package/src/commands/init.ts +2 -1
- package/src/start.ts +66 -5
- package/src/utils/logger.ts +14 -2
- package/src/version.ts +27 -0
package/dist/cli.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
// packages/cli/src/cli.ts
|
|
33
33
|
var cli_exports = {};
|
|
34
34
|
__export(cli_exports, {
|
|
35
|
+
KNOWN_SUBCOMMANDS: () => KNOWN_SUBCOMMANDS,
|
|
35
36
|
createCLI: () => createCLI
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(cli_exports);
|
|
@@ -49,7 +50,7 @@ function timestamp() {
|
|
|
49
50
|
}
|
|
50
51
|
__name(timestamp, "timestamp");
|
|
51
52
|
function info(msg) {
|
|
52
|
-
console.
|
|
53
|
+
console.error(`${timestamp()} ${import_picocolors.default.cyan("\u2139")} ${msg}`);
|
|
53
54
|
}
|
|
54
55
|
__name(info, "info");
|
|
55
56
|
function success(msg) {
|
|
@@ -57,7 +58,7 @@ function success(msg) {
|
|
|
57
58
|
}
|
|
58
59
|
__name(success, "success");
|
|
59
60
|
function warn(msg) {
|
|
60
|
-
console.
|
|
61
|
+
console.error(`${timestamp()} ${import_picocolors.default.yellow("\u26A0")} ${msg}`);
|
|
61
62
|
}
|
|
62
63
|
__name(warn, "warn");
|
|
63
64
|
function error(msg) {
|
|
@@ -161,6 +162,23 @@ function addPackage(targetDir, pm, pkg, dev) {
|
|
|
161
162
|
}
|
|
162
163
|
__name(addPackage, "addPackage");
|
|
163
164
|
|
|
165
|
+
// packages/cli/src/version.ts
|
|
166
|
+
var import_node_fs3 = require("fs");
|
|
167
|
+
var cached;
|
|
168
|
+
function cliVersion() {
|
|
169
|
+
if (cached !== void 0) return cached;
|
|
170
|
+
try {
|
|
171
|
+
const manifest = JSON.parse(
|
|
172
|
+
(0, import_node_fs3.readFileSync)(new URL("../package.json", __VOBS_CJS_FILE_URL), "utf8")
|
|
173
|
+
);
|
|
174
|
+
cached = manifest.version ?? "0.0.0";
|
|
175
|
+
} catch {
|
|
176
|
+
cached = "0.0.0";
|
|
177
|
+
}
|
|
178
|
+
return cached;
|
|
179
|
+
}
|
|
180
|
+
__name(cliVersion, "cliVersion");
|
|
181
|
+
|
|
164
182
|
// packages/cli/src/commands/init.ts
|
|
165
183
|
async function initCommand(options) {
|
|
166
184
|
const projectName = options.name || await p.text({
|
|
@@ -206,7 +224,7 @@ async function initCommand(options) {
|
|
|
206
224
|
name: projectName,
|
|
207
225
|
packageName: projectName.toLowerCase().replace(/\s+/g, "-"),
|
|
208
226
|
typescript: useTypescript,
|
|
209
|
-
vobsVersion:
|
|
227
|
+
vobsVersion: cliVersion()
|
|
210
228
|
});
|
|
211
229
|
logger.success("Project scaffolded");
|
|
212
230
|
logger.step("Installing dependencies...");
|
|
@@ -358,7 +376,7 @@ async function addCommand(options) {
|
|
|
358
376
|
__name(addCommand, "addCommand");
|
|
359
377
|
|
|
360
378
|
// packages/cli/src/commands/dsh.ts
|
|
361
|
-
var
|
|
379
|
+
var import_node_fs4 = require("fs");
|
|
362
380
|
var import_promises = require("fs/promises");
|
|
363
381
|
var import_node_child_process3 = require("child_process");
|
|
364
382
|
var import_node_http = require("http");
|
|
@@ -369,7 +387,7 @@ var PLATFORM_MODULES = ["react", "react-dom"];
|
|
|
369
387
|
var vobsVersion = /* @__PURE__ */ __name(() => {
|
|
370
388
|
try {
|
|
371
389
|
const manifest = JSON.parse(
|
|
372
|
-
(0,
|
|
390
|
+
(0, import_node_fs4.readFileSync)(new URL("../../package.json", __VOBS_CJS_FILE_URL), "utf8")
|
|
373
391
|
);
|
|
374
392
|
return manifest.version ?? "1.7.5";
|
|
375
393
|
} catch {
|
|
@@ -411,7 +429,7 @@ async function dshInitCommand(target, options) {
|
|
|
411
429
|
"dsh-plugin"
|
|
412
430
|
);
|
|
413
431
|
ensureDir(targetDir);
|
|
414
|
-
(0,
|
|
432
|
+
(0, import_node_fs4.writeFileSync)(
|
|
415
433
|
(0, import_node_path5.resolve)(targetDir, ".gitignore"),
|
|
416
434
|
["node_modules/", "*.log", "*.tsbuildinfo", "", "# lib/ \u662F\u6784\u5EFA\u4EA7\u7269\uFF0C\u5FC5\u987B\u63D0\u4EA4\uFF1ADSH \u5B89\u88C5\u65F6\u4E0D\u4F1A\u6784\u5EFA\u4F60\u7684\u5305\u3002", ""].join("\n"),
|
|
417
435
|
"utf8"
|
|
@@ -438,7 +456,7 @@ __name(dshInitCommand, "dshInitCommand");
|
|
|
438
456
|
async function dshBuildCommand() {
|
|
439
457
|
const projectDir = process.cwd();
|
|
440
458
|
const configFile = (0, import_node_path5.resolve)(projectDir, "vite.config.ts");
|
|
441
|
-
if (!(0,
|
|
459
|
+
if (!(0, import_node_fs4.existsSync)(configFile)) {
|
|
442
460
|
logger.error("\u5F53\u524D\u76EE\u5F55\u6CA1\u6709 vite.config.ts\uFF0C\u8BF7\u5728\u63D2\u4EF6\u9879\u76EE\u6839\u76EE\u5F55\u8FD0\u884C\u3002");
|
|
443
461
|
process.exitCode = 1;
|
|
444
462
|
return;
|
|
@@ -461,7 +479,7 @@ async function dshBuildCommand() {
|
|
|
461
479
|
logger.info("\u6784\u5EFA Host \u4EA7\u7269 \u2026");
|
|
462
480
|
const hostTarget = (0, import_node_path5.resolve)(projectDir, hostEntry.replace(/^\.\//u, ""));
|
|
463
481
|
const hostEntrySource = (0, import_node_path5.resolve)(projectDir, "src", "host", "index.ts");
|
|
464
|
-
if (!(0,
|
|
482
|
+
if (!(0, import_node_fs4.existsSync)(hostEntrySource)) {
|
|
465
483
|
logger.warn(`\u6CA1\u6709\u627E\u5230 ${hostEntrySource}\uFF0C\u8DF3\u8FC7 Host \u6784\u5EFA\u3002`);
|
|
466
484
|
return;
|
|
467
485
|
}
|
|
@@ -574,7 +592,7 @@ async function dshDevCommand(options) {
|
|
|
574
592
|
packageRoot = (0, import_node_path5.dirname)((0, import_node_path5.dirname)(projectRequire.resolve("@vobs/dsh")));
|
|
575
593
|
}
|
|
576
594
|
const candidate = (0, import_node_path5.join)(packageRoot, "dist", "preview.js");
|
|
577
|
-
previewFile = (0,
|
|
595
|
+
previewFile = (0, import_node_fs4.existsSync)(candidate) ? candidate : void 0;
|
|
578
596
|
} catch {
|
|
579
597
|
previewFile = void 0;
|
|
580
598
|
}
|
|
@@ -646,7 +664,7 @@ data: 1
|
|
|
646
664
|
return;
|
|
647
665
|
}
|
|
648
666
|
if (url === "/client.js") {
|
|
649
|
-
if (!(0,
|
|
667
|
+
if (!(0, import_node_fs4.existsSync)(clientFile)) {
|
|
650
668
|
response.writeHead(503, { "content-type": "text/plain; charset=utf-8" });
|
|
651
669
|
response.end(`\u8FD8\u6CA1\u6709\u4EA7\u7269\uFF1A${clientEntry}
|
|
652
670
|
${buildError ?? ""}`);
|
|
@@ -667,7 +685,7 @@ ${buildError ?? ""}`);
|
|
|
667
685
|
__name(dshDevCommand, "dshDevCommand");
|
|
668
686
|
function readManifest(dir) {
|
|
669
687
|
try {
|
|
670
|
-
return JSON.parse((0,
|
|
688
|
+
return JSON.parse((0, import_node_fs4.readFileSync)((0, import_node_path5.resolve)(dir, "package.json"), "utf8"));
|
|
671
689
|
} catch {
|
|
672
690
|
return void 0;
|
|
673
691
|
}
|
|
@@ -690,10 +708,10 @@ function collectIssues(dir) {
|
|
|
690
708
|
result.errors.push("\u7F3A\u5C11 dsh.bundle.patch \u2014\u2014 DSH \u4E0D\u4F1A\u628A\u8FD9\u4E2A\u5305\u5F53\u4F5C\u7EC4\u5408\u5305\uFF08bundle\uFF09");
|
|
691
709
|
} else {
|
|
692
710
|
const patchPath = (0, import_node_path5.resolve)(dir, bundle.patch.replace(/^\.\//u, ""));
|
|
693
|
-
if (!(0,
|
|
711
|
+
if (!(0, import_node_fs4.existsSync)(patchPath)) {
|
|
694
712
|
result.errors.push(`dsh.bundle.patch \u6307\u5411\u7684 ${bundle.patch} \u4E0D\u5B58\u5728`);
|
|
695
713
|
} else {
|
|
696
|
-
const text5 = (0,
|
|
714
|
+
const text5 = (0, import_node_fs4.readFileSync)(patchPath, "utf8");
|
|
697
715
|
if (!text5.includes("insert:")) result.errors.push("cordis.patch.yml \u91CC\u6CA1\u6709 insert: \u6BB5\u843D\uFF0C\u63D2\u4EF6\u884C\u4E0D\u4F1A\u8FDB\u7EC4\u5408\u6811");
|
|
698
716
|
else if (name !== "" && !text5.includes(name)) {
|
|
699
717
|
result.warnings.push(`cordis.patch.yml \u91CC\u6CA1\u6709\u51FA\u73B0\u5305\u540D ${name}\uFF0C\u786E\u8BA4 insert \u7684 name \u5199\u5BF9\u4E86`);
|
|
@@ -707,7 +725,7 @@ function collectIssues(dir) {
|
|
|
707
725
|
const hostEntry = exportsField["."];
|
|
708
726
|
const clientEntry = exportsField["./client"];
|
|
709
727
|
if (typeof hostEntry !== "string") result.errors.push('exports["."] \u672A\u58F0\u660E \u2014\u2014 DSH \u52A0\u8F7D\u4E0D\u5230 Host \u4FA7\u63D2\u4EF6');
|
|
710
|
-
else if (!(0,
|
|
728
|
+
else if (!(0, import_node_fs4.existsSync)((0, import_node_path5.resolve)(dir, hostEntry.replace(/^\.\//u, "")))) {
|
|
711
729
|
result.errors.push(`exports["."] \u6307\u5411\u7684 ${hostEntry} \u4E0D\u5B58\u5728\uFF0C\u5148\u6784\u5EFA`);
|
|
712
730
|
} else result.passed.push("Host \u5165\u53E3\u5B58\u5728");
|
|
713
731
|
if (typeof clientEntry !== "string") result.errors.push('exports["./client"] \u672A\u58F0\u660E \u2014\u2014 \u9875\u9762\u62FF\u4E0D\u5230\u5BA2\u6237\u7AEF bundle');
|
|
@@ -725,10 +743,10 @@ function collectIssues(dir) {
|
|
|
725
743
|
} else result.passed.push("\u6CA1\u6709 workspace: \u534F\u8BAE\u4F9D\u8D56");
|
|
726
744
|
if (typeof clientEntry === "string") {
|
|
727
745
|
const clientPath = (0, import_node_path5.resolve)(dir, clientEntry.replace(/^\.\//u, ""));
|
|
728
|
-
if (!(0,
|
|
746
|
+
if (!(0, import_node_fs4.existsSync)(clientPath)) {
|
|
729
747
|
result.errors.push(`\u5BA2\u6237\u7AEF\u4EA7\u7269 ${clientEntry} \u4E0D\u5B58\u5728\uFF0C\u5148\u8FD0\u884C\u6784\u5EFA`);
|
|
730
748
|
} else {
|
|
731
|
-
const code = (0,
|
|
749
|
+
const code = (0, import_node_fs4.readFileSync)(clientPath, "utf8");
|
|
732
750
|
const loads = (code.match(/__ModuleLoader__\.load\(/gu) ?? []).length;
|
|
733
751
|
if (loads !== 1) result.errors.push(`\u5BA2\u6237\u7AEF\u4EA7\u7269\u91CC __ModuleLoader__.load \u51FA\u73B0 ${loads} \u6B21\uFF0C\u5E94\u4E3A 1 \u6B21\uFF08\u5148\u6784\u5EFA\uFF09`);
|
|
734
752
|
else result.passed.push("\u4EA7\u7269\u5F62\u6001\u662F DSH \u5BA2\u6237\u7AEF\u6A21\u5757\u534F\u8BAE");
|
|
@@ -779,7 +797,7 @@ function buildInstallSpec(options) {
|
|
|
779
797
|
if (options.spec !== void 0) return { spec: options.spec };
|
|
780
798
|
if (options.from !== void 0) {
|
|
781
799
|
const target = (0, import_node_path5.resolve)(options.from);
|
|
782
|
-
if (!(0,
|
|
800
|
+
if (!(0, import_node_fs4.existsSync)(target)) return { error: `--from \u6307\u5411\u7684\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF1A${target}` };
|
|
783
801
|
return { spec: target };
|
|
784
802
|
}
|
|
785
803
|
if (options.repo !== void 0) {
|
|
@@ -886,8 +904,309 @@ async function dshCommand(action, target, options) {
|
|
|
886
904
|
}
|
|
887
905
|
__name(dshCommand, "dshCommand");
|
|
888
906
|
|
|
907
|
+
// packages/cli/src/commands/check.ts
|
|
908
|
+
var import_promises2 = require("fs/promises");
|
|
909
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
910
|
+
var import_typescript = __toESM(require("typescript"), 1);
|
|
911
|
+
var REPORT_PATH = ".vobs/check.json";
|
|
912
|
+
var TEST_FILE = /(?:^|\/)(?:[^/]*\.(?:test|spec)\.tsx?|__tests__\/)/u;
|
|
913
|
+
var VOBS_C210 = "VOBS_C210";
|
|
914
|
+
var VOBS_C232 = "VOBS_C232";
|
|
915
|
+
var VOBS_C118 = "VOBS_C118";
|
|
916
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "lib", "build", ".git", ".vobs", "coverage"]);
|
|
917
|
+
async function collectSources(root) {
|
|
918
|
+
let rootStat;
|
|
919
|
+
try {
|
|
920
|
+
rootStat = await (0, import_promises2.stat)(root);
|
|
921
|
+
} catch {
|
|
922
|
+
throw new Error(`\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
923
|
+
}
|
|
924
|
+
if (!rootStat.isDirectory()) {
|
|
925
|
+
throw new Error(`\u4E0D\u662F\u76EE\u5F55\uFF1A${root}\uFF08vobs check \u63A5\u53D7\u4E00\u4E2A\u76EE\u5F55\uFF09`);
|
|
926
|
+
}
|
|
927
|
+
const found = [];
|
|
928
|
+
const walk = /* @__PURE__ */ __name(async (dir) => {
|
|
929
|
+
let entries;
|
|
930
|
+
try {
|
|
931
|
+
entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
|
|
932
|
+
} catch (error2) {
|
|
933
|
+
if (dir === root) {
|
|
934
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\uFF1A${root}\uFF08${error2 instanceof Error ? error2.message : String(error2)}\uFF09`);
|
|
935
|
+
}
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
for (const entry of entries) {
|
|
939
|
+
if (entry.isDirectory()) {
|
|
940
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
941
|
+
await walk(import_node_path6.default.join(dir, entry.name));
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
if (!entry.isFile()) continue;
|
|
945
|
+
if (!/\.tsx?$/u.test(entry.name) || entry.name.endsWith(".d.ts")) continue;
|
|
946
|
+
found.push(import_node_path6.default.join(dir, entry.name));
|
|
947
|
+
}
|
|
948
|
+
}, "walk");
|
|
949
|
+
await walk(root);
|
|
950
|
+
return found.sort();
|
|
951
|
+
}
|
|
952
|
+
__name(collectSources, "collectSources");
|
|
953
|
+
function returnsJsx(node) {
|
|
954
|
+
let seen = false;
|
|
955
|
+
const visit = /* @__PURE__ */ __name((child) => {
|
|
956
|
+
if (seen) return;
|
|
957
|
+
if (import_typescript.default.isJsxElement(child) || import_typescript.default.isJsxSelfClosingElement(child) || import_typescript.default.isJsxFragment(child)) {
|
|
958
|
+
seen = true;
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
if (child !== node && import_typescript.default.isFunctionLike(child)) return;
|
|
962
|
+
import_typescript.default.forEachChild(child, visit);
|
|
963
|
+
}, "visit");
|
|
964
|
+
import_typescript.default.forEachChild(node, visit);
|
|
965
|
+
return seen;
|
|
966
|
+
}
|
|
967
|
+
__name(returnsJsx, "returnsJsx");
|
|
968
|
+
function signalNameOf(node) {
|
|
969
|
+
if (!import_typescript.default.isPropertyAccessExpression(node)) return void 0;
|
|
970
|
+
if (node.name.text !== "value") return void 0;
|
|
971
|
+
return node.expression.getText();
|
|
972
|
+
}
|
|
973
|
+
__name(signalNameOf, "signalNameOf");
|
|
974
|
+
function lineSnippet(source, node) {
|
|
975
|
+
const { line } = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
976
|
+
return (source.text.split(/\r?\n/u)[line] ?? "").trim();
|
|
977
|
+
}
|
|
978
|
+
__name(lineSnippet, "lineSnippet");
|
|
979
|
+
function diagnosticAt(source, file, node, rest) {
|
|
980
|
+
const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
981
|
+
return {
|
|
982
|
+
...rest,
|
|
983
|
+
file,
|
|
984
|
+
line: line + 1,
|
|
985
|
+
column: character + 1,
|
|
986
|
+
snippet: lineSnippet(source, node)
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
__name(diagnosticAt, "diagnosticAt");
|
|
990
|
+
var isWriteOperator = /* @__PURE__ */ __name((kind) => kind === import_typescript.default.SyntaxKind.EqualsToken || kind === import_typescript.default.SyntaxKind.PlusEqualsToken || kind === import_typescript.default.SyntaxKind.MinusEqualsToken || kind === import_typescript.default.SyntaxKind.AsteriskEqualsToken || kind === import_typescript.default.SyntaxKind.SlashEqualsToken, "isWriteOperator");
|
|
991
|
+
function ruleEffectSelfSubscription(source, file) {
|
|
992
|
+
const found = [];
|
|
993
|
+
const inspectEffectBody = /* @__PURE__ */ __name((body) => {
|
|
994
|
+
const reads = /* @__PURE__ */ new Map();
|
|
995
|
+
const writes = /* @__PURE__ */ new Map();
|
|
996
|
+
const visit2 = /* @__PURE__ */ __name((node, insideUntrack) => {
|
|
997
|
+
if (node !== body && import_typescript.default.isFunctionLike(node)) return;
|
|
998
|
+
const nextUntracked = insideUntrack || import_typescript.default.isCallExpression(node) && import_typescript.default.isIdentifier(node.expression) && node.expression.text === "untrack";
|
|
999
|
+
if (import_typescript.default.isBinaryExpression(node) && isWriteOperator(node.operatorToken.kind)) {
|
|
1000
|
+
const name = signalNameOf(node.left);
|
|
1001
|
+
if (name !== void 0 && !nextUntracked) writes.set(name, node);
|
|
1002
|
+
} else if ((import_typescript.default.isPrefixUnaryExpression(node) || import_typescript.default.isPostfixUnaryExpression(node)) && (node.operator === import_typescript.default.SyntaxKind.PlusPlusToken || node.operator === import_typescript.default.SyntaxKind.MinusMinusToken)) {
|
|
1003
|
+
const name = signalNameOf(node.operand);
|
|
1004
|
+
if (name !== void 0) {
|
|
1005
|
+
if (!nextUntracked) writes.set(name, node);
|
|
1006
|
+
reads.set(name, node);
|
|
1007
|
+
}
|
|
1008
|
+
} else if (import_typescript.default.isCallExpression(node) && import_typescript.default.isPropertyAccessExpression(node.expression) && node.expression.name.text === "set") {
|
|
1009
|
+
if (!nextUntracked) writes.set(node.expression.expression.getText(), node);
|
|
1010
|
+
} else {
|
|
1011
|
+
const name = signalNameOf(node);
|
|
1012
|
+
if (name !== void 0) {
|
|
1013
|
+
const parent = node.parent;
|
|
1014
|
+
const isAssignmentTarget = import_typescript.default.isBinaryExpression(parent) && isWriteOperator(parent.operatorToken.kind) && parent.left === node;
|
|
1015
|
+
if (!isAssignmentTarget) reads.set(name, node);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
import_typescript.default.forEachChild(node, (child) => visit2(child, nextUntracked));
|
|
1019
|
+
}, "visit");
|
|
1020
|
+
visit2(body, false);
|
|
1021
|
+
for (const [name, writeNode] of writes) {
|
|
1022
|
+
if (!reads.has(name)) continue;
|
|
1023
|
+
found.push(diagnosticAt(source, file, writeNode, {
|
|
1024
|
+
code: VOBS_C210,
|
|
1025
|
+
severity: "error",
|
|
1026
|
+
message: `effect \u5199\u5165\u4E86\u5B83\u81EA\u5DF1\u4F9D\u8D56\u7684\u4FE1\u53F7 "${name}" \u2014\u2014 \u8FD9\u6B21\u5199\u5165\u4F1A\u628A\u5B83\u91CD\u65B0\u8C03\u5EA6\uFF0C\u5F62\u6210\u81EA\u8BA2\u9605\u5FAA\u73AF`,
|
|
1027
|
+
fix: `\u628A\u8FD9\u6B21\u5199\u5165\u5305\u8FDB untrack\uFF1Auntrack(() => { ${name}.value = next })\uFF1B\u5982\u679C\u8FD9\u4E2A effect \u672C\u6765\u5C31\u53EA\u8BE5\u505A\u526F\u4F5C\u7528\uFF0C\u68C0\u67E5\u662F\u4E0D\u662F\u8BEF\u8BFB\u4E86\u4E0D\u8BE5\u8BFB\u7684\u4FE1\u53F7\u3002`
|
|
1028
|
+
}));
|
|
1029
|
+
}
|
|
1030
|
+
}, "inspectEffectBody");
|
|
1031
|
+
const visit = /* @__PURE__ */ __name((node) => {
|
|
1032
|
+
if (import_typescript.default.isCallExpression(node) && import_typescript.default.isIdentifier(node.expression) && (node.expression.text === "effect" || node.expression.text === "renderEffect")) {
|
|
1033
|
+
const callback = node.arguments[0];
|
|
1034
|
+
if (callback !== void 0 && (import_typescript.default.isArrowFunction(callback) || import_typescript.default.isFunctionExpression(callback))) {
|
|
1035
|
+
inspectEffectBody(callback.body);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1039
|
+
}, "visit");
|
|
1040
|
+
visit(source);
|
|
1041
|
+
return found;
|
|
1042
|
+
}
|
|
1043
|
+
__name(ruleEffectSelfSubscription, "ruleEffectSelfSubscription");
|
|
1044
|
+
function isKeyedListCall(node) {
|
|
1045
|
+
if (!import_typescript.default.isCallExpression(node)) return false;
|
|
1046
|
+
if (!import_typescript.default.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "map") return false;
|
|
1047
|
+
const callback = node.arguments[0];
|
|
1048
|
+
return callback !== void 0 && import_typescript.default.isFunctionLike(callback) && returnsJsx(callback);
|
|
1049
|
+
}
|
|
1050
|
+
__name(isKeyedListCall, "isKeyedListCall");
|
|
1051
|
+
function ruleListInBranch(source, file) {
|
|
1052
|
+
const found = [];
|
|
1053
|
+
const message = "list \u5199\u5728\u4E09\u5143/&& \u7684\u5206\u652F\u91CC \u2014\u2014 \u4F1A\u8D70\u591A\u6001\u63D2\u5165\uFF0C\u5931\u53BB keyed \u590D\u7528";
|
|
1054
|
+
const fix = "\u628A list \u63D0\u6210**\u76F4\u63A5\u7684**\u5B50\u8868\u8FBE\u5F0F\uFF1A\u5148\u5199\u6761\u4EF6\u5206\u652F\uFF0C\u518D\u5355\u72EC\u5199 {items.map(...)}\u3002\u4E09\u5143\u91CC\u540C\u65F6\u6709\u8282\u70B9\u4E0E list \u65F6\uFF0Clist \u90A3\u4E00\u652F\u4E0D\u4F1A\u7F16\u8BD1\u6210 insertList\u3002";
|
|
1055
|
+
const visit = /* @__PURE__ */ __name((node) => {
|
|
1056
|
+
if (import_typescript.default.isConditionalExpression(node)) {
|
|
1057
|
+
for (const branch of [node.whenTrue, node.whenFalse]) {
|
|
1058
|
+
if (isKeyedListCall(branch)) {
|
|
1059
|
+
found.push(diagnosticAt(source, file, branch, { code: VOBS_C232, severity: "warning", message, fix }));
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
} else if (import_typescript.default.isBinaryExpression(node) && node.operatorToken.kind === import_typescript.default.SyntaxKind.AmpersandAmpersandToken && isKeyedListCall(node.right)) {
|
|
1063
|
+
found.push(diagnosticAt(source, file, node.right, { code: VOBS_C232, severity: "warning", message, fix }));
|
|
1064
|
+
}
|
|
1065
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1066
|
+
}, "visit");
|
|
1067
|
+
visit(source);
|
|
1068
|
+
return found;
|
|
1069
|
+
}
|
|
1070
|
+
__name(ruleListInBranch, "ruleListInBranch");
|
|
1071
|
+
function containsSignalRead(node) {
|
|
1072
|
+
let seen = false;
|
|
1073
|
+
const visit = /* @__PURE__ */ __name((child) => {
|
|
1074
|
+
if (seen) return;
|
|
1075
|
+
if (import_typescript.default.isFunctionLike(child)) return;
|
|
1076
|
+
if (signalNameOf(child) !== void 0) {
|
|
1077
|
+
seen = true;
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
import_typescript.default.forEachChild(child, visit);
|
|
1081
|
+
}, "visit");
|
|
1082
|
+
visit(node);
|
|
1083
|
+
return seen;
|
|
1084
|
+
}
|
|
1085
|
+
__name(containsSignalRead, "containsSignalRead");
|
|
1086
|
+
function ruleSignalCapturedInBody(source, file) {
|
|
1087
|
+
const found = [];
|
|
1088
|
+
const inspectComponent = /* @__PURE__ */ __name((fn) => {
|
|
1089
|
+
const body = fn.body;
|
|
1090
|
+
if (body === void 0 || !import_typescript.default.isBlock(body) || !returnsJsx(fn)) return;
|
|
1091
|
+
const captured = [];
|
|
1092
|
+
for (const statement of body.statements) {
|
|
1093
|
+
if (!import_typescript.default.isVariableStatement(statement)) continue;
|
|
1094
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1095
|
+
if (!import_typescript.default.isIdentifier(declaration.name) || declaration.initializer === void 0) continue;
|
|
1096
|
+
if (containsSignalRead(declaration.initializer)) {
|
|
1097
|
+
captured.push({ name: declaration.name.text, node: declaration });
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
if (captured.length === 0) return;
|
|
1102
|
+
const usedInJsx = /* @__PURE__ */ new Set();
|
|
1103
|
+
const visit2 = /* @__PURE__ */ __name((node) => {
|
|
1104
|
+
if (import_typescript.default.isJsxExpression(node) && node.expression !== void 0) {
|
|
1105
|
+
const scan = /* @__PURE__ */ __name((child) => {
|
|
1106
|
+
if (import_typescript.default.isIdentifier(child)) usedInJsx.add(child.text);
|
|
1107
|
+
import_typescript.default.forEachChild(child, scan);
|
|
1108
|
+
}, "scan");
|
|
1109
|
+
scan(node.expression);
|
|
1110
|
+
}
|
|
1111
|
+
import_typescript.default.forEachChild(node, visit2);
|
|
1112
|
+
}, "visit");
|
|
1113
|
+
visit2(body);
|
|
1114
|
+
for (const item of captured) {
|
|
1115
|
+
if (!usedInJsx.has(item.name)) continue;
|
|
1116
|
+
found.push(diagnosticAt(source, file, item.node, {
|
|
1117
|
+
code: VOBS_C118,
|
|
1118
|
+
severity: "warning",
|
|
1119
|
+
message: `\u7EC4\u4EF6\u4F53\u91CC\u8BFB\u4E86\u4FE1\u53F7\u5E76\u5B58\u8FDB "${item.name}" \u2014\u2014 \u7EC4\u4EF6\u4F53\u53EA\u6267\u884C\u4E00\u6B21\uFF0C\u8FD9\u4E2A\u503C\u4E4B\u540E\u6C38\u8FDC\u4E0D\u4F1A\u66F4\u65B0`,
|
|
1120
|
+
fix: `\u628A\u8BFB\u53D6\u653E\u8FDB\u52A8\u6001\u8868\u8FBE\u5F0F\uFF08\u76F4\u63A5\u5728 JSX \u91CC\u7528 .value\uFF09\uFF0C\u6216\u6539\u6210 memo \u6D3E\u751F\u503C\uFF1Aconst ${item.name} = memo(() => ...)\u3002`
|
|
1121
|
+
}));
|
|
1122
|
+
}
|
|
1123
|
+
}, "inspectComponent");
|
|
1124
|
+
const visit = /* @__PURE__ */ __name((node) => {
|
|
1125
|
+
if (import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isFunctionExpression(node) || import_typescript.default.isArrowFunction(node)) {
|
|
1126
|
+
inspectComponent(node);
|
|
1127
|
+
}
|
|
1128
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1129
|
+
}, "visit");
|
|
1130
|
+
visit(source);
|
|
1131
|
+
return found;
|
|
1132
|
+
}
|
|
1133
|
+
__name(ruleSignalCapturedInBody, "ruleSignalCapturedInBody");
|
|
1134
|
+
var IGNORE_NEXT_LINE = /vobs-check-ignore-next-line/u;
|
|
1135
|
+
function suppressedLines(text5) {
|
|
1136
|
+
const suppressed = /* @__PURE__ */ new Set();
|
|
1137
|
+
const lines = text5.split(/\r?\n/u);
|
|
1138
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1139
|
+
if (IGNORE_NEXT_LINE.test(lines[index])) suppressed.add(index + 2);
|
|
1140
|
+
}
|
|
1141
|
+
return suppressed;
|
|
1142
|
+
}
|
|
1143
|
+
__name(suppressedLines, "suppressedLines");
|
|
1144
|
+
function analyzeSource(text5, file) {
|
|
1145
|
+
const source = import_typescript.default.createSourceFile(file, text5, import_typescript.default.ScriptTarget.Latest, true, import_typescript.default.ScriptKind.TSX);
|
|
1146
|
+
const suppressed = suppressedLines(text5);
|
|
1147
|
+
return [
|
|
1148
|
+
...ruleEffectSelfSubscription(source, file),
|
|
1149
|
+
...ruleListInBranch(source, file),
|
|
1150
|
+
...ruleSignalCapturedInBody(source, file)
|
|
1151
|
+
].filter((item) => !suppressed.has(item.line)).sort((a, b) => a.line - b.line || a.column - b.column);
|
|
1152
|
+
}
|
|
1153
|
+
__name(analyzeSource, "analyzeSource");
|
|
1154
|
+
async function checkCommand(options = {}) {
|
|
1155
|
+
const root = import_node_path6.default.resolve(options.dir ?? process.cwd());
|
|
1156
|
+
let all;
|
|
1157
|
+
try {
|
|
1158
|
+
all = await collectSources(root);
|
|
1159
|
+
} catch (error2) {
|
|
1160
|
+
logger.error(error2 instanceof Error ? error2.message : String(error2));
|
|
1161
|
+
process.exitCode = 1;
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const files = options.includeTests === true ? all : all.filter((file) => !TEST_FILE.test(import_node_path6.default.relative(root, file).split(import_node_path6.default.sep).join("/")));
|
|
1165
|
+
const skipped = all.length - files.length;
|
|
1166
|
+
const diagnostics = [];
|
|
1167
|
+
for (const absolute of files) {
|
|
1168
|
+
const text5 = await (0, import_promises2.readFile)(absolute, "utf8");
|
|
1169
|
+
const relative2 = import_node_path6.default.relative(root, absolute).split(import_node_path6.default.sep).join("/");
|
|
1170
|
+
diagnostics.push(...analyzeSource(text5, relative2));
|
|
1171
|
+
}
|
|
1172
|
+
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
1173
|
+
const report = { root, files: files.length, skippedTests: skipped, diagnostics };
|
|
1174
|
+
if (options.write === true) {
|
|
1175
|
+
const target = import_node_path6.default.join(root, REPORT_PATH);
|
|
1176
|
+
await (0, import_promises2.mkdir)(import_node_path6.default.dirname(target), { recursive: true });
|
|
1177
|
+
await (0, import_promises2.writeFile)(target, `${JSON.stringify(report, null, 2)}
|
|
1178
|
+
`, "utf8");
|
|
1179
|
+
logger.info(`\u62A5\u544A\u5199\u5165 ${REPORT_PATH}\uFF08\u5F00\u53D1\u53F0\u9762\u677F\u8BFB\u5B83\u663E\u793A\u300C\u9879\u76EE\u300D\u9875\uFF09`);
|
|
1180
|
+
}
|
|
1181
|
+
if (options.json === true) {
|
|
1182
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1183
|
+
if (errors.length > 0) process.exitCode = 1;
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (diagnostics.length === 0) {
|
|
1187
|
+
logger.success(`\u68C0\u67E5\u901A\u8FC7 \u2014\u2014 ${files.length} \u4E2A\u6587\u4EF6${skipped > 0 ? `\uFF08\u8DF3\u8FC7 ${skipped} \u4E2A\u6D4B\u8BD5\u6587\u4EF6\uFF09` : ""}\uFF0C\u6CA1\u6709\u53D1\u73B0\u95EE\u9898`);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
console.log("");
|
|
1191
|
+
for (const item of diagnostics) {
|
|
1192
|
+
const mark = item.severity === "error" ? "\u2716" : "!";
|
|
1193
|
+
console.log(` ${mark} ${item.file}:${item.line}:${item.column} ${item.code}`);
|
|
1194
|
+
console.log(` ${item.message}`);
|
|
1195
|
+
if (item.snippet !== "") console.log(` ${item.snippet}`);
|
|
1196
|
+
console.log(` \u2192 ${item.fix}`);
|
|
1197
|
+
console.log("");
|
|
1198
|
+
}
|
|
1199
|
+
const warnings = diagnostics.length - errors.length;
|
|
1200
|
+
console.log(
|
|
1201
|
+
` ${errors.length} \u4E2A\u9519\u8BEF \xB7 ${warnings} \u4E2A\u8B66\u544A \xB7 \u5171\u68C0\u67E5 ${files.length} \u4E2A\u6587\u4EF6${skipped > 0 ? `\uFF08\u8DF3\u8FC7 ${skipped} \u4E2A\u6D4B\u8BD5\u6587\u4EF6\uFF0C\u7528 --include-tests \u7EB3\u5165\uFF09` : ""}
|
|
1202
|
+
`
|
|
1203
|
+
);
|
|
1204
|
+
if (errors.length > 0) process.exitCode = 1;
|
|
1205
|
+
}
|
|
1206
|
+
__name(checkCommand, "checkCommand");
|
|
1207
|
+
|
|
889
1208
|
// packages/cli/src/banner.ts
|
|
890
|
-
var BANNER_LINE1 = "\u{1D5E9}\u{1D5E2}\u{1D5D5}\u{1D5E6} \u{1D5D6}\u{1D5DF}\u{1D5DC}
|
|
1209
|
+
var BANNER_LINE1 = "\u{1D5E9}\u{1D5E2}\u{1D5D5}\u{1D5E6} \u{1D5D6}\u{1D5DF}\u{1D5DC}";
|
|
891
1210
|
var BANNER_TAGLINE = "Signals First \xB7 Zero Re-renders \xB7 Ultra Lightweight";
|
|
892
1211
|
var INDENT = " ";
|
|
893
1212
|
var DIM = "\x1B[2m";
|
|
@@ -903,12 +1222,12 @@ function showLogo() {
|
|
|
903
1222
|
}
|
|
904
1223
|
__name(showLogo, "showLogo");
|
|
905
1224
|
function showBanner() {
|
|
906
|
-
console.log(`${INDENT}${BANNER_LINE1}`);
|
|
1225
|
+
console.log(`${INDENT}${BANNER_LINE1} v${cliVersion()}`);
|
|
907
1226
|
}
|
|
908
1227
|
__name(showBanner, "showBanner");
|
|
909
1228
|
function showDevBanner() {
|
|
910
1229
|
console.log();
|
|
911
|
-
console.log(`${INDENT}${BANNER_LINE1}`);
|
|
1230
|
+
console.log(`${INDENT}${BANNER_LINE1} v${cliVersion()}`);
|
|
912
1231
|
console.log();
|
|
913
1232
|
}
|
|
914
1233
|
__name(showDevBanner, "showDevBanner");
|
|
@@ -962,6 +1281,14 @@ function createCLI() {
|
|
|
962
1281
|
dev: options.dev
|
|
963
1282
|
});
|
|
964
1283
|
});
|
|
1284
|
+
cli.command("check [dir]", "\u9759\u6001\u68C0\u67E5\u6E90\u7801\uFF1Aeffect \u81EA\u8BA2\u9605 / \u5217\u8868\u5199\u8FDB\u5206\u652F / \u7EC4\u4EF6\u4F53\u91CC\u8BFB\u4FE1\u53F7\uFF08\u884C\u5185\u6291\u5236\uFF1A\u4E0A\u4E00\u884C\u5199 // vobs-check-ignore-next-line\uFF09").option("--json", "\u4EE5 JSON \u8F93\u51FA\uFF08\u7ED9 AI \u4E0E\u5DE5\u5177\u6D88\u8D39\uFF09").option("--write", `\u628A\u7ED3\u679C\u5199\u5230 ${".vobs/check.json"}\uFF08\u5F00\u53D1\u53F0\u9762\u677F\u8BFB\u5B83\uFF09`).option("--include-tests", "\u628A\u6D4B\u8BD5\u6587\u4EF6\u4E5F\u7EB3\u5165\u68C0\u67E5\uFF08\u9ED8\u8BA4\u8DF3\u8FC7\uFF09").action(async (dir, options) => {
|
|
1285
|
+
await checkCommand({
|
|
1286
|
+
dir,
|
|
1287
|
+
json: options.json === true,
|
|
1288
|
+
write: options.write === true,
|
|
1289
|
+
includeTests: options.includeTests === true
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
965
1292
|
cli.command("dsh [action] [target]", "DSH \u63D2\u4EF6\u5DE5\u5177\u94FE\uFF08init / dev / build / check / install\uFF09").option("--dir <dir>", "init\uFF1A\u76EE\u6807\u76EE\u5F55").option("--pm <pm>", "init\uFF1A\u5305\u7BA1\u7406\u5668\uFF08pnpm/npm\uFF09").option("--port <port>", "dev\uFF1A\u9884\u89C8\u7AEF\u53E3", { default: 5199 }).option("--profile <name>", "install\uFF1ADSH profile \u540D", { default: "desktop" }).option("--spec <spec>", "install\uFF1A\u5B8C\u6574\u5B89\u88C5 spec").option("--repo <owner/name>", "install\uFF1AGitHub owner/name").option("--tag <ref>", "install\uFF1AGit tag / \u5206\u652F / commit").option("--subpath <path>", "install\uFF1A\u4ED3\u5E93\u5185\u5B50\u76EE\u5F55").option("--from <path>", "install\uFF1A\u4ECE\u672C\u5730\u76EE\u5F55\u5B89\u88C5").option("--dry", "install\uFF1A\u53EA\u6253\u5370\u547D\u4EE4\uFF0C\u4E0D\u6267\u884C").action(async (action, target, options) => {
|
|
966
1293
|
await dshCommand(action, target, {
|
|
967
1294
|
dir: options.dir,
|
|
@@ -983,12 +1310,25 @@ function createCLI() {
|
|
|
983
1310
|
showBanner();
|
|
984
1311
|
});
|
|
985
1312
|
cli.help();
|
|
986
|
-
cli.version(
|
|
1313
|
+
cli.version(cliVersion());
|
|
987
1314
|
return cli;
|
|
988
1315
|
}
|
|
989
1316
|
__name(createCLI, "createCLI");
|
|
1317
|
+
var KNOWN_SUBCOMMANDS = [
|
|
1318
|
+
"init",
|
|
1319
|
+
"create",
|
|
1320
|
+
"dev",
|
|
1321
|
+
"build",
|
|
1322
|
+
"generate",
|
|
1323
|
+
"g",
|
|
1324
|
+
"add",
|
|
1325
|
+
"check",
|
|
1326
|
+
"dsh",
|
|
1327
|
+
"logo"
|
|
1328
|
+
];
|
|
990
1329
|
// Annotate the CommonJS export names for ESM import in node:
|
|
991
1330
|
0 && (module.exports = {
|
|
1331
|
+
KNOWN_SUBCOMMANDS,
|
|
992
1332
|
createCLI
|
|
993
1333
|
});
|
|
994
1334
|
//# sourceMappingURL=cli.cjs.map
|