@weapp-vite/volar 2.0.4 → 2.0.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.cjs CHANGED
@@ -1,300 +1,323 @@
1
- "use strict";
1
+ //#region \0rolldown/runtime.js
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name2 in all)
10
- __defProp(target, name2, { get: all[name2], enumerable: true });
11
- };
12
8
  var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- default: () => index_default
34
- });
35
- module.exports = __toCommonJS(index_exports);
36
- var import_node_module = require("module");
37
- var import_node_path = __toESM(require("path"));
38
- var import_node_process = __toESM(require("process"));
39
-
40
- // package.json
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ let node_module = require("node:module");
24
+ let node_path = require("node:path");
25
+ node_path = __toESM(node_path);
26
+ let node_process = require("node:process");
27
+ node_process = __toESM(node_process);
28
+ let _weapp_core_schematics = require("@weapp-core/schematics");
29
+ //#region package.json
41
30
  var name = "@weapp-vite/volar";
42
-
43
- // src/schema.ts
44
- var import_schematics = require("@weapp-core/schematics");
31
+ //#endregion
32
+ //#region src/schema.ts
33
+ /**
34
+ * 为小程序配置生成 JSON Schema
35
+ * 为支持 JSON Schema 的编辑器提供验证和自动补全
36
+ *
37
+ * 注意:JSON Schema 定义由 @weapp-core/schematics 统一维护
38
+ * 使用 Zod 定义并自动生成,确保单一数据源
39
+ */
40
+ /**
41
+ * 根据文件类型获取对应的 JSON Schema
42
+ * Schema 定义来自 @weapp-core/schematics,使用 Zod 维护单一数据源
43
+ */
45
44
  function getSchemaForType(type) {
46
- const definition = import_schematics.JSON_SCHEMA_DEFINITIONS.find((d) => d.typeName === type);
47
- if (!definition) {
48
- return null;
49
- }
50
- return definition.schema;
45
+ const definition = _weapp_core_schematics.JSON_SCHEMA_DEFINITIONS.find((d) => d.typeName === type);
46
+ if (!definition) return null;
47
+ return definition.schema;
51
48
  }
52
-
53
- // src/index.ts
54
- var BLOCK_TYPE = "json";
55
- var JS_LANG = "js";
56
- var JSONC_LANG = "jsonc";
57
- var JSON_LANG = "json";
58
- var JSON5_LANG = "json5";
59
- var PLUGIN_VERSION = 2.2;
60
- var TS_LANG = "ts";
61
- var FULL_CAPABILITIES = {
62
- verification: true,
63
- completion: true,
64
- semantic: true,
65
- navigation: true,
66
- structure: true,
67
- format: true
49
+ //#endregion
50
+ //#region src/index.ts
51
+ const BLOCK_TYPE = "json";
52
+ const JS_LANG = "js";
53
+ const JSONC_LANG = "jsonc";
54
+ const JSON_LANG = "json";
55
+ const JSON5_LANG = "json5";
56
+ const PLUGIN_VERSION = 2.2;
57
+ const TS_LANG = "ts";
58
+ const BACKSLASH_RE = /\\/g;
59
+ const NON_SPACE_RE = /\S/;
60
+ const WXS_MODULE_RE = /<wxs[\s\S]*?module\s*=\s*(?:"([^"]+)"|'([^']+)')[\s\S]*?\/?>/gi;
61
+ const FULL_CAPABILITIES = {
62
+ verification: true,
63
+ completion: true,
64
+ semantic: true,
65
+ navigation: true,
66
+ structure: true,
67
+ format: true
68
68
  };
69
- var VOID_CAPABILITIES = {
70
- verification: false,
71
- completion: false,
72
- semantic: false,
73
- navigation: false,
74
- structure: false,
75
- format: false
69
+ const VOID_CAPABILITIES = {
70
+ verification: false,
71
+ completion: false,
72
+ semantic: false,
73
+ navigation: false,
74
+ structure: false,
75
+ format: false
76
76
  };
77
- var require2 = (0, import_node_module.createRequire)(
78
- typeof module !== "undefined" && module.filename ? module.filename : import_node_path.default.join(import_node_process.default.cwd(), "weapp-vite-volar.cjs")
79
- );
80
- var hasSchematicsTypes = false;
77
+ const require$1 = (0, node_module.createRequire)(typeof module !== "undefined" && module.filename ? module.filename : node_path.default.join(node_process.default.cwd(), "weapp-vite-volar.cjs"));
78
+ let hasSchematicsTypes = false;
81
79
  try {
82
- require2.resolve("@weapp-core/schematics");
83
- hasSchematicsTypes = true;
80
+ require$1.resolve("@weapp-core/schematics");
81
+ hasSchematicsTypes = true;
84
82
  } catch {
85
- hasSchematicsTypes = false;
83
+ hasSchematicsTypes = false;
84
+ }
85
+ function parseVueSfc(content, filename = "component.vue") {
86
+ try {
87
+ return require$1("@vue/compiler-sfc").parse(content, { filename });
88
+ } catch {
89
+ return;
90
+ }
91
+ }
92
+ function collectWxsModuleNames(templateContent) {
93
+ if (!templateContent) return [];
94
+ const names = /* @__PURE__ */ new Set();
95
+ for (const match of templateContent.matchAll(WXS_MODULE_RE)) {
96
+ const name = match[1] ?? match[2];
97
+ if (name) names.add(name);
98
+ }
99
+ return [...names];
100
+ }
101
+ function createWxsModuleDeclarations(moduleNames) {
102
+ if (!moduleNames.length) return "";
103
+ return moduleNames.map((name) => `const ${name} = {} as Record<string, (...args: any[]) => any>`).join("\n");
104
+ }
105
+ function appendWxsDeclarations(code, moduleNames) {
106
+ const declarations = createWxsModuleDeclarations(moduleNames);
107
+ if (!declarations) return code;
108
+ return code ? `${code}\n\n${declarations}\n` : `${declarations}\n`;
109
+ }
110
+ function createSyntheticScriptSetup(moduleNames) {
111
+ const content = createWxsModuleDeclarations(moduleNames);
112
+ if (!content) return;
113
+ return {
114
+ type: "script",
115
+ content,
116
+ loc: {
117
+ source: `<script setup lang="ts">\n${content}\n<\/script>`,
118
+ start: {
119
+ column: 1,
120
+ line: 1,
121
+ offset: 0
122
+ },
123
+ end: {
124
+ column: 1,
125
+ line: 1,
126
+ offset: 0
127
+ }
128
+ },
129
+ attrs: {
130
+ setup: true,
131
+ lang: "ts"
132
+ },
133
+ lang: "ts",
134
+ setup: true,
135
+ name: "scriptSetup"
136
+ };
86
137
  }
87
138
  function normalizeFilename(filename) {
88
- if (!filename) {
89
- return "";
90
- }
91
- return filename.replace(/\\/g, "/");
139
+ if (!filename) return "";
140
+ return filename.replace(BACKSLASH_RE, "/");
92
141
  }
93
142
  function inferConfigType(filename) {
94
- const normalized = normalizeFilename(filename);
95
- if (normalized.endsWith("/app.vue")) {
96
- return "App";
97
- }
98
- if (normalized.includes("/plugin/")) {
99
- return "Plugin";
100
- }
101
- if (normalized.includes("/components/")) {
102
- return "Component";
103
- }
104
- if (normalized.includes("/theme/")) {
105
- return "Theme";
106
- }
107
- if (normalized.includes("/sitemap")) {
108
- return "Sitemap";
109
- }
110
- return "Page";
143
+ const normalized = normalizeFilename(filename);
144
+ if (normalized.endsWith("/app.vue")) return "App";
145
+ if (normalized.includes("/plugin/")) return "Plugin";
146
+ if (normalized.includes("/components/")) return "Component";
147
+ if (normalized.includes("/theme/")) return "Theme";
148
+ if (normalized.includes("/sitemap")) return "Sitemap";
149
+ return "Page";
111
150
  }
112
151
  function normalizeLang(lang) {
113
- if (!lang) {
114
- return JSON_LANG;
115
- }
116
- const lower = lang.toLowerCase();
117
- if (lower === "txt") {
118
- return JSON_LANG;
119
- }
120
- return lower;
152
+ if (!lang) return JSON_LANG;
153
+ const lower = lang.toLowerCase();
154
+ if (lower === "txt") return JSON_LANG;
155
+ return lower;
121
156
  }
122
157
  function findExportDefaultExpression(code, tsModule, lang) {
123
- const scriptKind = lang === TS_LANG ? tsModule.ScriptKind.TS : tsModule.ScriptKind.JS;
124
- const sourceFile = tsModule.createSourceFile(
125
- `config.${lang}`,
126
- code,
127
- tsModule.ScriptTarget.Latest,
128
- true,
129
- scriptKind
130
- );
131
- for (const statement of sourceFile.statements) {
132
- if (tsModule.isExportAssignment(statement)) {
133
- const expressionStart = statement.expression.getStart(sourceFile);
134
- const expressionEnd = statement.expression.getEnd();
135
- const leading = code.slice(0, statement.getStart(sourceFile));
136
- const expression = code.slice(expressionStart, expressionEnd);
137
- const trailing = code.slice(statement.getEnd());
138
- return {
139
- expression,
140
- expressionStart,
141
- expressionEnd,
142
- leading,
143
- trailing
144
- };
145
- }
146
- }
147
- return null;
158
+ const scriptKind = lang === TS_LANG ? tsModule.ScriptKind.TS : tsModule.ScriptKind.JS;
159
+ const sourceFile = tsModule.createSourceFile(`config.${lang}`, code, tsModule.ScriptTarget.Latest, true, scriptKind);
160
+ for (const statement of sourceFile.statements) if (tsModule.isExportAssignment(statement)) {
161
+ const expressionStart = statement.expression.getStart(sourceFile);
162
+ const expressionEnd = statement.expression.getEnd();
163
+ const leading = code.slice(0, statement.getStart(sourceFile));
164
+ return {
165
+ expression: code.slice(expressionStart, expressionEnd),
166
+ expressionStart,
167
+ expressionEnd,
168
+ leading,
169
+ trailing: code.slice(statement.getEnd())
170
+ };
171
+ }
172
+ return null;
148
173
  }
149
174
  function injectSchemaIntoJsonObject(content, schemaId) {
150
- const trimmed = content.trim();
151
- if (!trimmed.startsWith("{")) {
152
- return content;
153
- }
154
- const leftBraceIndex = content.indexOf("{");
155
- if (leftBraceIndex < 0) {
156
- return content;
157
- }
158
- const afterLeft = content.slice(leftBraceIndex + 1);
159
- const firstNonSpace = afterLeft.match(/\S/);
160
- const nextCharIndex = firstNonSpace ? leftBraceIndex + 1 + firstNonSpace.index : -1;
161
- const isEmptyObject = nextCharIndex >= 0 && content[nextCharIndex] === "}";
162
- const schemaLine = ` "$schema": "${schemaId}"`;
163
- const injected = isEmptyObject ? `{
164
- ${schemaLine}
165
- }` : `{
166
- ${schemaLine},${content.slice(leftBraceIndex + 1)}`;
167
- if (trimmed === content) {
168
- return injected;
169
- }
170
- const leading = content.slice(0, content.indexOf(trimmed));
171
- const trailing = content.slice(content.indexOf(trimmed) + trimmed.length);
172
- return `${leading}${injected}${trailing}`;
175
+ const trimmed = content.trim();
176
+ if (!trimmed.startsWith("{")) return content;
177
+ const leftBraceIndex = content.indexOf("{");
178
+ if (leftBraceIndex < 0) return content;
179
+ const firstNonSpace = content.slice(leftBraceIndex + 1).match(NON_SPACE_RE);
180
+ const nextCharIndex = firstNonSpace ? leftBraceIndex + 1 + firstNonSpace.index : -1;
181
+ const isEmptyObject = nextCharIndex >= 0 && content[nextCharIndex] === "}";
182
+ const schemaLine = ` "$schema": "${schemaId}"`;
183
+ const injected = isEmptyObject ? `{\n${schemaLine}\n}` : `{\n${schemaLine},${content.slice(leftBraceIndex + 1)}`;
184
+ if (trimmed === content) return injected;
185
+ return `${content.slice(0, content.indexOf(trimmed))}${injected}${content.slice(content.indexOf(trimmed) + trimmed.length)}`;
173
186
  }
174
- var plugin = (ctx) => {
175
- let tsModule = ctx?.modules?.typescript;
176
- if (!tsModule) {
177
- try {
178
- tsModule = require2("typescript");
179
- } catch {
180
- tsModule = void 0;
181
- }
182
- }
183
- return {
184
- name,
185
- version: PLUGIN_VERSION,
186
- getEmbeddedCodes(_, sfc) {
187
- const names = [];
188
- for (let i = 0; i < sfc.customBlocks.length; i++) {
189
- const block = sfc.customBlocks[i];
190
- if (block.type === BLOCK_TYPE) {
191
- const normalizedLang = normalizeLang(block.lang);
192
- const isJsLike = normalizedLang === JS_LANG || normalizedLang === TS_LANG;
193
- if (isJsLike) {
194
- names.push({ id: `${BLOCK_TYPE}_${i}`, lang: TS_LANG });
195
- continue;
196
- }
197
- const embeddedLang = normalizedLang === JSON_LANG || normalizedLang === JSONC_LANG || normalizedLang === JSON5_LANG ? JSONC_LANG : JSON_LANG;
198
- names.push({ id: `${BLOCK_TYPE}_${i}`, lang: embeddedLang });
199
- }
200
- }
201
- return names;
202
- },
203
- resolveEmbeddedCode(fileName, sfc, embeddedCode) {
204
- const match = embeddedCode.id.match(new RegExp(`^${BLOCK_TYPE}_(\\d+)$`));
205
- if (!match) {
206
- return;
207
- }
208
- const index = Number.parseInt(match[1]);
209
- const block = sfc.customBlocks[index];
210
- if (!block) {
211
- return;
212
- }
213
- const normalizedLang = normalizeLang(block.lang);
214
- const configType = inferConfigType(fileName);
215
- if (!hasSchematicsTypes) {
216
- embeddedCode.content.push([
217
- block.content,
218
- block.name,
219
- 0,
220
- FULL_CAPABILITIES
221
- ]);
222
- return;
223
- }
224
- const userWantsJs = normalizedLang === JS_LANG || normalizedLang === TS_LANG;
225
- if (userWantsJs) {
226
- const parsed = tsModule && findExportDefaultExpression(block.content, tsModule, normalizedLang);
227
- if (parsed && hasSchematicsTypes) {
228
- const typeImport = `import type { ${configType} as __WeappConfig } from '@weapp-core/schematics'
229
- `;
230
- const helper = "const __weapp_defineConfig = <T extends __WeappConfig>(config: T) => config\n\n";
231
- embeddedCode.content.push([
232
- `${typeImport}${helper}`,
233
- void 0,
234
- 0,
235
- VOID_CAPABILITIES
236
- ]);
237
- if (parsed.leading) {
238
- embeddedCode.content.push([
239
- parsed.leading,
240
- block.name,
241
- 0,
242
- FULL_CAPABILITIES
243
- ]);
244
- }
245
- embeddedCode.content.push([
246
- "export default __weapp_defineConfig(",
247
- void 0,
248
- parsed.expressionStart,
249
- VOID_CAPABILITIES
250
- ]);
251
- embeddedCode.content.push([
252
- parsed.expression,
253
- block.name,
254
- parsed.expressionStart,
255
- FULL_CAPABILITIES
256
- ]);
257
- embeddedCode.content.push([
258
- ")",
259
- void 0,
260
- parsed.expressionEnd,
261
- VOID_CAPABILITIES
262
- ]);
263
- if (parsed.trailing) {
264
- embeddedCode.content.push([
265
- parsed.trailing,
266
- block.name,
267
- parsed.expressionEnd,
268
- FULL_CAPABILITIES
269
- ]);
270
- }
271
- return;
272
- }
273
- embeddedCode.content.push([
274
- block.content,
275
- block.name,
276
- 0,
277
- FULL_CAPABILITIES
278
- ]);
279
- return;
280
- }
281
- const schema = getSchemaForType(configType);
282
- if (schema && schema.$id && !block.content.includes("$schema")) {
283
- embeddedCode.content.push([
284
- injectSchemaIntoJsonObject(block.content, schema.$id),
285
- block.name,
286
- 0,
287
- FULL_CAPABILITIES
288
- ]);
289
- return;
290
- }
291
- embeddedCode.content.push([
292
- block.content,
293
- block.name,
294
- 0,
295
- FULL_CAPABILITIES
296
- ]);
297
- }
298
- };
187
+ /**
188
+ * Volar 语言插件:为 weapp 配置块提供类型与 schema 提示。
189
+ */
190
+ const plugin = (ctx) => {
191
+ let tsModule = ctx?.modules?.typescript;
192
+ if (!tsModule) try {
193
+ tsModule = require$1("typescript");
194
+ } catch {
195
+ tsModule = void 0;
196
+ }
197
+ return {
198
+ name,
199
+ version: PLUGIN_VERSION,
200
+ order: -1,
201
+ parseSFC2(fileName, languageId, content) {
202
+ if (languageId !== "vue") return;
203
+ const parsed = parseVueSfc(content, fileName);
204
+ if (!parsed) return;
205
+ const descriptor = parsed.descriptor;
206
+ const wxsModuleNames = collectWxsModuleNames(descriptor.template?.content);
207
+ if (!wxsModuleNames.length) return parsed;
208
+ if (descriptor.scriptSetup) descriptor.scriptSetup.content = appendWxsDeclarations(descriptor.scriptSetup.content, wxsModuleNames);
209
+ else descriptor.scriptSetup = createSyntheticScriptSetup(wxsModuleNames);
210
+ return parsed;
211
+ },
212
+ getEmbeddedCodes(_, sfc) {
213
+ const names = [];
214
+ for (let i = 0; i < sfc.customBlocks.length; i++) {
215
+ const block = sfc.customBlocks[i];
216
+ if (block.type === BLOCK_TYPE) {
217
+ const normalizedLang = normalizeLang(block.lang);
218
+ if (normalizedLang === JS_LANG || normalizedLang === TS_LANG) {
219
+ names.push({
220
+ id: `${BLOCK_TYPE}_${i}`,
221
+ lang: TS_LANG
222
+ });
223
+ continue;
224
+ }
225
+ const embeddedLang = normalizedLang === JSON_LANG || normalizedLang === JSONC_LANG || normalizedLang === JSON5_LANG ? JSONC_LANG : JSON_LANG;
226
+ names.push({
227
+ id: `${BLOCK_TYPE}_${i}`,
228
+ lang: embeddedLang
229
+ });
230
+ }
231
+ }
232
+ return names;
233
+ },
234
+ resolveEmbeddedCode(fileName, sfc, embeddedCode) {
235
+ const match = embeddedCode.id.match(new RegExp(`^${BLOCK_TYPE}_(\\d+)$`));
236
+ if (!match) return;
237
+ const index = Number.parseInt(match[1]);
238
+ const block = sfc.customBlocks[index];
239
+ if (!block) return;
240
+ const normalizedLang = normalizeLang(block.lang);
241
+ const configType = inferConfigType(fileName);
242
+ if (!hasSchematicsTypes) {
243
+ embeddedCode.content.push([
244
+ block.content,
245
+ block.name,
246
+ 0,
247
+ FULL_CAPABILITIES
248
+ ]);
249
+ return;
250
+ }
251
+ if (normalizedLang === JS_LANG || normalizedLang === TS_LANG) {
252
+ const parsed = tsModule && findExportDefaultExpression(block.content, tsModule, normalizedLang);
253
+ if (parsed && hasSchematicsTypes) {
254
+ const typeImport = `import type { ${configType} as __WeappConfig } from '@weapp-core/schematics'\n`;
255
+ embeddedCode.content.push([
256
+ `${typeImport}const __weapp_defineConfig = <T extends __WeappConfig>(config: T) => config
257
+
258
+ `,
259
+ void 0,
260
+ 0,
261
+ VOID_CAPABILITIES
262
+ ]);
263
+ if (parsed.leading) embeddedCode.content.push([
264
+ parsed.leading,
265
+ block.name,
266
+ 0,
267
+ FULL_CAPABILITIES
268
+ ]);
269
+ embeddedCode.content.push([
270
+ "export default __weapp_defineConfig(",
271
+ void 0,
272
+ parsed.expressionStart,
273
+ VOID_CAPABILITIES
274
+ ]);
275
+ embeddedCode.content.push([
276
+ parsed.expression,
277
+ block.name,
278
+ parsed.expressionStart,
279
+ FULL_CAPABILITIES
280
+ ]);
281
+ embeddedCode.content.push([
282
+ ")",
283
+ void 0,
284
+ parsed.expressionEnd,
285
+ VOID_CAPABILITIES
286
+ ]);
287
+ if (parsed.trailing) embeddedCode.content.push([
288
+ parsed.trailing,
289
+ block.name,
290
+ parsed.expressionEnd,
291
+ FULL_CAPABILITIES
292
+ ]);
293
+ return;
294
+ }
295
+ embeddedCode.content.push([
296
+ block.content,
297
+ block.name,
298
+ 0,
299
+ FULL_CAPABILITIES
300
+ ]);
301
+ return;
302
+ }
303
+ const schema = getSchemaForType(configType);
304
+ if (schema && schema.$id && !block.content.includes("$schema")) {
305
+ embeddedCode.content.push([
306
+ injectSchemaIntoJsonObject(block.content, schema.$id),
307
+ block.name,
308
+ 0,
309
+ FULL_CAPABILITIES
310
+ ]);
311
+ return;
312
+ }
313
+ embeddedCode.content.push([
314
+ block.content,
315
+ block.name,
316
+ 0,
317
+ FULL_CAPABILITIES
318
+ ]);
319
+ }
320
+ };
299
321
  };
300
- var index_default = plugin;
322
+ //#endregion
323
+ module.exports = plugin;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { VueLanguagePlugin } from '@vue/language-core';
2
-
1
+ import type { VueLanguagePlugin } from '@vue/language-core';
3
2
  /**
4
3
  * Volar 语言插件:为 weapp 配置块提供类型与 schema 提示。
5
4
  */
6
5
  declare const plugin: VueLanguagePlugin;
7
-
8
- export = plugin;
6
+ export default plugin;
package/dist/index.mjs CHANGED
@@ -1,269 +1,299 @@
1
- // src/index.ts
2
- import { createRequire } from "module";
3
- import path from "path";
4
- import process from "process";
5
-
6
- // package.json
7
- var name = "@weapp-vite/volar";
8
-
9
- // src/schema.ts
1
+ import { createRequire } from "node:module";
2
+ import path from "node:path";
3
+ import process from "node:process";
10
4
  import { JSON_SCHEMA_DEFINITIONS } from "@weapp-core/schematics";
5
+ //#region package.json
6
+ var name = "@weapp-vite/volar";
7
+ //#endregion
8
+ //#region src/schema.ts
9
+ /**
10
+ * 为小程序配置生成 JSON Schema
11
+ * 为支持 JSON Schema 的编辑器提供验证和自动补全
12
+ *
13
+ * 注意:JSON Schema 定义由 @weapp-core/schematics 统一维护
14
+ * 使用 Zod 定义并自动生成,确保单一数据源
15
+ */
16
+ /**
17
+ * 根据文件类型获取对应的 JSON Schema
18
+ * Schema 定义来自 @weapp-core/schematics,使用 Zod 维护单一数据源
19
+ */
11
20
  function getSchemaForType(type) {
12
- const definition = JSON_SCHEMA_DEFINITIONS.find((d) => d.typeName === type);
13
- if (!definition) {
14
- return null;
15
- }
16
- return definition.schema;
21
+ const definition = JSON_SCHEMA_DEFINITIONS.find((d) => d.typeName === type);
22
+ if (!definition) return null;
23
+ return definition.schema;
17
24
  }
18
-
19
- // src/index.ts
20
- var BLOCK_TYPE = "json";
21
- var JS_LANG = "js";
22
- var JSONC_LANG = "jsonc";
23
- var JSON_LANG = "json";
24
- var JSON5_LANG = "json5";
25
- var PLUGIN_VERSION = 2.2;
26
- var TS_LANG = "ts";
27
- var FULL_CAPABILITIES = {
28
- verification: true,
29
- completion: true,
30
- semantic: true,
31
- navigation: true,
32
- structure: true,
33
- format: true
25
+ //#endregion
26
+ //#region src/index.ts
27
+ const BLOCK_TYPE = "json";
28
+ const JS_LANG = "js";
29
+ const JSONC_LANG = "jsonc";
30
+ const JSON_LANG = "json";
31
+ const JSON5_LANG = "json5";
32
+ const PLUGIN_VERSION = 2.2;
33
+ const TS_LANG = "ts";
34
+ const BACKSLASH_RE = /\\/g;
35
+ const NON_SPACE_RE = /\S/;
36
+ const WXS_MODULE_RE = /<wxs[\s\S]*?module\s*=\s*(?:"([^"]+)"|'([^']+)')[\s\S]*?\/?>/gi;
37
+ const FULL_CAPABILITIES = {
38
+ verification: true,
39
+ completion: true,
40
+ semantic: true,
41
+ navigation: true,
42
+ structure: true,
43
+ format: true
34
44
  };
35
- var VOID_CAPABILITIES = {
36
- verification: false,
37
- completion: false,
38
- semantic: false,
39
- navigation: false,
40
- structure: false,
41
- format: false
45
+ const VOID_CAPABILITIES = {
46
+ verification: false,
47
+ completion: false,
48
+ semantic: false,
49
+ navigation: false,
50
+ structure: false,
51
+ format: false
42
52
  };
43
- var require2 = createRequire(
44
- typeof module !== "undefined" && module.filename ? module.filename : path.join(process.cwd(), "weapp-vite-volar.cjs")
45
- );
46
- var hasSchematicsTypes = false;
53
+ const require = createRequire(typeof module !== "undefined" && module.filename ? module.filename : path.join(process.cwd(), "weapp-vite-volar.cjs"));
54
+ let hasSchematicsTypes = false;
47
55
  try {
48
- require2.resolve("@weapp-core/schematics");
49
- hasSchematicsTypes = true;
56
+ require.resolve("@weapp-core/schematics");
57
+ hasSchematicsTypes = true;
50
58
  } catch {
51
- hasSchematicsTypes = false;
59
+ hasSchematicsTypes = false;
60
+ }
61
+ function parseVueSfc(content, filename = "component.vue") {
62
+ try {
63
+ return require("@vue/compiler-sfc").parse(content, { filename });
64
+ } catch {
65
+ return;
66
+ }
67
+ }
68
+ function collectWxsModuleNames(templateContent) {
69
+ if (!templateContent) return [];
70
+ const names = /* @__PURE__ */ new Set();
71
+ for (const match of templateContent.matchAll(WXS_MODULE_RE)) {
72
+ const name = match[1] ?? match[2];
73
+ if (name) names.add(name);
74
+ }
75
+ return [...names];
76
+ }
77
+ function createWxsModuleDeclarations(moduleNames) {
78
+ if (!moduleNames.length) return "";
79
+ return moduleNames.map((name) => `const ${name} = {} as Record<string, (...args: any[]) => any>`).join("\n");
80
+ }
81
+ function appendWxsDeclarations(code, moduleNames) {
82
+ const declarations = createWxsModuleDeclarations(moduleNames);
83
+ if (!declarations) return code;
84
+ return code ? `${code}\n\n${declarations}\n` : `${declarations}\n`;
85
+ }
86
+ function createSyntheticScriptSetup(moduleNames) {
87
+ const content = createWxsModuleDeclarations(moduleNames);
88
+ if (!content) return;
89
+ return {
90
+ type: "script",
91
+ content,
92
+ loc: {
93
+ source: `<script setup lang="ts">\n${content}\n<\/script>`,
94
+ start: {
95
+ column: 1,
96
+ line: 1,
97
+ offset: 0
98
+ },
99
+ end: {
100
+ column: 1,
101
+ line: 1,
102
+ offset: 0
103
+ }
104
+ },
105
+ attrs: {
106
+ setup: true,
107
+ lang: "ts"
108
+ },
109
+ lang: "ts",
110
+ setup: true,
111
+ name: "scriptSetup"
112
+ };
52
113
  }
53
114
  function normalizeFilename(filename) {
54
- if (!filename) {
55
- return "";
56
- }
57
- return filename.replace(/\\/g, "/");
115
+ if (!filename) return "";
116
+ return filename.replace(BACKSLASH_RE, "/");
58
117
  }
59
118
  function inferConfigType(filename) {
60
- const normalized = normalizeFilename(filename);
61
- if (normalized.endsWith("/app.vue")) {
62
- return "App";
63
- }
64
- if (normalized.includes("/plugin/")) {
65
- return "Plugin";
66
- }
67
- if (normalized.includes("/components/")) {
68
- return "Component";
69
- }
70
- if (normalized.includes("/theme/")) {
71
- return "Theme";
72
- }
73
- if (normalized.includes("/sitemap")) {
74
- return "Sitemap";
75
- }
76
- return "Page";
119
+ const normalized = normalizeFilename(filename);
120
+ if (normalized.endsWith("/app.vue")) return "App";
121
+ if (normalized.includes("/plugin/")) return "Plugin";
122
+ if (normalized.includes("/components/")) return "Component";
123
+ if (normalized.includes("/theme/")) return "Theme";
124
+ if (normalized.includes("/sitemap")) return "Sitemap";
125
+ return "Page";
77
126
  }
78
127
  function normalizeLang(lang) {
79
- if (!lang) {
80
- return JSON_LANG;
81
- }
82
- const lower = lang.toLowerCase();
83
- if (lower === "txt") {
84
- return JSON_LANG;
85
- }
86
- return lower;
128
+ if (!lang) return JSON_LANG;
129
+ const lower = lang.toLowerCase();
130
+ if (lower === "txt") return JSON_LANG;
131
+ return lower;
87
132
  }
88
133
  function findExportDefaultExpression(code, tsModule, lang) {
89
- const scriptKind = lang === TS_LANG ? tsModule.ScriptKind.TS : tsModule.ScriptKind.JS;
90
- const sourceFile = tsModule.createSourceFile(
91
- `config.${lang}`,
92
- code,
93
- tsModule.ScriptTarget.Latest,
94
- true,
95
- scriptKind
96
- );
97
- for (const statement of sourceFile.statements) {
98
- if (tsModule.isExportAssignment(statement)) {
99
- const expressionStart = statement.expression.getStart(sourceFile);
100
- const expressionEnd = statement.expression.getEnd();
101
- const leading = code.slice(0, statement.getStart(sourceFile));
102
- const expression = code.slice(expressionStart, expressionEnd);
103
- const trailing = code.slice(statement.getEnd());
104
- return {
105
- expression,
106
- expressionStart,
107
- expressionEnd,
108
- leading,
109
- trailing
110
- };
111
- }
112
- }
113
- return null;
134
+ const scriptKind = lang === TS_LANG ? tsModule.ScriptKind.TS : tsModule.ScriptKind.JS;
135
+ const sourceFile = tsModule.createSourceFile(`config.${lang}`, code, tsModule.ScriptTarget.Latest, true, scriptKind);
136
+ for (const statement of sourceFile.statements) if (tsModule.isExportAssignment(statement)) {
137
+ const expressionStart = statement.expression.getStart(sourceFile);
138
+ const expressionEnd = statement.expression.getEnd();
139
+ const leading = code.slice(0, statement.getStart(sourceFile));
140
+ return {
141
+ expression: code.slice(expressionStart, expressionEnd),
142
+ expressionStart,
143
+ expressionEnd,
144
+ leading,
145
+ trailing: code.slice(statement.getEnd())
146
+ };
147
+ }
148
+ return null;
114
149
  }
115
150
  function injectSchemaIntoJsonObject(content, schemaId) {
116
- const trimmed = content.trim();
117
- if (!trimmed.startsWith("{")) {
118
- return content;
119
- }
120
- const leftBraceIndex = content.indexOf("{");
121
- if (leftBraceIndex < 0) {
122
- return content;
123
- }
124
- const afterLeft = content.slice(leftBraceIndex + 1);
125
- const firstNonSpace = afterLeft.match(/\S/);
126
- const nextCharIndex = firstNonSpace ? leftBraceIndex + 1 + firstNonSpace.index : -1;
127
- const isEmptyObject = nextCharIndex >= 0 && content[nextCharIndex] === "}";
128
- const schemaLine = ` "$schema": "${schemaId}"`;
129
- const injected = isEmptyObject ? `{
130
- ${schemaLine}
131
- }` : `{
132
- ${schemaLine},${content.slice(leftBraceIndex + 1)}`;
133
- if (trimmed === content) {
134
- return injected;
135
- }
136
- const leading = content.slice(0, content.indexOf(trimmed));
137
- const trailing = content.slice(content.indexOf(trimmed) + trimmed.length);
138
- return `${leading}${injected}${trailing}`;
151
+ const trimmed = content.trim();
152
+ if (!trimmed.startsWith("{")) return content;
153
+ const leftBraceIndex = content.indexOf("{");
154
+ if (leftBraceIndex < 0) return content;
155
+ const firstNonSpace = content.slice(leftBraceIndex + 1).match(NON_SPACE_RE);
156
+ const nextCharIndex = firstNonSpace ? leftBraceIndex + 1 + firstNonSpace.index : -1;
157
+ const isEmptyObject = nextCharIndex >= 0 && content[nextCharIndex] === "}";
158
+ const schemaLine = ` "$schema": "${schemaId}"`;
159
+ const injected = isEmptyObject ? `{\n${schemaLine}\n}` : `{\n${schemaLine},${content.slice(leftBraceIndex + 1)}`;
160
+ if (trimmed === content) return injected;
161
+ return `${content.slice(0, content.indexOf(trimmed))}${injected}${content.slice(content.indexOf(trimmed) + trimmed.length)}`;
139
162
  }
140
- var plugin = (ctx) => {
141
- let tsModule = ctx?.modules?.typescript;
142
- if (!tsModule) {
143
- try {
144
- tsModule = require2("typescript");
145
- } catch {
146
- tsModule = void 0;
147
- }
148
- }
149
- return {
150
- name,
151
- version: PLUGIN_VERSION,
152
- getEmbeddedCodes(_, sfc) {
153
- const names = [];
154
- for (let i = 0; i < sfc.customBlocks.length; i++) {
155
- const block = sfc.customBlocks[i];
156
- if (block.type === BLOCK_TYPE) {
157
- const normalizedLang = normalizeLang(block.lang);
158
- const isJsLike = normalizedLang === JS_LANG || normalizedLang === TS_LANG;
159
- if (isJsLike) {
160
- names.push({ id: `${BLOCK_TYPE}_${i}`, lang: TS_LANG });
161
- continue;
162
- }
163
- const embeddedLang = normalizedLang === JSON_LANG || normalizedLang === JSONC_LANG || normalizedLang === JSON5_LANG ? JSONC_LANG : JSON_LANG;
164
- names.push({ id: `${BLOCK_TYPE}_${i}`, lang: embeddedLang });
165
- }
166
- }
167
- return names;
168
- },
169
- resolveEmbeddedCode(fileName, sfc, embeddedCode) {
170
- const match = embeddedCode.id.match(new RegExp(`^${BLOCK_TYPE}_(\\d+)$`));
171
- if (!match) {
172
- return;
173
- }
174
- const index = Number.parseInt(match[1]);
175
- const block = sfc.customBlocks[index];
176
- if (!block) {
177
- return;
178
- }
179
- const normalizedLang = normalizeLang(block.lang);
180
- const configType = inferConfigType(fileName);
181
- if (!hasSchematicsTypes) {
182
- embeddedCode.content.push([
183
- block.content,
184
- block.name,
185
- 0,
186
- FULL_CAPABILITIES
187
- ]);
188
- return;
189
- }
190
- const userWantsJs = normalizedLang === JS_LANG || normalizedLang === TS_LANG;
191
- if (userWantsJs) {
192
- const parsed = tsModule && findExportDefaultExpression(block.content, tsModule, normalizedLang);
193
- if (parsed && hasSchematicsTypes) {
194
- const typeImport = `import type { ${configType} as __WeappConfig } from '@weapp-core/schematics'
195
- `;
196
- const helper = "const __weapp_defineConfig = <T extends __WeappConfig>(config: T) => config\n\n";
197
- embeddedCode.content.push([
198
- `${typeImport}${helper}`,
199
- void 0,
200
- 0,
201
- VOID_CAPABILITIES
202
- ]);
203
- if (parsed.leading) {
204
- embeddedCode.content.push([
205
- parsed.leading,
206
- block.name,
207
- 0,
208
- FULL_CAPABILITIES
209
- ]);
210
- }
211
- embeddedCode.content.push([
212
- "export default __weapp_defineConfig(",
213
- void 0,
214
- parsed.expressionStart,
215
- VOID_CAPABILITIES
216
- ]);
217
- embeddedCode.content.push([
218
- parsed.expression,
219
- block.name,
220
- parsed.expressionStart,
221
- FULL_CAPABILITIES
222
- ]);
223
- embeddedCode.content.push([
224
- ")",
225
- void 0,
226
- parsed.expressionEnd,
227
- VOID_CAPABILITIES
228
- ]);
229
- if (parsed.trailing) {
230
- embeddedCode.content.push([
231
- parsed.trailing,
232
- block.name,
233
- parsed.expressionEnd,
234
- FULL_CAPABILITIES
235
- ]);
236
- }
237
- return;
238
- }
239
- embeddedCode.content.push([
240
- block.content,
241
- block.name,
242
- 0,
243
- FULL_CAPABILITIES
244
- ]);
245
- return;
246
- }
247
- const schema = getSchemaForType(configType);
248
- if (schema && schema.$id && !block.content.includes("$schema")) {
249
- embeddedCode.content.push([
250
- injectSchemaIntoJsonObject(block.content, schema.$id),
251
- block.name,
252
- 0,
253
- FULL_CAPABILITIES
254
- ]);
255
- return;
256
- }
257
- embeddedCode.content.push([
258
- block.content,
259
- block.name,
260
- 0,
261
- FULL_CAPABILITIES
262
- ]);
263
- }
264
- };
265
- };
266
- var index_default = plugin;
267
- export {
268
- index_default as default
163
+ /**
164
+ * Volar 语言插件:为 weapp 配置块提供类型与 schema 提示。
165
+ */
166
+ const plugin = (ctx) => {
167
+ let tsModule = ctx?.modules?.typescript;
168
+ if (!tsModule) try {
169
+ tsModule = require("typescript");
170
+ } catch {
171
+ tsModule = void 0;
172
+ }
173
+ return {
174
+ name,
175
+ version: PLUGIN_VERSION,
176
+ order: -1,
177
+ parseSFC2(fileName, languageId, content) {
178
+ if (languageId !== "vue") return;
179
+ const parsed = parseVueSfc(content, fileName);
180
+ if (!parsed) return;
181
+ const descriptor = parsed.descriptor;
182
+ const wxsModuleNames = collectWxsModuleNames(descriptor.template?.content);
183
+ if (!wxsModuleNames.length) return parsed;
184
+ if (descriptor.scriptSetup) descriptor.scriptSetup.content = appendWxsDeclarations(descriptor.scriptSetup.content, wxsModuleNames);
185
+ else descriptor.scriptSetup = createSyntheticScriptSetup(wxsModuleNames);
186
+ return parsed;
187
+ },
188
+ getEmbeddedCodes(_, sfc) {
189
+ const names = [];
190
+ for (let i = 0; i < sfc.customBlocks.length; i++) {
191
+ const block = sfc.customBlocks[i];
192
+ if (block.type === BLOCK_TYPE) {
193
+ const normalizedLang = normalizeLang(block.lang);
194
+ if (normalizedLang === JS_LANG || normalizedLang === TS_LANG) {
195
+ names.push({
196
+ id: `${BLOCK_TYPE}_${i}`,
197
+ lang: TS_LANG
198
+ });
199
+ continue;
200
+ }
201
+ const embeddedLang = normalizedLang === JSON_LANG || normalizedLang === JSONC_LANG || normalizedLang === JSON5_LANG ? JSONC_LANG : JSON_LANG;
202
+ names.push({
203
+ id: `${BLOCK_TYPE}_${i}`,
204
+ lang: embeddedLang
205
+ });
206
+ }
207
+ }
208
+ return names;
209
+ },
210
+ resolveEmbeddedCode(fileName, sfc, embeddedCode) {
211
+ const match = embeddedCode.id.match(new RegExp(`^${BLOCK_TYPE}_(\\d+)$`));
212
+ if (!match) return;
213
+ const index = Number.parseInt(match[1]);
214
+ const block = sfc.customBlocks[index];
215
+ if (!block) return;
216
+ const normalizedLang = normalizeLang(block.lang);
217
+ const configType = inferConfigType(fileName);
218
+ if (!hasSchematicsTypes) {
219
+ embeddedCode.content.push([
220
+ block.content,
221
+ block.name,
222
+ 0,
223
+ FULL_CAPABILITIES
224
+ ]);
225
+ return;
226
+ }
227
+ if (normalizedLang === JS_LANG || normalizedLang === TS_LANG) {
228
+ const parsed = tsModule && findExportDefaultExpression(block.content, tsModule, normalizedLang);
229
+ if (parsed && hasSchematicsTypes) {
230
+ const typeImport = `import type { ${configType} as __WeappConfig } from '@weapp-core/schematics'\n`;
231
+ embeddedCode.content.push([
232
+ `${typeImport}const __weapp_defineConfig = <T extends __WeappConfig>(config: T) => config
233
+
234
+ `,
235
+ void 0,
236
+ 0,
237
+ VOID_CAPABILITIES
238
+ ]);
239
+ if (parsed.leading) embeddedCode.content.push([
240
+ parsed.leading,
241
+ block.name,
242
+ 0,
243
+ FULL_CAPABILITIES
244
+ ]);
245
+ embeddedCode.content.push([
246
+ "export default __weapp_defineConfig(",
247
+ void 0,
248
+ parsed.expressionStart,
249
+ VOID_CAPABILITIES
250
+ ]);
251
+ embeddedCode.content.push([
252
+ parsed.expression,
253
+ block.name,
254
+ parsed.expressionStart,
255
+ FULL_CAPABILITIES
256
+ ]);
257
+ embeddedCode.content.push([
258
+ ")",
259
+ void 0,
260
+ parsed.expressionEnd,
261
+ VOID_CAPABILITIES
262
+ ]);
263
+ if (parsed.trailing) embeddedCode.content.push([
264
+ parsed.trailing,
265
+ block.name,
266
+ parsed.expressionEnd,
267
+ FULL_CAPABILITIES
268
+ ]);
269
+ return;
270
+ }
271
+ embeddedCode.content.push([
272
+ block.content,
273
+ block.name,
274
+ 0,
275
+ FULL_CAPABILITIES
276
+ ]);
277
+ return;
278
+ }
279
+ const schema = getSchemaForType(configType);
280
+ if (schema && schema.$id && !block.content.includes("$schema")) {
281
+ embeddedCode.content.push([
282
+ injectSchemaIntoJsonObject(block.content, schema.$id),
283
+ block.name,
284
+ 0,
285
+ FULL_CAPABILITIES
286
+ ]);
287
+ return;
288
+ }
289
+ embeddedCode.content.push([
290
+ block.content,
291
+ block.name,
292
+ 0,
293
+ FULL_CAPABILITIES
294
+ ]);
295
+ }
296
+ };
269
297
  };
298
+ //#endregion
299
+ export { plugin as default };
@@ -0,0 +1,26 @@
1
+ export interface JsonSchema {
2
+ $schema?: string;
3
+ $id?: string;
4
+ title?: string;
5
+ description?: string;
6
+ type: string;
7
+ properties?: Record<string, any>;
8
+ required?: string[];
9
+ additionalProperties?: boolean | any;
10
+ items?: any;
11
+ definitions?: Record<string, any>;
12
+ enum?: string[];
13
+ minimum?: number;
14
+ maximum?: number;
15
+ minItems?: number;
16
+ maxItems?: number;
17
+ }
18
+ /**
19
+ * 根据文件类型获取对应的 JSON Schema
20
+ * Schema 定义来自 @weapp-core/schematics,使用 Zod 维护单一数据源
21
+ */
22
+ export declare function getSchemaForType(type: 'App' | 'Page' | 'Component' | 'Plugin' | 'Sitemap' | 'Theme'): JsonSchema | null;
23
+ /**
24
+ * 为配置块生成 schema 注释
25
+ */
26
+ export declare function generateSchemaComment(type: 'App' | 'Page' | 'Component' | 'Plugin' | 'Sitemap' | 'Theme'): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weapp-vite/volar",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Volar plugin for weapp-vite - Provides IntelliSense and type checking for WeChat mini-program config blocks",
5
5
  "author": "ice breaker <1324318532@qq.com>",
6
6
  "license": "MIT",
@@ -25,14 +25,14 @@
25
25
  "sideEffects": false,
26
26
  "exports": {
27
27
  ".": {
28
- "types": "./dist/index.d.mts",
28
+ "types": "./dist/index.d.ts",
29
29
  "import": "./dist/index.mjs",
30
30
  "require": "./dist/index.cjs"
31
31
  }
32
32
  },
33
33
  "main": "./dist/index.cjs",
34
34
  "module": "./dist/index.mjs",
35
- "types": "./dist/index.d.mts",
35
+ "types": "./dist/index.d.ts",
36
36
  "files": [
37
37
  "dist"
38
38
  ],
@@ -40,13 +40,14 @@
40
40
  "node": "^20.19.0 || >=22.12.0"
41
41
  },
42
42
  "dependencies": {
43
- "@weapp-core/schematics": "6.0.2"
43
+ "@weapp-core/schematics": "6.0.3"
44
44
  },
45
45
  "scripts": {
46
- "dev": "tsup --watch --sourcemap",
47
- "build": "tsup",
46
+ "dev": "tsdown -w --sourcemap",
47
+ "build": "tsdown --no-dts && tsc -p tsconfig.build.json",
48
48
  "test": "vitest run",
49
49
  "test:dev": "vitest",
50
+ "typecheck": "tsc --noEmit",
50
51
  "release": "pnpm publish",
51
52
  "lint": "eslint .",
52
53
  "lint:fix": "eslint . --fix"
package/dist/index.d.mts DELETED
@@ -1,8 +0,0 @@
1
- import { VueLanguagePlugin } from '@vue/language-core';
2
-
3
- /**
4
- * Volar 语言插件:为 weapp 配置块提供类型与 schema 提示。
5
- */
6
- declare const plugin: VueLanguagePlugin;
7
-
8
- export { plugin as default };