@lytjs/plugin-vite 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var sfc = require('@lytjs/compiler/sfc');
6
+
7
+ // src/options.ts
8
+ var defaultOptions = {
9
+ include: [/\.lyt$/],
10
+ exclude: [/node_modules/, /\.git/],
11
+ ssr: false,
12
+ signalMode: false,
13
+ enableVaporHMR: false
14
+ };
15
+ function resolveOptions(options) {
16
+ return {
17
+ include: options.include ?? defaultOptions.include,
18
+ exclude: options.exclude ?? defaultOptions.exclude,
19
+ ssr: options.ssr ?? defaultOptions.ssr,
20
+ signalMode: options.signalMode ?? defaultOptions.signalMode,
21
+ enableVaporHMR: options.enableVaporHMR ?? defaultOptions.enableVaporHMR
22
+ };
23
+ }
24
+
25
+ // src/utils.ts
26
+ function createFilter(include, exclude) {
27
+ const includes = Array.isArray(include) ? include : include ? [include] : [];
28
+ const excludes = Array.isArray(exclude) ? exclude : exclude ? [exclude] : [];
29
+ return (id) => {
30
+ for (const pattern of excludes) {
31
+ if (pattern.test(id)) return false;
32
+ }
33
+ if (includes.length === 0) return true;
34
+ for (const pattern of includes) {
35
+ if (pattern.test(id)) return true;
36
+ }
37
+ return false;
38
+ };
39
+ }
40
+ function generateScopeId(filename) {
41
+ const hash = filename.split("").reduce((acc, char) => {
42
+ return (acc << 5) - acc + char.charCodeAt(0) | 0;
43
+ }, 0);
44
+ return `data-v-${Math.abs(hash).toString(36).substring(0, 8)}`;
45
+ }
46
+ var hmrCache = /* @__PURE__ */ new Map();
47
+ var routeBlockMap = /* @__PURE__ */ new Map();
48
+ var vaporComponentIdMap = /* @__PURE__ */ new Map();
49
+ function lytjs(rawOptions = {}) {
50
+ const options = resolveOptions(rawOptions);
51
+ const filter = createFilter(options.include, options.exclude);
52
+ let isProduction = false;
53
+ return {
54
+ name: "@lytjs/plugin-vite",
55
+ enforce: "pre",
56
+ config(_userConfig, env) {
57
+ isProduction = env.mode === "production";
58
+ return {
59
+ esbuild: {
60
+ include: [/\.ts$/, /\.tsx$/],
61
+ exclude: [/\.lyt$/]
62
+ },
63
+ optimizeDeps: {
64
+ exclude: ["@lytjs/core", "@lytjs/compiler"]
65
+ }
66
+ };
67
+ },
68
+ resolveId(id) {
69
+ if (filter(id)) {
70
+ return id;
71
+ }
72
+ if (id.endsWith(".route")) {
73
+ return `\0virtual-route:${id}`;
74
+ }
75
+ return null;
76
+ },
77
+ load(id) {
78
+ if (!filter(id)) {
79
+ if (id.startsWith("\0virtual-route:")) {
80
+ const sourceId = id.replace("\0virtual-route:", "");
81
+ const routeContent = routeBlockMap.get(sourceId);
82
+ if (routeContent) {
83
+ return `export default ${routeContent}`;
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+ return null;
89
+ },
90
+ transform(code, id) {
91
+ if (!filter(id)) return null;
92
+ try {
93
+ const descriptor = sfc.parseSFC(code, { filename: id });
94
+ const routeBlock = descriptor.customBlocks.find(
95
+ (block) => block.type === "route"
96
+ );
97
+ if (routeBlock) {
98
+ routeBlockMap.set(id, routeBlock.content);
99
+ }
100
+ const result = sfc.compileSFC(descriptor, {
101
+ filename: id,
102
+ ssr: options.ssr,
103
+ rendererMode: options.signalMode ? "signal" : "vnode"
104
+ });
105
+ let compiledCode = result.code;
106
+ const scopedStyles = descriptor.styles.filter((s) => s.scoped);
107
+ if (scopedStyles.length > 0) {
108
+ const scopeId = generateScopeId(id);
109
+ compiledCode = compiledCode.replace(
110
+ /(export default\s*\{)/,
111
+ `$1
112
+ __scopeId: '${scopeId}',`
113
+ );
114
+ }
115
+ if (!isProduction) {
116
+ hmrCache.set(id, {
117
+ script: descriptor.script?.content || "",
118
+ template: descriptor.template?.content || "",
119
+ styles: descriptor.styles.map((s) => s.content),
120
+ isVapor: options.signalMode || false
121
+ });
122
+ }
123
+ if (!isProduction) {
124
+ if (options.signalMode && options.enableVaporHMR !== false) {
125
+ const componentId = `vapor-${id.replace(/[^a-zA-Z0-9]/g, "_")}`;
126
+ vaporComponentIdMap.set(id, componentId);
127
+ compiledCode += `
128
+ if (import.meta.hot) {
129
+ import.meta.hot.accept((newModule) => {
130
+ if (newModule && newModule.default) {
131
+ // Vapor HMR: \u66F4\u65B0\u7EC4\u4EF6\u5B9A\u4E49\uFF0C\u4FDD\u7559\u72B6\u6001
132
+ const registry = window.__LYTJS_HMR_REGISTRY__;
133
+ if (registry) {
134
+ const instance = registry.get('${componentId}');
135
+ if (instance) {
136
+ instance.component = newModule.default;
137
+ // Signal \u6A21\u5F0F\u4F1A\u81EA\u52A8\u901A\u8FC7 effect \u91CD\u65B0\u6E32\u67D3
138
+ console.log('[LytJS HMR] Vapor component updated: ${id}');
139
+ }
140
+ }
141
+ }
142
+ });
143
+ }
144
+ `;
145
+ } else {
146
+ compiledCode += "\nif (import.meta.hot) { import.meta.hot.accept(); }\n";
147
+ }
148
+ }
149
+ return {
150
+ code: compiledCode,
151
+ map: result.sourceMap
152
+ };
153
+ } catch (error) {
154
+ console.error(`[@lytjs/plugin-vite] Error compiling ${id}:`, error);
155
+ if (!isProduction) {
156
+ return {
157
+ code: `
158
+ export default function ErrorComponent() {
159
+ throw new Error(${JSON.stringify(
160
+ error instanceof Error ? error.message : String(error)
161
+ )});
162
+ }
163
+ `,
164
+ map: null
165
+ };
166
+ }
167
+ throw error;
168
+ }
169
+ },
170
+ async handleHotUpdate(ctx) {
171
+ const { file, server, modules } = ctx;
172
+ if (!filter(file)) return;
173
+ const prevCache = hmrCache.get(file);
174
+ if (!prevCache) return modules;
175
+ try {
176
+ const content = await ctx.read();
177
+ const descriptor = sfc.parseSFC(content, { filename: file });
178
+ const newCache = {
179
+ script: descriptor.script?.content || "",
180
+ template: descriptor.template?.content || "",
181
+ styles: descriptor.styles.map((s) => s.content),
182
+ isVapor: prevCache.isVapor
183
+ };
184
+ const scriptChanged = prevCache.script !== newCache.script;
185
+ const templateChanged = prevCache.template !== newCache.template;
186
+ const stylesChanged = prevCache.styles.length !== newCache.styles.length || prevCache.styles.some((s, i) => s !== newCache.styles[i]);
187
+ hmrCache.set(file, newCache);
188
+ if (scriptChanged) {
189
+ server.ws.send({
190
+ type: "full-reload",
191
+ path: "*"
192
+ });
193
+ return [];
194
+ }
195
+ if (templateChanged && !stylesChanged) {
196
+ return modules;
197
+ }
198
+ if (stylesChanged && !templateChanged) {
199
+ return modules;
200
+ }
201
+ return modules;
202
+ } catch (error) {
203
+ console.error(`[@lytjs/plugin-vite] HMR error for ${file}:`, error);
204
+ return modules;
205
+ }
206
+ }
207
+ };
208
+ }
209
+
210
+ exports.default = lytjs;
211
+ exports.defaultOptions = defaultOptions;
212
+ exports.resolveOptions = resolveOptions;
213
+ //# sourceMappingURL=index.cjs.map
214
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/options.ts","../src/utils.ts","../src/index.ts"],"names":["parseSFC","compileSFC"],"mappings":";;;;;;;AAgBO,IAAM,cAAA,GAA+C;AAAA,EAC1D,OAAA,EAAS,CAAC,QAAQ,CAAA;AAAA,EAClB,OAAA,EAAS,CAAC,cAAA,EAAgB,OAAO,CAAA;AAAA,EACjC,GAAA,EAAK,KAAA;AAAA,EACL,UAAA,EAAY,KAAA;AAAA,EACZ,cAAA,EAAgB;AAClB;AAKO,SAAS,eAAe,OAAA,EAA2D;AACxF,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,cAAA,CAAe,OAAA;AAAA,IAC3C,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,cAAA,CAAe,OAAA;AAAA,IAC3C,GAAA,EAAK,OAAA,CAAQ,GAAA,IAAO,cAAA,CAAe,GAAA;AAAA,IACnC,UAAA,EAAY,OAAA,CAAQ,UAAA,IAAc,cAAA,CAAe,UAAA;AAAA,IACjD,cAAA,EAAgB,OAAA,CAAQ,cAAA,IAAkB,cAAA,CAAe;AAAA,GAC3D;AACF;;;AC1BO,SAAS,YAAA,CACd,SACA,OAAA,EACyB;AACzB,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,UAAU,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI,EAAC;AAC3E,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,UAAU,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI,EAAC;AAE3E,EAAA,OAAO,CAAC,EAAA,KAAwB;AAE9B,IAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,MAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA,EAAG,OAAO,KAAA;AAAA,IAC/B;AAGA,IAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAElC,IAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,MAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAA,EAAG,OAAO,IAAA;AAAA,IAC/B;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF;AAYO,SAAS,gBAAgB,QAAA,EAA0B;AACxD,EAAA,MAAM,IAAA,GAAO,SAAS,KAAA,CAAM,EAAE,EAAE,MAAA,CAAO,CAAC,KAAK,IAAA,KAAS;AACpD,IAAA,OAAA,CAAS,OAAO,CAAA,IAAK,GAAA,GAAM,IAAA,CAAK,UAAA,CAAW,CAAC,CAAA,GAAK,CAAA;AAAA,EACnD,GAAG,CAAC,CAAA;AACJ,EAAA,OAAO,CAAA,OAAA,EAAU,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAC9D;AChCA,IAAM,QAAA,uBAAe,GAAA,EAAsF;AAG3G,IAAM,aAAA,uBAAoB,GAAA,EAAoB;AAG9C,IAAM,mBAAA,uBAA0B,GAAA,EAAoB;AAKrC,SAAR,KAAA,CAAuB,UAAA,GAAiC,EAAC,EAAW;AACzE,EAAA,MAAM,OAAA,GAAU,eAAe,UAAU,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,OAAA,CAAQ,OAAA,EAAS,QAAQ,OAAO,CAAA;AAC5D,EAAA,IAAI,YAAA,GAAe,KAAA;AAEnB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,oBAAA;AAAA,IACN,OAAA,EAAS,KAAA;AAAA,IAET,MAAA,CAAO,aAAa,GAAA,EAAK;AACvB,MAAA,YAAA,GAAe,IAAI,IAAA,KAAS,YAAA;AAE5B,MAAA,OAAO;AAAA,QACL,OAAA,EAAS;AAAA,UACP,OAAA,EAAS,CAAC,OAAA,EAAS,QAAQ,CAAA;AAAA,UAC3B,OAAA,EAAS,CAAC,QAAQ;AAAA,SACpB;AAAA,QACA,YAAA,EAAc;AAAA,UACZ,OAAA,EAAS,CAAC,aAAA,EAAe,iBAAiB;AAAA;AAC5C,OACF;AAAA,IACF,CAAA;AAAA,IAEA,UAAU,EAAA,EAAI;AAEZ,MAAA,IAAI,MAAA,CAAO,EAAE,CAAA,EAAG;AACd,QAAA,OAAO,EAAA;AAAA,MACT;AAEA,MAAA,IAAI,EAAA,CAAG,QAAA,CAAS,QAAQ,CAAA,EAAG;AACzB,QAAA,OAAO,mBAAmB,EAAE,CAAA,CAAA;AAAA,MAC9B;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IAEA,KAAK,EAAA,EAAI;AACP,MAAA,IAAI,CAAC,MAAA,CAAO,EAAE,CAAA,EAAG;AAEf,QAAA,IAAI,EAAA,CAAG,UAAA,CAAW,kBAAkB,CAAA,EAAG;AACrC,UAAA,MAAM,QAAA,GAAW,EAAA,CAAG,OAAA,CAAQ,kBAAA,EAAoB,EAAE,CAAA;AAClD,UAAA,MAAM,YAAA,GAAe,aAAA,CAAc,GAAA,CAAI,QAAQ,CAAA;AAC/C,UAAA,IAAI,YAAA,EAAc;AAChB,YAAA,OAAO,kBAAkB,YAAY,CAAA,CAAA;AAAA,UACvC;AAAA,QACF;AACA,QAAA,OAAO,IAAA;AAAA,MACT;AAEA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IAEA,SAAA,CAAU,MAAM,EAAA,EAAI;AAClB,MAAA,IAAI,CAAC,MAAA,CAAO,EAAE,CAAA,EAAG,OAAO,IAAA;AAExB,MAAA,IAAI;AAEF,QAAA,MAAM,aAAaA,YAAA,CAAS,IAAA,EAAM,EAAE,QAAA,EAAU,IAAI,CAAA;AAGlD,QAAA,MAAM,UAAA,GAAa,WAAW,YAAA,CAAa,IAAA;AAAA,UACzC,CAAC,KAAA,KAA0B,KAAA,CAAM,IAAA,KAAS;AAAA,SAC5C;AACA,QAAA,IAAI,UAAA,EAAY;AAEd,UAAA,aAAA,CAAc,GAAA,CAAI,EAAA,EAAI,UAAA,CAAW,OAAO,CAAA;AAAA,QAC1C;AAGA,QAAA,MAAM,MAAA,GAASC,eAAW,UAAA,EAAY;AAAA,UACpC,QAAA,EAAU,EAAA;AAAA,UACV,KAAK,OAAA,CAAQ,GAAA;AAAA,UACb,YAAA,EAAc,OAAA,CAAQ,UAAA,GAAa,QAAA,GAAW;AAAA,SAC/C,CAAA;AAED,QAAA,IAAI,eAAe,MAAA,CAAO,IAAA;AAG1B,QAAA,MAAM,eAAe,UAAA,CAAW,MAAA,CAAO,OAAO,CAAC,CAAA,KAAqB,EAAE,MAAM,CAAA;AAC5E,QAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,UAAA,MAAM,OAAA,GAAU,gBAAgB,EAAE,CAAA;AAElC,UAAA,YAAA,GAAe,YAAA,CAAa,OAAA;AAAA,YAC1B,uBAAA;AAAA,YACA,CAAA;AAAA,cAAA,EAAqB,OAAO,CAAA,EAAA;AAAA,WAC9B;AAAA,QACF;AAGA,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,QAAA,CAAS,IAAI,EAAA,EAAI;AAAA,YACf,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,OAAA,IAAW,EAAA;AAAA,YACtC,QAAA,EAAU,UAAA,CAAW,QAAA,EAAU,OAAA,IAAW,EAAA;AAAA,YAC1C,QAAQ,UAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAA,KAAqB,EAAE,OAAO,CAAA;AAAA,YAC7D,OAAA,EAAS,QAAQ,UAAA,IAAc;AAAA,WAChC,CAAA;AAAA,QACH;AAGA,QAAA,IAAI,CAAC,YAAA,EAAc;AAEjB,UAAA,IAAI,OAAA,CAAQ,UAAA,IAAc,OAAA,CAAQ,cAAA,KAAmB,KAAA,EAAO;AAC1D,YAAA,MAAM,cAAc,CAAA,MAAA,EAAS,EAAA,CAAG,OAAA,CAAQ,eAAA,EAAiB,GAAG,CAAC,CAAA,CAAA;AAC7D,YAAA,mBAAA,CAAoB,GAAA,CAAI,IAAI,WAAW,CAAA;AACvC,YAAA,YAAA,IAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAAA,EAOa,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA,4DAAA,EAIU,EAAE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAAA,UAOtD,CAAA,MAAO;AACL,YAAA,YAAA,IAAgB,wDAAA;AAAA,UAClB;AAAA,QACF;AAEA,QAAA,OAAO;AAAA,UACL,IAAA,EAAM,YAAA;AAAA,UACN,KAAK,MAAA,CAAO;AAAA,SACd;AAAA,MACF,SAAS,KAAA,EAAO;AAEd,QAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,qCAAA,EAAwC,EAAE,CAAA,CAAA,CAAA,EAAK,KAAK,CAAA;AAGlE,QAAA,IAAI,CAAC,YAAA,EAAc;AACjB,UAAA,OAAO;AAAA,YACL,IAAA,EAAM;AAAA;AAAA,gCAAA,EAEgB,IAAA,CAAK,SAAA;AAAA,cACrB,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,aACtD,CAAA;AAAA;AAAA,YAAA,CAAA;AAAA,YAGL,GAAA,EAAK;AAAA,WACP;AAAA,QACF;AAEA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,gBAAgB,GAAA,EAAiB;AACrC,MAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAQ,GAAI,GAAA;AAElC,MAAA,IAAI,CAAC,MAAA,CAAO,IAAI,CAAA,EAAG;AAEnB,MAAA,MAAM,SAAA,GAAY,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA;AACnC,MAAA,IAAI,CAAC,WAAW,OAAO,OAAA;AAEvB,MAAA,IAAI;AAEF,QAAA,MAAM,OAAA,GAAU,MAAM,GAAA,CAAI,IAAA,EAAK;AAC/B,QAAA,MAAM,aAAaD,YAAA,CAAS,OAAA,EAAS,EAAE,QAAA,EAAU,MAAM,CAAA;AAEvD,QAAA,MAAM,QAAA,GAAW;AAAA,UACf,MAAA,EAAQ,UAAA,CAAW,MAAA,EAAQ,OAAA,IAAW,EAAA;AAAA,UACtC,QAAA,EAAU,UAAA,CAAW,QAAA,EAAU,OAAA,IAAW,EAAA;AAAA,UAC1C,QAAQ,UAAA,CAAW,MAAA,CAAO,IAAI,CAAC,CAAA,KAAqB,EAAE,OAAO,CAAA;AAAA,UAC7D,SAAS,SAAA,CAAU;AAAA,SACrB;AAGA,QAAA,MAAM,aAAA,GAAgB,SAAA,CAAU,MAAA,KAAW,QAAA,CAAS,MAAA;AACpD,QAAA,MAAM,eAAA,GAAkB,SAAA,CAAU,QAAA,KAAa,QAAA,CAAS,QAAA;AACxD,QAAA,MAAM,gBACJ,SAAA,CAAU,MAAA,CAAO,MAAA,KAAW,QAAA,CAAS,OAAO,MAAA,IAC5C,SAAA,CAAU,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,EAAW,CAAA,KAAc,MAAM,QAAA,CAAS,MAAA,CAAO,CAAC,CAAC,CAAA;AAG1E,QAAA,QAAA,CAAS,GAAA,CAAI,MAAM,QAAQ,CAAA;AAG3B,QAAA,IAAI,aAAA,EAAe;AACjB,UAAA,MAAA,CAAO,GAAG,IAAA,CAAK;AAAA,YACb,IAAA,EAAM,aAAA;AAAA,YACN,IAAA,EAAM;AAAA,WACP,CAAA;AACD,UAAA,OAAO,EAAC;AAAA,QACV;AAGA,QAAA,IAAI,eAAA,IAAmB,CAAC,aAAA,EAAe;AAErC,UAAA,OAAO,OAAA;AAAA,QACT;AAGA,QAAA,IAAI,aAAA,IAAiB,CAAC,eAAA,EAAiB;AAErC,UAAA,OAAO,OAAA;AAAA,QACT;AAGA,QAAA,OAAO,OAAA;AAAA,MACT,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,mCAAA,EAAsC,IAAI,CAAA,CAAA,CAAA,EAAK,KAAK,CAAA;AAClE,QAAA,OAAO,OAAA;AAAA,MACT;AAAA,IACF;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/plugin-vite - Options and configuration\r\n */\r\n\r\nexport interface LytjsPluginOptions {\r\n include?: RegExp | RegExp[];\r\n exclude?: RegExp | RegExp[];\r\n ssr?: boolean;\r\n signalMode?: boolean;\r\n /** Phase 1.2: 启用 Vapor HMR 支持 */\r\n enableVaporHMR?: boolean;\r\n}\r\n\r\n/**\r\n * Default plugin options\r\n */\r\nexport const defaultOptions: Required<LytjsPluginOptions> = {\r\n include: [/\\.lyt$/],\r\n exclude: [/node_modules/, /\\.git/],\r\n ssr: false,\r\n signalMode: false,\r\n enableVaporHMR: false,\r\n};\r\n\r\n/**\r\n * Resolve user options with defaults\r\n */\r\nexport function resolveOptions(options: LytjsPluginOptions): Required<LytjsPluginOptions> {\r\n return {\r\n include: options.include ?? defaultOptions.include,\r\n exclude: options.exclude ?? defaultOptions.exclude,\r\n ssr: options.ssr ?? defaultOptions.ssr,\r\n signalMode: options.signalMode ?? defaultOptions.signalMode,\r\n enableVaporHMR: options.enableVaporHMR ?? defaultOptions.enableVaporHMR,\r\n };\r\n}\r\n\r\n/**\r\n * Validate plugin options\r\n */\r\nexport function validateOptions(options: LytjsPluginOptions): void {\r\n if (options.include && !Array.isArray(options.include) && !(options.include instanceof RegExp)) {\r\n throw new TypeError('[@lytjs/plugin-vite] Option \"include\" must be a RegExp or an array of RegExp');\r\n }\r\n if (options.exclude && !Array.isArray(options.exclude) && !(options.exclude instanceof RegExp)) {\r\n throw new TypeError('[@lytjs/plugin-vite] Option \"exclude\" must be a RegExp or an array of RegExp');\r\n }\r\n}\r\n","/**\r\n * @lytjs/plugin-vite - Utility functions\r\n */\r\n\r\nimport type { LytjsPluginOptions } from './options';\r\n\r\n/**\r\n * Create a filter function for file matching\r\n */\r\nexport function createFilter(\r\n include: LytjsPluginOptions['include'],\r\n exclude: LytjsPluginOptions['exclude'],\r\n): (id: string) => boolean {\r\n const includes = Array.isArray(include) ? include : include ? [include] : [];\r\n const excludes = Array.isArray(exclude) ? exclude : exclude ? [exclude] : [];\r\n\r\n return (id: string): boolean => {\r\n // Check excludes first\r\n for (const pattern of excludes) {\r\n if (pattern.test(id)) return false;\r\n }\r\n\r\n // Check includes\r\n if (includes.length === 0) return true; // Include all if no patterns specified\r\n\r\n for (const pattern of includes) {\r\n if (pattern.test(id)) return true;\r\n }\r\n\r\n return false;\r\n };\r\n}\r\n\r\n/**\r\n * Normalize path for consistent matching\r\n */\r\nexport function normalizePath(path: string): string {\r\n return path.replace(/\\\\/g, '/');\r\n}\r\n\r\n/**\r\n * Generate a scope ID for scoped styles\r\n */\r\nexport function generateScopeId(filename: string): string {\r\n const hash = filename.split('').reduce((acc, char) => {\r\n return ((acc << 5) - acc + char.charCodeAt(0)) | 0;\r\n }, 0);\r\n return `data-v-${Math.abs(hash).toString(36).substring(0, 8)}`;\r\n}\r\n","/**\r\n * @lytjs/plugin-vite\r\n *\r\n * LytJS official Vite plugin for SFC compilation, HMR, and build optimizations.\r\n *\r\n * @packageDocumentation\r\n */\r\n\r\nimport type { Plugin, HmrContext } from 'vite';\r\nimport type { LytjsPluginOptions } from './options';\r\nimport type { SFCStyleBlock, SFCCustomBlock } from '@lytjs/compiler/sfc';\r\nimport { resolveOptions, defaultOptions } from './options';\r\nimport { createFilter, generateScopeId } from './utils';\r\nimport { parseSFC, compileSFC } from '@lytjs/compiler/sfc';\r\n\r\n// HMR cache for tracking component updates\r\nconst hmrCache = new Map<string, { script: string; template: string; styles: string[]; isVapor: boolean }>();\r\n\r\n// Route block collection\r\nconst routeBlockMap = new Map<string, string>();\r\n\r\n// Vapor component ID map (Phase 1.2)\r\nconst vaporComponentIdMap = new Map<string, string>();\r\n\r\n/**\r\n * Create the LytJS Vite plugin\r\n */\r\nexport default function lytjs(rawOptions: LytjsPluginOptions = {}): Plugin {\r\n const options = resolveOptions(rawOptions);\r\n const filter = createFilter(options.include, options.exclude);\r\n let isProduction = false;\r\n\r\n return {\r\n name: '@lytjs/plugin-vite',\r\n enforce: 'pre',\r\n\r\n config(_userConfig, env) {\r\n isProduction = env.mode === 'production';\r\n\r\n return {\r\n esbuild: {\r\n include: [/\\.ts$/, /\\.tsx$/],\r\n exclude: [/\\.lyt$/],\r\n },\r\n optimizeDeps: {\r\n exclude: ['@lytjs/core', '@lytjs/compiler'],\r\n },\r\n };\r\n },\r\n\r\n resolveId(id) {\r\n // Handle .lyt imports\r\n if (filter(id)) {\r\n return id;\r\n }\r\n // Handle .route virtual modules\r\n if (id.endsWith('.route')) {\r\n return `\\0virtual-route:${id}`;\r\n }\r\n return null;\r\n },\r\n\r\n load(id) {\r\n if (!filter(id)) {\r\n // Handle virtual route modules\r\n if (id.startsWith('\\0virtual-route:')) {\r\n const sourceId = id.replace('\\0virtual-route:', '');\r\n const routeContent = routeBlockMap.get(sourceId);\r\n if (routeContent) {\r\n return `export default ${routeContent}`;\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n return null;\r\n },\r\n\r\n transform(code, id) {\r\n if (!filter(id)) return null;\r\n\r\n try {\r\n // Parse the SFC\r\n const descriptor = parseSFC(code, { filename: id });\r\n\r\n // Handle <route> custom block\r\n const routeBlock = descriptor.customBlocks.find(\r\n (block: SFCCustomBlock) => block.type === 'route'\r\n );\r\n if (routeBlock) {\r\n // Store route info for later collection\r\n routeBlockMap.set(id, routeBlock.content);\r\n }\r\n\r\n // Generate the component code\r\n const result = compileSFC(descriptor, {\r\n filename: id,\r\n ssr: options.ssr,\r\n rendererMode: options.signalMode ? 'signal' : 'vnode',\r\n });\r\n\r\n let compiledCode = result.code;\r\n\r\n // Handle scoped styles - inject scope attribute\r\n const scopedStyles = descriptor.styles.filter((s: SFCStyleBlock) => s.scoped);\r\n if (scopedStyles.length > 0) {\r\n const scopeId = generateScopeId(id);\r\n // Inject __scopeId into the component setup\r\n compiledCode = compiledCode.replace(\r\n /(export default\\s*\\{)/,\r\n `$1\\n __scopeId: '${scopeId}',`\r\n );\r\n }\r\n\r\n // Cache for HMR\r\n if (!isProduction) {\r\n hmrCache.set(id, {\r\n script: descriptor.script?.content || '',\r\n template: descriptor.template?.content || '',\r\n styles: descriptor.styles.map((s: SFCStyleBlock) => s.content),\r\n isVapor: options.signalMode || false,\r\n });\r\n }\r\n\r\n // Add HMR accept for development\r\n if (!isProduction) {\r\n // Phase 1.2: Vapor HMR 支持\r\n if (options.signalMode && options.enableVaporHMR !== false) {\r\n const componentId = `vapor-${id.replace(/[^a-zA-Z0-9]/g, '_')}`;\r\n vaporComponentIdMap.set(id, componentId);\r\n compiledCode += `\r\nif (import.meta.hot) {\r\n import.meta.hot.accept((newModule) => {\r\n if (newModule && newModule.default) {\r\n // Vapor HMR: 更新组件定义,保留状态\r\n const registry = window.__LYTJS_HMR_REGISTRY__;\r\n if (registry) {\r\n const instance = registry.get('${componentId}');\r\n if (instance) {\r\n instance.component = newModule.default;\r\n // Signal 模式会自动通过 effect 重新渲染\r\n console.log('[LytJS HMR] Vapor component updated: ${id}');\r\n }\r\n }\r\n }\r\n });\r\n}\r\n`;\r\n } else {\r\n compiledCode += '\\nif (import.meta.hot) { import.meta.hot.accept(); }\\n';\r\n }\r\n }\r\n\r\n return {\r\n code: compiledCode,\r\n map: result.sourceMap as any,\r\n };\r\n } catch (error) {\r\n // Log compilation errors\r\n console.error(`[@lytjs/plugin-vite] Error compiling ${id}:`, error);\r\n\r\n // Return error overlay in development\r\n if (!isProduction) {\r\n return {\r\n code: `\r\n export default function ErrorComponent() {\r\n throw new Error(${JSON.stringify(\r\n error instanceof Error ? error.message : String(error)\r\n )});\r\n }\r\n `,\r\n map: null,\r\n };\r\n }\r\n\r\n throw error;\r\n }\r\n },\r\n\r\n async handleHotUpdate(ctx: HmrContext) {\r\n const { file, server, modules } = ctx;\r\n\r\n if (!filter(file)) return;\r\n\r\n const prevCache = hmrCache.get(file);\r\n if (!prevCache) return modules;\r\n\r\n try {\r\n // Read the updated file content\r\n const content = await ctx.read();\r\n const descriptor = parseSFC(content, { filename: file });\r\n\r\n const newCache = {\r\n script: descriptor.script?.content || '',\r\n template: descriptor.template?.content || '',\r\n styles: descriptor.styles.map((s: SFCStyleBlock) => s.content),\r\n isVapor: prevCache.isVapor,\r\n };\r\n\r\n // Determine what changed\r\n const scriptChanged = prevCache.script !== newCache.script;\r\n const templateChanged = prevCache.template !== newCache.template;\r\n const stylesChanged =\r\n prevCache.styles.length !== newCache.styles.length ||\r\n prevCache.styles.some((s: string, i: number) => s !== newCache.styles[i]);\r\n\r\n // Update cache\r\n hmrCache.set(file, newCache);\r\n\r\n // If script changed, full reload needed\r\n if (scriptChanged) {\r\n server.ws.send({\r\n type: 'full-reload',\r\n path: '*',\r\n });\r\n return [];\r\n }\r\n\r\n // If only template changed, we can do a hot update\r\n if (templateChanged && !stylesChanged) {\r\n // The transform hook will recompile the component\r\n return modules;\r\n }\r\n\r\n // If styles changed, we can handle CSS HMR separately\r\n if (stylesChanged && !templateChanged) {\r\n // Return the modules for CSS update\r\n return modules;\r\n }\r\n\r\n // Multiple changes, let Vite handle it\r\n return modules;\r\n } catch (error) {\r\n console.error(`[@lytjs/plugin-vite] HMR error for ${file}:`, error);\r\n return modules;\r\n }\r\n },\r\n };\r\n}\r\n\r\nexport type { LytjsPluginOptions };\r\nexport { defaultOptions, resolveOptions };\r\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,208 @@
1
+ import { parseSFC, compileSFC } from '@lytjs/compiler/sfc';
2
+
3
+ // src/options.ts
4
+ var defaultOptions = {
5
+ include: [/\.lyt$/],
6
+ exclude: [/node_modules/, /\.git/],
7
+ ssr: false,
8
+ signalMode: false,
9
+ enableVaporHMR: false
10
+ };
11
+ function resolveOptions(options) {
12
+ return {
13
+ include: options.include ?? defaultOptions.include,
14
+ exclude: options.exclude ?? defaultOptions.exclude,
15
+ ssr: options.ssr ?? defaultOptions.ssr,
16
+ signalMode: options.signalMode ?? defaultOptions.signalMode,
17
+ enableVaporHMR: options.enableVaporHMR ?? defaultOptions.enableVaporHMR
18
+ };
19
+ }
20
+
21
+ // src/utils.ts
22
+ function createFilter(include, exclude) {
23
+ const includes = Array.isArray(include) ? include : include ? [include] : [];
24
+ const excludes = Array.isArray(exclude) ? exclude : exclude ? [exclude] : [];
25
+ return (id) => {
26
+ for (const pattern of excludes) {
27
+ if (pattern.test(id)) return false;
28
+ }
29
+ if (includes.length === 0) return true;
30
+ for (const pattern of includes) {
31
+ if (pattern.test(id)) return true;
32
+ }
33
+ return false;
34
+ };
35
+ }
36
+ function generateScopeId(filename) {
37
+ const hash = filename.split("").reduce((acc, char) => {
38
+ return (acc << 5) - acc + char.charCodeAt(0) | 0;
39
+ }, 0);
40
+ return `data-v-${Math.abs(hash).toString(36).substring(0, 8)}`;
41
+ }
42
+ var hmrCache = /* @__PURE__ */ new Map();
43
+ var routeBlockMap = /* @__PURE__ */ new Map();
44
+ var vaporComponentIdMap = /* @__PURE__ */ new Map();
45
+ function lytjs(rawOptions = {}) {
46
+ const options = resolveOptions(rawOptions);
47
+ const filter = createFilter(options.include, options.exclude);
48
+ let isProduction = false;
49
+ return {
50
+ name: "@lytjs/plugin-vite",
51
+ enforce: "pre",
52
+ config(_userConfig, env) {
53
+ isProduction = env.mode === "production";
54
+ return {
55
+ esbuild: {
56
+ include: [/\.ts$/, /\.tsx$/],
57
+ exclude: [/\.lyt$/]
58
+ },
59
+ optimizeDeps: {
60
+ exclude: ["@lytjs/core", "@lytjs/compiler"]
61
+ }
62
+ };
63
+ },
64
+ resolveId(id) {
65
+ if (filter(id)) {
66
+ return id;
67
+ }
68
+ if (id.endsWith(".route")) {
69
+ return `\0virtual-route:${id}`;
70
+ }
71
+ return null;
72
+ },
73
+ load(id) {
74
+ if (!filter(id)) {
75
+ if (id.startsWith("\0virtual-route:")) {
76
+ const sourceId = id.replace("\0virtual-route:", "");
77
+ const routeContent = routeBlockMap.get(sourceId);
78
+ if (routeContent) {
79
+ return `export default ${routeContent}`;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ return null;
85
+ },
86
+ transform(code, id) {
87
+ if (!filter(id)) return null;
88
+ try {
89
+ const descriptor = parseSFC(code, { filename: id });
90
+ const routeBlock = descriptor.customBlocks.find(
91
+ (block) => block.type === "route"
92
+ );
93
+ if (routeBlock) {
94
+ routeBlockMap.set(id, routeBlock.content);
95
+ }
96
+ const result = compileSFC(descriptor, {
97
+ filename: id,
98
+ ssr: options.ssr,
99
+ rendererMode: options.signalMode ? "signal" : "vnode"
100
+ });
101
+ let compiledCode = result.code;
102
+ const scopedStyles = descriptor.styles.filter((s) => s.scoped);
103
+ if (scopedStyles.length > 0) {
104
+ const scopeId = generateScopeId(id);
105
+ compiledCode = compiledCode.replace(
106
+ /(export default\s*\{)/,
107
+ `$1
108
+ __scopeId: '${scopeId}',`
109
+ );
110
+ }
111
+ if (!isProduction) {
112
+ hmrCache.set(id, {
113
+ script: descriptor.script?.content || "",
114
+ template: descriptor.template?.content || "",
115
+ styles: descriptor.styles.map((s) => s.content),
116
+ isVapor: options.signalMode || false
117
+ });
118
+ }
119
+ if (!isProduction) {
120
+ if (options.signalMode && options.enableVaporHMR !== false) {
121
+ const componentId = `vapor-${id.replace(/[^a-zA-Z0-9]/g, "_")}`;
122
+ vaporComponentIdMap.set(id, componentId);
123
+ compiledCode += `
124
+ if (import.meta.hot) {
125
+ import.meta.hot.accept((newModule) => {
126
+ if (newModule && newModule.default) {
127
+ // Vapor HMR: \u66F4\u65B0\u7EC4\u4EF6\u5B9A\u4E49\uFF0C\u4FDD\u7559\u72B6\u6001
128
+ const registry = window.__LYTJS_HMR_REGISTRY__;
129
+ if (registry) {
130
+ const instance = registry.get('${componentId}');
131
+ if (instance) {
132
+ instance.component = newModule.default;
133
+ // Signal \u6A21\u5F0F\u4F1A\u81EA\u52A8\u901A\u8FC7 effect \u91CD\u65B0\u6E32\u67D3
134
+ console.log('[LytJS HMR] Vapor component updated: ${id}');
135
+ }
136
+ }
137
+ }
138
+ });
139
+ }
140
+ `;
141
+ } else {
142
+ compiledCode += "\nif (import.meta.hot) { import.meta.hot.accept(); }\n";
143
+ }
144
+ }
145
+ return {
146
+ code: compiledCode,
147
+ map: result.sourceMap
148
+ };
149
+ } catch (error) {
150
+ console.error(`[@lytjs/plugin-vite] Error compiling ${id}:`, error);
151
+ if (!isProduction) {
152
+ return {
153
+ code: `
154
+ export default function ErrorComponent() {
155
+ throw new Error(${JSON.stringify(
156
+ error instanceof Error ? error.message : String(error)
157
+ )});
158
+ }
159
+ `,
160
+ map: null
161
+ };
162
+ }
163
+ throw error;
164
+ }
165
+ },
166
+ async handleHotUpdate(ctx) {
167
+ const { file, server, modules } = ctx;
168
+ if (!filter(file)) return;
169
+ const prevCache = hmrCache.get(file);
170
+ if (!prevCache) return modules;
171
+ try {
172
+ const content = await ctx.read();
173
+ const descriptor = parseSFC(content, { filename: file });
174
+ const newCache = {
175
+ script: descriptor.script?.content || "",
176
+ template: descriptor.template?.content || "",
177
+ styles: descriptor.styles.map((s) => s.content),
178
+ isVapor: prevCache.isVapor
179
+ };
180
+ const scriptChanged = prevCache.script !== newCache.script;
181
+ const templateChanged = prevCache.template !== newCache.template;
182
+ const stylesChanged = prevCache.styles.length !== newCache.styles.length || prevCache.styles.some((s, i) => s !== newCache.styles[i]);
183
+ hmrCache.set(file, newCache);
184
+ if (scriptChanged) {
185
+ server.ws.send({
186
+ type: "full-reload",
187
+ path: "*"
188
+ });
189
+ return [];
190
+ }
191
+ if (templateChanged && !stylesChanged) {
192
+ return modules;
193
+ }
194
+ if (stylesChanged && !templateChanged) {
195
+ return modules;
196
+ }
197
+ return modules;
198
+ } catch (error) {
199
+ console.error(`[@lytjs/plugin-vite] HMR error for ${file}:`, error);
200
+ return modules;
201
+ }
202
+ }
203
+ };
204
+ }
205
+
206
+ export { lytjs as default, defaultOptions, resolveOptions };
207
+ //# sourceMappingURL=index.mjs.map
208
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/options.ts","../src/utils.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/plugin-vite - Options and configuration\r\n */\r\n\r\nexport interface LytjsPluginOptions {\r\n include?: RegExp | RegExp[];\r\n exclude?: RegExp | RegExp[];\r\n ssr?: boolean;\r\n signalMode?: boolean;\r\n /** Phase 1.2: 启用 Vapor HMR 支持 */\r\n enableVaporHMR?: boolean;\r\n}\r\n\r\n/**\r\n * Default plugin options\r\n */\r\nexport const defaultOptions: Required<LytjsPluginOptions> = {\r\n include: [/\\.lyt$/],\r\n exclude: [/node_modules/, /\\.git/],\r\n ssr: false,\r\n signalMode: false,\r\n enableVaporHMR: false,\r\n};\r\n\r\n/**\r\n * Resolve user options with defaults\r\n */\r\nexport function resolveOptions(options: LytjsPluginOptions): Required<LytjsPluginOptions> {\r\n return {\r\n include: options.include ?? defaultOptions.include,\r\n exclude: options.exclude ?? defaultOptions.exclude,\r\n ssr: options.ssr ?? defaultOptions.ssr,\r\n signalMode: options.signalMode ?? defaultOptions.signalMode,\r\n enableVaporHMR: options.enableVaporHMR ?? defaultOptions.enableVaporHMR,\r\n };\r\n}\r\n\r\n/**\r\n * Validate plugin options\r\n */\r\nexport function validateOptions(options: LytjsPluginOptions): void {\r\n if (options.include && !Array.isArray(options.include) && !(options.include instanceof RegExp)) {\r\n throw new TypeError('[@lytjs/plugin-vite] Option \"include\" must be a RegExp or an array of RegExp');\r\n }\r\n if (options.exclude && !Array.isArray(options.exclude) && !(options.exclude instanceof RegExp)) {\r\n throw new TypeError('[@lytjs/plugin-vite] Option \"exclude\" must be a RegExp or an array of RegExp');\r\n }\r\n}\r\n","/**\r\n * @lytjs/plugin-vite - Utility functions\r\n */\r\n\r\nimport type { LytjsPluginOptions } from './options';\r\n\r\n/**\r\n * Create a filter function for file matching\r\n */\r\nexport function createFilter(\r\n include: LytjsPluginOptions['include'],\r\n exclude: LytjsPluginOptions['exclude'],\r\n): (id: string) => boolean {\r\n const includes = Array.isArray(include) ? include : include ? [include] : [];\r\n const excludes = Array.isArray(exclude) ? exclude : exclude ? [exclude] : [];\r\n\r\n return (id: string): boolean => {\r\n // Check excludes first\r\n for (const pattern of excludes) {\r\n if (pattern.test(id)) return false;\r\n }\r\n\r\n // Check includes\r\n if (includes.length === 0) return true; // Include all if no patterns specified\r\n\r\n for (const pattern of includes) {\r\n if (pattern.test(id)) return true;\r\n }\r\n\r\n return false;\r\n };\r\n}\r\n\r\n/**\r\n * Normalize path for consistent matching\r\n */\r\nexport function normalizePath(path: string): string {\r\n return path.replace(/\\\\/g, '/');\r\n}\r\n\r\n/**\r\n * Generate a scope ID for scoped styles\r\n */\r\nexport function generateScopeId(filename: string): string {\r\n const hash = filename.split('').reduce((acc, char) => {\r\n return ((acc << 5) - acc + char.charCodeAt(0)) | 0;\r\n }, 0);\r\n return `data-v-${Math.abs(hash).toString(36).substring(0, 8)}`;\r\n}\r\n","/**\r\n * @lytjs/plugin-vite\r\n *\r\n * LytJS official Vite plugin for SFC compilation, HMR, and build optimizations.\r\n *\r\n * @packageDocumentation\r\n */\r\n\r\nimport type { Plugin, HmrContext } from 'vite';\r\nimport type { LytjsPluginOptions } from './options';\r\nimport type { SFCStyleBlock, SFCCustomBlock } from '@lytjs/compiler/sfc';\r\nimport { resolveOptions, defaultOptions } from './options';\r\nimport { createFilter, generateScopeId } from './utils';\r\nimport { parseSFC, compileSFC } from '@lytjs/compiler/sfc';\r\n\r\n// HMR cache for tracking component updates\r\nconst hmrCache = new Map<string, { script: string; template: string; styles: string[]; isVapor: boolean }>();\r\n\r\n// Route block collection\r\nconst routeBlockMap = new Map<string, string>();\r\n\r\n// Vapor component ID map (Phase 1.2)\r\nconst vaporComponentIdMap = new Map<string, string>();\r\n\r\n/**\r\n * Create the LytJS Vite plugin\r\n */\r\nexport default function lytjs(rawOptions: LytjsPluginOptions = {}): Plugin {\r\n const options = resolveOptions(rawOptions);\r\n const filter = createFilter(options.include, options.exclude);\r\n let isProduction = false;\r\n\r\n return {\r\n name: '@lytjs/plugin-vite',\r\n enforce: 'pre',\r\n\r\n config(_userConfig, env) {\r\n isProduction = env.mode === 'production';\r\n\r\n return {\r\n esbuild: {\r\n include: [/\\.ts$/, /\\.tsx$/],\r\n exclude: [/\\.lyt$/],\r\n },\r\n optimizeDeps: {\r\n exclude: ['@lytjs/core', '@lytjs/compiler'],\r\n },\r\n };\r\n },\r\n\r\n resolveId(id) {\r\n // Handle .lyt imports\r\n if (filter(id)) {\r\n return id;\r\n }\r\n // Handle .route virtual modules\r\n if (id.endsWith('.route')) {\r\n return `\\0virtual-route:${id}`;\r\n }\r\n return null;\r\n },\r\n\r\n load(id) {\r\n if (!filter(id)) {\r\n // Handle virtual route modules\r\n if (id.startsWith('\\0virtual-route:')) {\r\n const sourceId = id.replace('\\0virtual-route:', '');\r\n const routeContent = routeBlockMap.get(sourceId);\r\n if (routeContent) {\r\n return `export default ${routeContent}`;\r\n }\r\n }\r\n return null;\r\n }\r\n\r\n return null;\r\n },\r\n\r\n transform(code, id) {\r\n if (!filter(id)) return null;\r\n\r\n try {\r\n // Parse the SFC\r\n const descriptor = parseSFC(code, { filename: id });\r\n\r\n // Handle <route> custom block\r\n const routeBlock = descriptor.customBlocks.find(\r\n (block: SFCCustomBlock) => block.type === 'route'\r\n );\r\n if (routeBlock) {\r\n // Store route info for later collection\r\n routeBlockMap.set(id, routeBlock.content);\r\n }\r\n\r\n // Generate the component code\r\n const result = compileSFC(descriptor, {\r\n filename: id,\r\n ssr: options.ssr,\r\n rendererMode: options.signalMode ? 'signal' : 'vnode',\r\n });\r\n\r\n let compiledCode = result.code;\r\n\r\n // Handle scoped styles - inject scope attribute\r\n const scopedStyles = descriptor.styles.filter((s: SFCStyleBlock) => s.scoped);\r\n if (scopedStyles.length > 0) {\r\n const scopeId = generateScopeId(id);\r\n // Inject __scopeId into the component setup\r\n compiledCode = compiledCode.replace(\r\n /(export default\\s*\\{)/,\r\n `$1\\n __scopeId: '${scopeId}',`\r\n );\r\n }\r\n\r\n // Cache for HMR\r\n if (!isProduction) {\r\n hmrCache.set(id, {\r\n script: descriptor.script?.content || '',\r\n template: descriptor.template?.content || '',\r\n styles: descriptor.styles.map((s: SFCStyleBlock) => s.content),\r\n isVapor: options.signalMode || false,\r\n });\r\n }\r\n\r\n // Add HMR accept for development\r\n if (!isProduction) {\r\n // Phase 1.2: Vapor HMR 支持\r\n if (options.signalMode && options.enableVaporHMR !== false) {\r\n const componentId = `vapor-${id.replace(/[^a-zA-Z0-9]/g, '_')}`;\r\n vaporComponentIdMap.set(id, componentId);\r\n compiledCode += `\r\nif (import.meta.hot) {\r\n import.meta.hot.accept((newModule) => {\r\n if (newModule && newModule.default) {\r\n // Vapor HMR: 更新组件定义,保留状态\r\n const registry = window.__LYTJS_HMR_REGISTRY__;\r\n if (registry) {\r\n const instance = registry.get('${componentId}');\r\n if (instance) {\r\n instance.component = newModule.default;\r\n // Signal 模式会自动通过 effect 重新渲染\r\n console.log('[LytJS HMR] Vapor component updated: ${id}');\r\n }\r\n }\r\n }\r\n });\r\n}\r\n`;\r\n } else {\r\n compiledCode += '\\nif (import.meta.hot) { import.meta.hot.accept(); }\\n';\r\n }\r\n }\r\n\r\n return {\r\n code: compiledCode,\r\n map: result.sourceMap as any,\r\n };\r\n } catch (error) {\r\n // Log compilation errors\r\n console.error(`[@lytjs/plugin-vite] Error compiling ${id}:`, error);\r\n\r\n // Return error overlay in development\r\n if (!isProduction) {\r\n return {\r\n code: `\r\n export default function ErrorComponent() {\r\n throw new Error(${JSON.stringify(\r\n error instanceof Error ? error.message : String(error)\r\n )});\r\n }\r\n `,\r\n map: null,\r\n };\r\n }\r\n\r\n throw error;\r\n }\r\n },\r\n\r\n async handleHotUpdate(ctx: HmrContext) {\r\n const { file, server, modules } = ctx;\r\n\r\n if (!filter(file)) return;\r\n\r\n const prevCache = hmrCache.get(file);\r\n if (!prevCache) return modules;\r\n\r\n try {\r\n // Read the updated file content\r\n const content = await ctx.read();\r\n const descriptor = parseSFC(content, { filename: file });\r\n\r\n const newCache = {\r\n script: descriptor.script?.content || '',\r\n template: descriptor.template?.content || '',\r\n styles: descriptor.styles.map((s: SFCStyleBlock) => s.content),\r\n isVapor: prevCache.isVapor,\r\n };\r\n\r\n // Determine what changed\r\n const scriptChanged = prevCache.script !== newCache.script;\r\n const templateChanged = prevCache.template !== newCache.template;\r\n const stylesChanged =\r\n prevCache.styles.length !== newCache.styles.length ||\r\n prevCache.styles.some((s: string, i: number) => s !== newCache.styles[i]);\r\n\r\n // Update cache\r\n hmrCache.set(file, newCache);\r\n\r\n // If script changed, full reload needed\r\n if (scriptChanged) {\r\n server.ws.send({\r\n type: 'full-reload',\r\n path: '*',\r\n });\r\n return [];\r\n }\r\n\r\n // If only template changed, we can do a hot update\r\n if (templateChanged && !stylesChanged) {\r\n // The transform hook will recompile the component\r\n return modules;\r\n }\r\n\r\n // If styles changed, we can handle CSS HMR separately\r\n if (stylesChanged && !templateChanged) {\r\n // Return the modules for CSS update\r\n return modules;\r\n }\r\n\r\n // Multiple changes, let Vite handle it\r\n return modules;\r\n } catch (error) {\r\n console.error(`[@lytjs/plugin-vite] HMR error for ${file}:`, error);\r\n return modules;\r\n }\r\n },\r\n };\r\n}\r\n\r\nexport type { LytjsPluginOptions };\r\nexport { defaultOptions, resolveOptions };\r\n"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@lytjs/plugin-vite",
3
+ "version": "6.0.0",
4
+ "description": "LytJS official Vite plugin for SFC compilation, HMR, and build optimizations",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "test:coverage": "vitest run --coverage",
27
+ "type-check": "tsc --noEmit",
28
+ "lint": "eslint \"src/**/*.ts\"",
29
+ "clean": "rm -rf dist"
30
+ },
31
+ "dependencies": {
32
+ "@lytjs/compiler": "^6.0.0",
33
+ "@lytjs/common-is": "^6.0.0"
34
+ },
35
+ "peerDependencies": {
36
+ "vite": "^5.0.0 || ^6.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "tsup": "^8.0.0",
40
+ "typescript": "^5.4.0",
41
+ "vitest": "^3.0.0",
42
+ "vite": "^5.0.0"
43
+ },
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://gitee.com/lytjs/lytjs.git",
48
+ "directory": "packages/plugins/packages/plugin-vite"
49
+ },
50
+ "keywords": [
51
+ "lytjs",
52
+ "vite",
53
+ "plugin",
54
+ "sfc",
55
+ "hmr"
56
+ ]
57
+ }