@vizejs/vite-plugin 0.69.0 → 0.71.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.
Files changed (2) hide show
  1. package/dist/index.mjs +261 -11
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -72,6 +72,17 @@ if (import.meta.hot) {
72
72
  }
73
73
  //#endregion
74
74
  //#region src/utils/css.ts
75
+ const deepSelectorPattern = /:deep\(([^()]*(?:\([^()]*\))*[^()]*)\)/;
76
+ const globalSelectorPattern = /:global\(([^()]*(?:\([^()]*\))*[^()]*)\)/g;
77
+ const recursiveAtRules = new Set([
78
+ "@container",
79
+ "@layer",
80
+ "@media",
81
+ "@supports"
82
+ ]);
83
+ function scopeCssForPipeline(css, scopeId) {
84
+ return transformCssBlock(css, scopeId);
85
+ }
75
86
  /**
76
87
  * Resolve CSS @import statements by inlining the imported files,
77
88
  * then resolve @custom-media definitions within the combined CSS.
@@ -113,10 +124,227 @@ function resolveCssImports(css, importer, aliasRules, isDev, devUrlBase) {
113
124
  }
114
125
  return _match;
115
126
  });
116
- result = result.replace(/:deep\(([^()]*(?:\([^()]*\))*[^()]*)\)/g, "$1");
127
+ result = result.replace(new RegExp(deepSelectorPattern.source, "g"), "$1");
117
128
  result = result.replace(/\n{3,}/g, "\n\n");
118
129
  return result;
119
130
  }
131
+ function transformCssBlock(css, scopeId) {
132
+ let output = "";
133
+ let cursor = 0;
134
+ while (cursor < css.length) {
135
+ const brace = findNextTopLevelBrace(css, cursor);
136
+ if (brace === -1) {
137
+ output += css.slice(cursor);
138
+ break;
139
+ }
140
+ const end = findMatchingBrace(css, brace);
141
+ if (end === -1) {
142
+ output += css.slice(cursor);
143
+ break;
144
+ }
145
+ const header = css.slice(cursor, brace);
146
+ const body = css.slice(brace + 1, end);
147
+ const leadingLength = header.search(/\S/);
148
+ const leading = leadingLength === -1 ? header : header.slice(0, leadingLength);
149
+ const statement = leadingLength === -1 ? "" : header.slice(leadingLength);
150
+ output += leading;
151
+ if (statement.trimStart().startsWith("@")) {
152
+ output += statement;
153
+ output += "{";
154
+ output += shouldRecurseAtRule(statement) ? transformCssBlock(body, scopeId) : body;
155
+ output += "}";
156
+ } else {
157
+ output += scopeSelectorList(statement, scopeId);
158
+ output += "{";
159
+ output += body;
160
+ output += "}";
161
+ }
162
+ cursor = end + 1;
163
+ }
164
+ return output;
165
+ }
166
+ function shouldRecurseAtRule(statement) {
167
+ const name = statement.trimStart().split(/\s+/, 1)[0];
168
+ return name !== void 0 && recursiveAtRules.has(name);
169
+ }
170
+ function findNextTopLevelBrace(css, start) {
171
+ let parenDepth = 0;
172
+ let bracketDepth = 0;
173
+ let quote = null;
174
+ let inComment = false;
175
+ for (let index = start; index < css.length; index += 1) {
176
+ const char = css[index];
177
+ const next = css[index + 1];
178
+ if (inComment) {
179
+ if (char === "*" && next === "/") {
180
+ inComment = false;
181
+ index += 1;
182
+ }
183
+ continue;
184
+ }
185
+ if (quote !== null) {
186
+ if (char === "\\") index += 1;
187
+ else if (char === quote) quote = null;
188
+ continue;
189
+ }
190
+ if (char === "/" && next === "*") {
191
+ inComment = true;
192
+ index += 1;
193
+ continue;
194
+ }
195
+ if (char === "'" || char === "\"") {
196
+ quote = char;
197
+ continue;
198
+ }
199
+ if (char === "(") parenDepth += 1;
200
+ else if (char === ")" && parenDepth > 0) parenDepth -= 1;
201
+ else if (char === "[") bracketDepth += 1;
202
+ else if (char === "]" && bracketDepth > 0) bracketDepth -= 1;
203
+ else if (char === "{" && parenDepth === 0 && bracketDepth === 0) return index;
204
+ }
205
+ return -1;
206
+ }
207
+ function findMatchingBrace(css, start) {
208
+ let depth = 0;
209
+ let quote = null;
210
+ let inComment = false;
211
+ for (let index = start; index < css.length; index += 1) {
212
+ const char = css[index];
213
+ const next = css[index + 1];
214
+ if (inComment) {
215
+ if (char === "*" && next === "/") {
216
+ inComment = false;
217
+ index += 1;
218
+ }
219
+ continue;
220
+ }
221
+ if (quote !== null) {
222
+ if (char === "\\") index += 1;
223
+ else if (char === quote) quote = null;
224
+ continue;
225
+ }
226
+ if (char === "/" && next === "*") {
227
+ inComment = true;
228
+ index += 1;
229
+ continue;
230
+ }
231
+ if (char === "'" || char === "\"") {
232
+ quote = char;
233
+ continue;
234
+ }
235
+ if (char === "{") depth += 1;
236
+ else if (char === "}") {
237
+ depth -= 1;
238
+ if (depth === 0) return index;
239
+ }
240
+ }
241
+ return -1;
242
+ }
243
+ function scopeSelectorList(selectorList, scopeId) {
244
+ return splitSelectorList(selectorList).map((selector) => scopeSelector(selector, scopeId)).join(",");
245
+ }
246
+ function splitSelectorList(selectorList) {
247
+ const selectors = [];
248
+ let start = 0;
249
+ let parenDepth = 0;
250
+ let bracketDepth = 0;
251
+ let quote = null;
252
+ for (let index = 0; index < selectorList.length; index += 1) {
253
+ const char = selectorList[index];
254
+ if (quote !== null) {
255
+ if (char === "\\") index += 1;
256
+ else if (char === quote) quote = null;
257
+ continue;
258
+ }
259
+ if (char === "'" || char === "\"") {
260
+ quote = char;
261
+ continue;
262
+ }
263
+ if (char === "(") parenDepth += 1;
264
+ else if (char === ")" && parenDepth > 0) parenDepth -= 1;
265
+ else if (char === "[") bracketDepth += 1;
266
+ else if (char === "]" && bracketDepth > 0) bracketDepth -= 1;
267
+ else if (char === "," && parenDepth === 0 && bracketDepth === 0) {
268
+ selectors.push(selectorList.slice(start, index));
269
+ start = index + 1;
270
+ }
271
+ }
272
+ selectors.push(selectorList.slice(start));
273
+ return selectors;
274
+ }
275
+ function scopeSelector(selector, scopeId) {
276
+ const leadingLength = selector.search(/\S/);
277
+ if (leadingLength === -1) return selector;
278
+ const leading = selector.slice(0, leadingLength);
279
+ const trailingLength = selector.match(/\s*$/)?.[0].length ?? 0;
280
+ const bodyEnd = trailingLength === 0 ? selector.length : selector.length - trailingLength;
281
+ const trailing = selector.slice(bodyEnd);
282
+ let body = selector.slice(leadingLength, bodyEnd).replace(globalSelectorPattern, "$1");
283
+ const deep = body.match(deepSelectorPattern);
284
+ if (deep?.index !== void 0) {
285
+ const before = body.slice(0, deep.index).trimEnd();
286
+ const inner = deep[1] ?? "";
287
+ const after = body.slice(deep.index + deep[0].length);
288
+ body = `${before.length === 0 ? `[${scopeId}]` : addScopeToSelectorEnd(before, scopeId)} ${inner}${after}`;
289
+ } else body = addScopeToSelectorEnd(body, scopeId);
290
+ return leading + body + trailing;
291
+ }
292
+ function addScopeToSelectorEnd(selector, scopeId) {
293
+ const targetStart = findLastCompoundStart(selector);
294
+ const beforeTarget = selector.slice(0, targetStart);
295
+ const target = selector.slice(targetStart);
296
+ const insertAt = findScopeInsertPosition(target);
297
+ return `${beforeTarget}${target.slice(0, insertAt)}[${scopeId}]${target.slice(insertAt)}`;
298
+ }
299
+ function findLastCompoundStart(selector) {
300
+ let parenDepth = 0;
301
+ let bracketDepth = 0;
302
+ let quote = null;
303
+ for (let index = selector.length - 1; index >= 0; index -= 1) {
304
+ const char = selector[index];
305
+ if (quote !== null) {
306
+ if (char === quote) quote = null;
307
+ continue;
308
+ }
309
+ if (char === "'" || char === "\"") {
310
+ quote = char;
311
+ continue;
312
+ }
313
+ if (char === ")") parenDepth += 1;
314
+ else if (char === "(" && parenDepth > 0) parenDepth -= 1;
315
+ else if (char === "]") bracketDepth += 1;
316
+ else if (char === "[" && bracketDepth > 0) bracketDepth -= 1;
317
+ else if (parenDepth === 0 && bracketDepth === 0 && (char === ">" || char === "+" || char === "~")) return index + 1;
318
+ else if (parenDepth === 0 && bracketDepth === 0 && /\s/.test(char)) {
319
+ while (index > 0 && /\s/.test(selector[index - 1])) index -= 1;
320
+ return index + 1;
321
+ }
322
+ }
323
+ return 0;
324
+ }
325
+ function findScopeInsertPosition(target) {
326
+ let parenDepth = 0;
327
+ let bracketDepth = 0;
328
+ let quote = null;
329
+ for (let index = 0; index < target.length; index += 1) {
330
+ const char = target[index];
331
+ if (quote !== null) {
332
+ if (char === "\\") index += 1;
333
+ else if (char === quote) quote = null;
334
+ continue;
335
+ }
336
+ if (char === "'" || char === "\"") {
337
+ quote = char;
338
+ continue;
339
+ }
340
+ if (char === "(") parenDepth += 1;
341
+ else if (char === ")" && parenDepth > 0) parenDepth -= 1;
342
+ else if (char === "[") bracketDepth += 1;
343
+ else if (char === "]" && bracketDepth > 0) bracketDepth -= 1;
344
+ else if (char === ":" && parenDepth === 0 && bracketDepth === 0) return index;
345
+ }
346
+ return target.length;
347
+ }
120
348
  function parseCustomMedia(css, map) {
121
349
  const re = /@custom-media\s+(--[\w-]+)\s+(.+?)\s*;/g;
122
350
  let m;
@@ -152,13 +380,16 @@ function needsPreprocessor(block) {
152
380
  function isCssModule(block) {
153
381
  return block.module !== false;
154
382
  }
383
+ function needsCssPipeline(block) {
384
+ return block.content.includes("@apply");
385
+ }
155
386
  /**
156
387
  * Check if any style blocks in the compiled module require delegation to
157
- * Vite's CSS pipeline (preprocessor or CSS Modules).
388
+ * Vite's CSS pipeline (preprocessor, CSS Modules, or PostCSS transforms).
158
389
  */
159
390
  function hasDelegatedStyles(compiled) {
160
391
  if (!compiled.styles) return false;
161
- return compiled.styles.some((s) => needsPreprocessor(s) || isCssModule(s));
392
+ return compiled.styles.some((s) => needsPreprocessor(s) || isCssModule(s) || needsCssPipeline(s));
162
393
  }
163
394
  function supportsTemplateOnlyHmr(output) {
164
395
  return /(?:^|\n)(?:_sfc_main|__sfc__)\.render\s*=\s*render\b/m.test(output);
@@ -340,11 +571,8 @@ function rewriteStaticAssetUrls(code, aliasRules) {
340
571
  return rewritten;
341
572
  }
342
573
  /**
343
- * Built-in Vite/Vue/Nuxt define keys that are handled by Vite's own transform pipeline.
344
- * These must NOT be replaced by the vize plugin because:
345
- * 1. Nuxt runs both client and server Vite builds, each with different values
346
- * (e.g., import.meta.server = true on server, false on client).
347
- * 2. Vite's import.meta transform already handles these correctly per-environment.
574
+ * Built-in Vite/Vue/Nuxt define keys that are normally handled by Vite's own
575
+ * transform pipeline.
348
576
  */
349
577
  const BUILTIN_DEFINE_PREFIXES = [
350
578
  "import.meta.server",
@@ -358,9 +586,20 @@ const BUILTIN_DEFINE_PREFIXES = [
358
586
  "__NUXT_",
359
587
  "process.env"
360
588
  ];
589
+ const VIRTUAL_MODULE_DEFINE_KEYS = new Set([
590
+ "import.meta.server",
591
+ "import.meta.client",
592
+ "import.meta.dev",
593
+ "import.meta.test",
594
+ "import.meta.prerender"
595
+ ]);
361
596
  function isBuiltinDefine(key) {
362
597
  return BUILTIN_DEFINE_PREFIXES.some((prefix) => key === prefix || key.startsWith(prefix + ".") || key.startsWith(prefix + "_"));
363
598
  }
599
+ function shouldApplyDefineInVirtualModule(key) {
600
+ if (VIRTUAL_MODULE_DEFINE_KEYS.has(key)) return true;
601
+ return !isBuiltinDefine(key);
602
+ }
364
603
  /**
365
604
  * Apply Vite define replacements to code.
366
605
  * Replaces keys like `import.meta.vfFeatures.photoSection` with their values.
@@ -820,6 +1059,16 @@ function getOxcDumpPath(root, realPath) {
820
1059
  fs.mkdirSync(dumpDir, { recursive: true });
821
1060
  return path.join(dumpDir, `vize-oxc-error-${path.basename(realPath)}.ts`);
822
1061
  }
1062
+ function getVirtualModuleDefines(state, ssr) {
1063
+ return {
1064
+ "import.meta.client": ssr ? "false" : "true",
1065
+ "import.meta.server": ssr ? "true" : "false",
1066
+ "import.meta.dev": state.isProduction ? "false" : "true",
1067
+ "import.meta.test": "false",
1068
+ "import.meta.prerender": "false",
1069
+ ...ssr ? state.serverViteDefine : state.clientViteDefine
1070
+ };
1071
+ }
823
1072
  function loadHook(state, id, loadOptions) {
824
1073
  const currentBase = loadOptions?.ssr ? state.serverViteBase : state.clientViteBase;
825
1074
  if (id === "\0vize:all-styles.css") return Array.from(state.collectedCss.values()).join("\n\n");
@@ -838,6 +1087,7 @@ function loadHook(state, id, loadOptions) {
838
1087
  if (fallbackCompiled?.styles && blockIndex >= 0 && blockIndex < fallbackCompiled.styles.length) {
839
1088
  const block = fallbackCompiled.styles[blockIndex];
840
1089
  let styleContent = block.content;
1090
+ if (scoped && block.scoped && (!lang || lang === "css")) styleContent = scopeCssForPipeline(styleContent, scoped);
841
1091
  if (scoped && block.scoped && lang && lang !== "css") {
842
1092
  const lines = styleContent.split("\n");
843
1093
  const hoisted = [];
@@ -936,9 +1186,9 @@ async function transformHook(state, code, id, options) {
936
1186
  const realPath = isMacro ? id.slice(1).replace("?macro=true", "") : fromVirtualId(id);
937
1187
  try {
938
1188
  const result = await transformWithOxc(code, realPath, { lang: "ts" });
939
- const defines = options?.ssr ? state.serverViteDefine : state.clientViteDefine;
1189
+ const defines = getVirtualModuleDefines(state, options?.ssr ?? false);
940
1190
  let transformed = result.code;
941
- if (Object.keys(defines).length > 0) transformed = applyDefineReplacements(transformed, defines);
1191
+ transformed = applyDefineReplacements(transformed, defines);
942
1192
  return {
943
1193
  code: transformed,
944
1194
  map: result.map
@@ -1194,7 +1444,7 @@ function vize(options = {}) {
1194
1444
  const isSsr = !!resolvedConfig.build?.ssr;
1195
1445
  const envDefine = {};
1196
1446
  if (resolvedConfig.define) for (const [key, value] of Object.entries(resolvedConfig.define)) {
1197
- if (isBuiltinDefine(key)) continue;
1447
+ if (!shouldApplyDefineInVirtualModule(key)) continue;
1198
1448
  if (typeof value === "string") envDefine[key] = value;
1199
1449
  else envDefine[key] = JSON.stringify(value);
1200
1450
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizejs/vite-plugin",
3
- "version": "0.69.0",
3
+ "version": "0.71.0",
4
4
  "description": "High-performance native Vite plugin for Vue SFC compilation powered by Vize",
5
5
  "keywords": [
6
6
  "compiler",
@@ -33,9 +33,9 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "@vizejs/native": "0.69.0",
36
+ "@vizejs/native": "0.71.0",
37
37
  "tinyglobby": "0.2.16",
38
- "vize": "0.69.0"
38
+ "vize": "0.71.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "25.6.0",