@compelem/compiler 0.30.0-beta.5 → 0.30.0-beta.6

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/index.d.ts CHANGED
@@ -113,6 +113,13 @@ interface StaticLiteralCodes {
113
113
  states?: string;
114
114
  /** `__ce_static__.computedGetters` 字面量值(@computed 前移,原 getter 体外提) */
115
115
  computedGetters?: string;
116
+ /**
117
+ * `__ce_static__.resources` 字面量值(@resource 前移)。
118
+ *
119
+ * 只放纯数据选项(timeout/immediate/track + source 的布尔标记);
120
+ * loader 与 source 函数搬成类上的 `__rl_<name>` / `__rs_<name>` 私有方法。
121
+ */
122
+ resources?: string;
116
123
  /** 类体访问器源码(prop/state/computed 的 get/set + `@tag` 类的 observedAttributes),
117
124
  * 接在 static 字面量之后注入。无 `@tag` 的类不带 observedAttributes —— 它们永不被
118
125
  * `customElements.define`,浏览器不会读它们的表。 */
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { C as collectCompelemImports, S as analyzeFile, T as parse, _ as DEBUG_DIV, a as scanKnownTags, b as inlineTemplateMethods, c as offsetToLineCol, d as reportDiagnostics, f as compileFile, g as injectInto, h as buildStaticLiteral, i as TAG_RE, l as relativeToRoot, m as defaultInclude, o as collectDiagnostics, p as compileSource, r as DEFAULT_TAG_EXT, s as newlineIndex, t as compelemCompiler, u as reportConventionDiagnostics, v as StaticAnalysisError, w as isCompelemSource, x as analyzeRender, y as assertStaticAnalysis } from "./vite-CLK9td6W.js";
1
+ import { C as collectCompelemImports, S as analyzeFile, T as parse, _ as DEBUG_DIV, a as scanKnownTags, b as inlineTemplateMethods, c as offsetToLineCol, d as reportDiagnostics, f as compileFile, g as injectInto, h as buildStaticLiteral, i as TAG_RE, l as relativeToRoot, m as defaultInclude, o as collectDiagnostics, p as compileSource, r as DEFAULT_TAG_EXT, s as newlineIndex, t as compelemCompiler, u as reportConventionDiagnostics, v as StaticAnalysisError, w as isCompelemSource, x as analyzeRender, y as assertStaticAnalysis } from "./vite-C95uwtJI.js";
2
2
 
3
3
  export { DEBUG_DIV, DEFAULT_TAG_EXT, StaticAnalysisError, TAG_RE, analyzeFile, analyzeRender, assertStaticAnalysis, buildStaticLiteral, collectCompelemImports, collectDiagnostics, compelemCompiler, compileFile, compileSource, defaultInclude, injectInto, inlineTemplateMethods, isCompelemSource, newlineIndex, offsetToLineCol, parse, relativeToRoot, reportConventionDiagnostics, reportDiagnostics, scanKnownTags };
@@ -494,12 +494,21 @@ function extractClassInfo(node, resolve) {
494
494
  const imported = resolve(d);
495
495
  return imported === "query" || imported === "queryAll";
496
496
  })) queryFields.add(name);
497
- if (member.type === "MethodDefinition") {
498
- if (member.kind === "get") getters.add(name);
499
- else methods.add(name);
497
+ const resDeco = (member.decorators ?? []).find((d) => {
498
+ const n = decoratorName(d);
499
+ return n ? resolve(n) === "resource" : false;
500
+ });
501
+ if (member.type === "MethodDefinition") if (member.kind === "get") {
502
+ getters.add(name);
503
+ const fn = member.value;
504
+ if (fn?.body) bodies.set(name, fn.body);
505
+ } else {
506
+ methods.add(name);
500
507
  const fn = member.value;
501
508
  if (fn?.body) bodies.set(name, fn.body);
502
- } else if (member.type === "PropertyDefinition") {
509
+ }
510
+ else if (member.type === "PropertyDefinition") if (resDeco) getters.add(name);
511
+ else {
503
512
  const init = member.value;
504
513
  if (init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")) {
505
514
  methods.add(name);
@@ -1804,6 +1813,7 @@ function buildStaticLiteral(opts, codes = {}) {
1804
1813
  parts.push(mark("props", `props: ${emitFamily("props", codes.props)}`));
1805
1814
  parts.push(mark("states", `states: ${emitFamily("states", codes.states)}`));
1806
1815
  parts.push(mark("computedGetters", `computedGetters: ${emitFamily("computedGetters", codes.computedGetters)}`));
1816
+ parts.push(mark("resources", `resources: ${emitFamily("resources", codes.resources)}`));
1807
1817
  const watchCode = emitWatchEffects(codes.watchEffects, codes.inlinedWatchAncestors);
1808
1818
  if (watchCode !== void 0) parts.push(mark("watchEffects", `watchEffects: ${watchCode}`));
1809
1819
  if (codes.cssEffect) parts.push(mark("cssEffect", `cssEffect: ${codes.cssEffect}`));
@@ -2595,8 +2605,8 @@ function checkConventions(comp, ctx) {
2595
2605
  function record(ctx, path) {
2596
2606
  addDepPath(ctx.deps, path);
2597
2607
  }
2598
- /** TS 包装节点解包(`this as any` / `that!` 等)—— 统一实现在 utils/ast#unwrapExpr。
2599
- * 本副本原先比别处多认 `TSInstantiationExpression`、少认 `TSTypeAssertion`,已归一。 */
2608
+ /** TS 包装节点解包(`this as any` / `that!` / `<T>x` 等)—— 统一实现在 utils/ast#unwrapExpr,
2609
+ * 认 TSInstantiationExpression 与 TSTypeAssertion。 */
2600
2610
  /** 置失败标记;DEPS_TRACE=1 时输出原因标签(见文件头)。 */
2601
2611
  function failAt(ctx, where) {
2602
2612
  if (typeof process !== "undefined" && process.env?.DEPS_TRACE) console.error("[deps-fail]", where);
@@ -4399,6 +4409,234 @@ function extractFieldAccessors(comp, code, ctx, bindings = {}, inheritedAccessor
4399
4409
  };
4400
4410
  }
4401
4411
 
4412
+ //#endregion
4413
+ //#region src/analyze/resource-extract.ts
4414
+ const CODE = "E-RESOURCE";
4415
+ /** 选项闭集,逐字对齐运行期 `ResourceDecoratorOptions` */
4416
+ const OPT_KEYS = new Set([
4417
+ "timeout",
4418
+ "immediate",
4419
+ "track",
4420
+ "source"
4421
+ ]);
4422
+ /** 合法标识符:资源名要拼进 `get <name>()`,非法名会生成坏代码 */
4423
+ const IDENT = /^[A-Za-z_$][\w$]*$/;
4424
+ /**
4425
+ * 不能用作资源名的成员:`CompElem` 原型上的访问器、方法与生命周期回调,
4426
+ * 外加 `Object.prototype` 的成员。
4427
+ *
4428
+ * 运行期按 `__ce_static__.resources` 在原型上装 `get <name>()`,安装前会检查
4429
+ * 原型链上是否已有同名键,有则跳过 —— 撞名的结果是资源**静默不可用**。
4430
+ * 编译器无法读到运行期原型(`CompElem` 继承 `HTMLElement`,Node 侧无法加载),
4431
+ * 这份名单与 `src/CompElem.ts` 的成员表同步维护。
4432
+ */
4433
+ const RESERVED_NAMES = new Set([
4434
+ "cid",
4435
+ "attrs",
4436
+ "props",
4437
+ "renderRoot",
4438
+ "renderRoots",
4439
+ "parentComponent",
4440
+ "wrapperComponent",
4441
+ "rootComponent",
4442
+ "slots",
4443
+ "slotHooks",
4444
+ "cssSheets",
4445
+ "cssVars",
4446
+ "isMounted",
4447
+ "isDestroyed",
4448
+ "insertStyleSheet",
4449
+ "setup",
4450
+ "propsReady",
4451
+ "render",
4452
+ "beforeMount",
4453
+ "mounted",
4454
+ "beforeDestroyed",
4455
+ "destroyed",
4456
+ "destroy",
4457
+ "slotChange",
4458
+ "afterUpdate",
4459
+ "updateProps",
4460
+ "getSubViewNodes",
4461
+ "emit",
4462
+ "nextTick",
4463
+ "defineSignalField",
4464
+ "forceUpdate",
4465
+ "connectedCallback",
4466
+ "disconnectedCallback",
4467
+ "attributeChangedCallback",
4468
+ "constructor",
4469
+ "toString",
4470
+ "toLocaleString",
4471
+ "valueOf",
4472
+ "hasOwnProperty",
4473
+ "isPrototypeOf",
4474
+ "propertyIsEnumerable",
4475
+ "__proto__"
4476
+ ]);
4477
+ /** 编译期注入的私有方法前缀 */
4478
+ const LOADER_PREFIX = "__rl_";
4479
+ const SOURCE_PREFIX = "__rs_";
4480
+ /** 迁移提示:给出可直接照抄的写法 */
4481
+ const MIGRATE = "\n 正确形态(loader 是第一个参数,选项可选):\n @resource((self, signal) => …, { timeout: 3000 })\n rows!: ResourceView<Row[]>\n 之后模板里写 this.rows.data / this.rows.busy / this.rows.load()。";
4482
+ function extractResources(comp, code, ctx) {
4483
+ const result = {
4484
+ accessors: "",
4485
+ removals: [],
4486
+ runtimeHelpers: [],
4487
+ errors: []
4488
+ };
4489
+ const cls = comp.cls ?? ctx.localClasses.get(comp.className);
4490
+ if (!cls) return result;
4491
+ const isResourceDeco = (d) => comp.compelemImports.get(d) === "resource";
4492
+ const members = cls.body?.body ?? [];
4493
+ const targets = [];
4494
+ for (const member of members) {
4495
+ const dec = (member.decorators ?? []).find((d) => {
4496
+ const n = decoratorName(d);
4497
+ return n ? isResourceDeco(n) : false;
4498
+ });
4499
+ if (!dec) continue;
4500
+ targets.push({
4501
+ member,
4502
+ dec,
4503
+ rawName: keyName(member.key) ?? ""
4504
+ });
4505
+ }
4506
+ if (!targets.length) return result;
4507
+ const fail = (member, dec, message) => {
4508
+ const dStart = dec?.start ?? member.start;
4509
+ result.errors.push({
4510
+ start: Math.min(member.start, dStart),
4511
+ end: member.end,
4512
+ message
4513
+ });
4514
+ };
4515
+ /** 本族已提交的资源名,用于同类内重复检查(TS 也会报,但这里给更直白的 E 码) */
4516
+ const emitted = /* @__PURE__ */ new Set();
4517
+ const entries = [];
4518
+ let accessors = "";
4519
+ const removals = [];
4520
+ for (const { member, dec, rawName } of targets) {
4521
+ if (member.type !== "PropertyDefinition") {
4522
+ fail(member, dec, `${CODE}: @resource 只能装饰字段声明。${MIGRATE}`);
4523
+ continue;
4524
+ }
4525
+ if (!rawName) {
4526
+ fail(member, dec, `${CODE}: 无法识别被装饰的字段名(计算属性键不支持)`);
4527
+ continue;
4528
+ }
4529
+ if (rawName.startsWith("#")) {
4530
+ fail(member, dec, `${CODE}: 不支持私有字段('${rawName}')——视图访问器要挂在原型上才能被模板读取`);
4531
+ continue;
4532
+ }
4533
+ if (!IDENT.test(rawName)) {
4534
+ fail(member, dec, `${CODE}: 资源名 '${rawName}' 不是合法标识符(要用作 get ${rawName}())`);
4535
+ continue;
4536
+ }
4537
+ if (RESERVED_NAMES.has(rawName)) {
4538
+ fail(member, dec, `${CODE}: 资源名 '${rawName}' 与 CompElem 原型链上的成员同名——访问器不会安装,this.${rawName} 取不到资源视图。请改用其它字段名`);
4539
+ continue;
4540
+ }
4541
+ if (member.static === true) {
4542
+ fail(member, dec, `${CODE}: 不支持静态资源('${rawName}')——注入的是实例 getter,static 成员会被整段删掉`);
4543
+ continue;
4544
+ }
4545
+ if (member.value != null) {
4546
+ fail(member, dec, `${CODE}: 字段不能带初始值('${rawName} = …')——值只由 loader 提供,写在字段上的初始值会被删掉,且不会报错`);
4547
+ continue;
4548
+ }
4549
+ if (emitted.has(rawName)) {
4550
+ fail(member, dec, `${CODE}: '${rawName}' 已经有另一个 @resource,资源名必须唯一`);
4551
+ continue;
4552
+ }
4553
+ const otherDecos = (member.decorators ?? []).map(decoratorName).filter((d) => d && !isResourceDeco(d));
4554
+ if (otherDecos.length) {
4555
+ fail(member, dec, `${CODE}: @resource 不能与其它装饰器同用('${rawName}' 上还有 @${otherDecos.join(" / @")})`);
4556
+ continue;
4557
+ }
4558
+ const callArgs = dec.expression?.type === "CallExpression" ? dec.expression.arguments ?? [] : [];
4559
+ if (callArgs.length < 1 || callArgs.length > 2) {
4560
+ fail(member, dec, `${CODE}: @resource 需要 (loader, options?) 两个参数以内${MIGRATE}`);
4561
+ continue;
4562
+ }
4563
+ const loaderNode = unwrapExpr(callArgs[0]);
4564
+ if (loaderNode?.type !== "ArrowFunctionExpression") {
4565
+ fail(member, dec, loaderNode?.type === "ObjectExpression" ? `${CODE}: 形态已改为 @resource(loader, options?)——loader 从选项对象移到第一个参数${MIGRATE}` : `${CODE}: 第一个参数须是内联箭头函数 (self, signal) => …——编译期要把这段表达式搬进实例方法,引用式写法拿不到组件实例`);
4566
+ continue;
4567
+ }
4568
+ if ((loaderNode.params ?? []).length !== 2) {
4569
+ fail(member, dec, `${CODE}: loader 必须声明两个形参 (self, signal) —— 装饰器实参里没有 this,组件实例要显式传进来`);
4570
+ continue;
4571
+ }
4572
+ /** loader 表达式切片(搬进方法体后 `this` 才是实例) */
4573
+ const loaderExpr = `(${code.slice(loaderNode.start, loaderNode.end)})`;
4574
+ let sourceExpr;
4575
+ const keep = [];
4576
+ let optsBad = false;
4577
+ let optsNode;
4578
+ if (callArgs.length === 2) {
4579
+ optsNode = unwrapExpr(callArgs[1]);
4580
+ if (optsNode?.type !== "ObjectExpression") {
4581
+ fail(member, dec, `${CODE}: 第二个参数须是内联对象字面量(变量/import 无法静态前移)`);
4582
+ continue;
4583
+ }
4584
+ }
4585
+ for (const p of optsNode?.properties ?? []) {
4586
+ if (p.type === "SpreadElement" || p.type === "ExperimentalSpreadProperty") {
4587
+ fail(member, dec, `${CODE}: 选项不支持展开(spread 无法静态前移),请逐项写出`);
4588
+ optsBad = true;
4589
+ break;
4590
+ }
4591
+ if (p.computed) {
4592
+ fail(member, dec, `${CODE}: 选项键无法静态解析(计算属性键不支持)`);
4593
+ optsBad = true;
4594
+ break;
4595
+ }
4596
+ const k = keyName(p.key);
4597
+ if (!k) {
4598
+ fail(member, dec, `${CODE}: 选项键无法静态解析(计算属性键不支持)`);
4599
+ optsBad = true;
4600
+ break;
4601
+ }
4602
+ if (!OPT_KEYS.has(k)) {
4603
+ fail(member, dec, `${CODE}: ${k === "loader" || k === "fetch" || k === "fetcher" ? `'${k}' 不是选项——loader 是第一个参数,写成 @resource((self, signal) => …)${MIGRATE}` : ""}未知选项 '${k}'——ResourceDecoratorOptions 仅支持 ${[...OPT_KEYS].join(" / ")}`);
4604
+ optsBad = true;
4605
+ break;
4606
+ }
4607
+ if (k === "source") {
4608
+ const v = unwrapExpr(p.value);
4609
+ if (v?.type !== "ArrowFunctionExpression") {
4610
+ fail(member, dec, `${CODE}: source 须是内联箭头函数 (self) => …——编译期要把这段表达式搬进实例方法,引用式写法拿不到组件实例`);
4611
+ optsBad = true;
4612
+ break;
4613
+ }
4614
+ if ((v.params ?? []).length > 1) {
4615
+ fail(member, dec, `${CODE}: source 最多接收一个形参 (self)`);
4616
+ optsBad = true;
4617
+ break;
4618
+ }
4619
+ sourceExpr = `(${code.slice(v.start, v.end)})`;
4620
+ continue;
4621
+ }
4622
+ keep.push(code.slice(p.start, p.end));
4623
+ }
4624
+ if (optsBad) continue;
4625
+ emitted.add(rawName);
4626
+ const keyLit = JSON.stringify(rawName);
4627
+ accessors += `${DEBUG_DIV} resource ${rawName}\n${LOADER_PREFIX}${rawName}(__signal) { return ${loaderExpr}(this, __signal) }\n` + (sourceExpr ? `${SOURCE_PREFIX}${rawName}() { return ${sourceExpr}(this) }\n` : "");
4628
+ const parts = keep.slice();
4629
+ if (sourceExpr) parts.push("source: true");
4630
+ entries.push(`${keyLit}: { ${parts.join(", ")} }`);
4631
+ removals.push(lineSpan(code, Math.min(member.start, dec.start ?? member.start), member.end));
4632
+ }
4633
+ if (!removals.length) return result;
4634
+ result.accessors = accessors;
4635
+ result.removals = removals;
4636
+ result.resourcesCode = `{ ${entries.join(", ")} }`;
4637
+ return result;
4638
+ }
4639
+
4402
4640
  //#endregion
4403
4641
  //#region src/analyze/method-deco-extract.ts
4404
4642
  /** 本地变量前缀(避开用户标识符;`__` 前缀与框架其余注入一致)。 */
@@ -4712,8 +4950,7 @@ function staticHtmlFragmentOf(e, comp) {
4712
4950
  * 非 this 起始 / 含 computed 访问时返回 null。
4713
4951
  *
4714
4952
  * 统一实现在 utils/ast#thisChain(属性名走 keyName,`#private` 带 `#` 前缀,
4715
- * 与 render-body 侧对齐 —— 本副本原先手写分支漏了前缀,会把 `this.#x` 与
4716
- * `this.x` 混成同一个依赖键)。
4953
+ * 否则 `this.#x` 与 `this.x` 会混成同一个依赖键)。
4717
4954
  */
4718
4955
  /**
4719
4956
  * 提取 TAG 指令实参中的 this 链全前缀路径。
@@ -7944,16 +8181,21 @@ function compileFile(code, id, options = {}) {
7944
8181
  const mdRes = extractMethodDecos(comp, code, convCtx);
7945
8182
  conventionErrors.push(...mdRes.errors);
7946
8183
  const mdHelpers = mdRes.runtimeHelpers.map((n) => helperBindings[n] ?? n);
8184
+ const resRes = extractResources(comp, code, convCtx);
8185
+ conventionErrors.push(...resRes.errors);
8186
+ const resHelpers = resRes.runtimeHelpers.map((n) => helperBindings[n] ?? n);
7947
8187
  const allRemovals = [
7948
8188
  ...watchRemovals,
7949
8189
  ...fieldRes.removals,
7950
- ...mdRes.removals
8190
+ ...mdRes.removals,
8191
+ ...resRes.removals
7951
8192
  ];
7952
8193
  const fieldCodes = {
7953
8194
  props: fieldRes.propsCode,
7954
8195
  states: fieldRes.statesCode,
7955
8196
  computedGetters: fieldRes.computedCode,
7956
- accessors: fieldRes.accessors || void 0
8197
+ resources: resRes.resourcesCode,
8198
+ accessors: fieldRes.accessors + resRes.accessors || void 0
7957
8199
  };
7958
8200
  const ctorPlan = fieldRes.fieldInits.length > 0 || !!mdRes.ctorStmts ? planCtorInsert(comp, comp.cls ?? convCtx.localClasses.get(comp.className), code) : void 0;
7959
8201
  if (ctorPlan?.warn) conventionErrors.push({
@@ -7969,7 +8211,11 @@ function compileFile(code, id, options = {}) {
7969
8211
  methodDecoCtor: mdRes.ctorStmts || void 0,
7970
8212
  methodDecoDestroy: mdRes.destroyStmts || void 0,
7971
8213
  get helpers() {
7972
- const all = [...fieldHelpers, ...writers];
8214
+ const all = [
8215
+ ...fieldHelpers,
8216
+ ...resHelpers,
8217
+ ...writers
8218
+ ];
7973
8219
  return all.length ? [...new Set(all)] : void 0;
7974
8220
  },
7975
8221
  myfxHelpers: mdHelpers.length ? [...mdHelpers] : void 0
@@ -8014,19 +8260,20 @@ function compileFile(code, id, options = {}) {
8014
8260
  continue;
8015
8261
  }
8016
8262
  if (comp.skipInjection) {
8017
- if (!!fieldRes.accessors || fieldRes.propsCode !== void 0 || fieldRes.statesCode !== void 0 || fieldRes.computedCode !== void 0 || !!cssEffectCode || watchEffectsCode) {
8263
+ if (!!fieldRes.accessors || !!resRes.accessors || fieldRes.propsCode !== void 0 || fieldRes.statesCode !== void 0 || fieldRes.computedCode !== void 0 || resRes.resourcesCode !== void 0 || !!cssEffectCode || watchEffectsCode) {
8018
8264
  const mk = (label, body) => `${DEBUG_DIV} ${label}\n${body}`;
8019
8265
  const parts = [
8020
8266
  mk("inherits", `...Reflect.getPrototypeOf(this).__ce_static__`),
8021
8267
  mk("props", `props: ${emitFamily("props", fieldRes.propsCode)}`),
8022
8268
  mk("states", `states: ${emitFamily("states", fieldRes.statesCode)}`),
8023
- mk("computedGetters", `computedGetters: ${emitFamily("computedGetters", fieldRes.computedCode)}`)
8269
+ mk("computedGetters", `computedGetters: ${emitFamily("computedGetters", fieldRes.computedCode)}`),
8270
+ mk("resources", `resources: ${emitFamily("resources", resRes.resourcesCode)}`)
8024
8271
  ];
8025
8272
  if (watchEffectsCode) parts.push(mk("watchEffects", `watchEffects: ${emitWatchEffects(watchEffectsCode, watchRes.inlinedAncestors)}`));
8026
8273
  if (cssEffectCode) parts.push(mk("cssEffect", `cssEffect: ${cssEffectCode}`));
8027
8274
  edits.push({
8028
8275
  comp,
8029
- literal: `static __ce_static__ = {\n${parts.join(",\n")}\n};\n` + (fieldRes.accessors || ""),
8276
+ literal: `static __ce_static__ = {\n${parts.join(",\n")}\n};\n` + (fieldRes.accessors + resRes.accessors || ""),
8030
8277
  removals: allRemovals,
8031
8278
  ...fieldExtras
8032
8279
  });
@@ -8725,4 +8972,4 @@ var vite_default = compelemCompiler;
8725
8972
 
8726
8973
  //#endregion
8727
8974
  export { collectCompelemImports as C, analyzeFile as S, parse$1 as T, DEBUG_DIV as _, scanKnownTags as a, inlineTemplateMethods as b, offsetToLineCol as c, reportDiagnostics as d, compileFile as f, injectInto as g, buildStaticLiteral as h, TAG_RE as i, relativeToRoot as l, defaultInclude as m, vite_default as n, collectDiagnostics as o, compileSource as p, DEFAULT_TAG_EXT as r, newlineIndex as s, compelemCompiler as t, reportConventionDiagnostics as u, StaticAnalysisError as v, isCompelemSource as w, analyzeRender as x, assertStaticAnalysis as y };
8728
- //# sourceMappingURL=vite-CLK9td6W.js.map
8975
+ //# sourceMappingURL=vite-C95uwtJI.js.map