@wyw-in-js/vite 1.0.9 → 2.0.0-alpha.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/esm/index.mjs CHANGED
@@ -1,615 +1,658 @@
1
1
  /**
2
- * This file contains a Vite loader for wyw-in-js.
3
- * It uses the transform.ts function to generate class names from source code,
4
- * returns transformed code without template literals and attaches generated source maps
5
- */
6
-
7
- import { existsSync } from 'fs';
8
- import path from 'path';
9
- import { createFilter, loadEnv } from 'vite';
10
- import { asyncResolverFactory, logger, syncResolve } from '@wyw-in-js/shared';
11
- import { createFileReporter, getFileIdx, transform, TransformCacheCollection } from '@wyw-in-js/transform';
12
- const isWindowsAbsolutePath = value => /^[a-zA-Z]:[\\/]/.test(value);
13
- const normalizeToPosix = value => value.replace(/\\/g, path.posix.sep);
2
+ * This file contains a Vite loader for wyw-in-js.
3
+ * It uses the transform.ts function to generate class names from source code,
4
+ * returns transformed code without template literals and attaches generated source maps
5
+ */
6
+ import { existsSync } from "fs";
7
+ import path from "path";
8
+ import { createFilter, loadEnv } from "vite";
9
+ import { asyncResolverFactory, logger, syncResolve } from "@wyw-in-js/shared";
10
+ import * as transformPkg from "@wyw-in-js/transform";
11
+ const { createTransformManifest, createFileReporter, disposeEvalBroker, getFileIdx, stringifyTransformManifest, transform, TransformCacheCollection } = transformPkg;
12
+ const createMetadataManifest = (metadata, context) => typeof createTransformManifest === "function" ? createTransformManifest(metadata, context) : {
13
+ ...metadata,
14
+ ...context,
15
+ version: 1
16
+ };
17
+ const stringifyMetadataManifest = (manifest) => typeof stringifyTransformManifest === "function" ? stringifyTransformManifest(manifest) : `${JSON.stringify(manifest, null, 2)}\n`;
18
+ const isWindowsAbsolutePath = (value) => /^[a-zA-Z]:[\\/]/.test(value);
19
+ const normalizeToPosix = (value) => value.replace(/\\/g, path.posix.sep);
14
20
  const isInside = (childPath, parentPath) => {
15
- const rel = path.relative(parentPath, childPath);
16
- return rel === '' || !rel.startsWith('..') && !path.isAbsolute(rel);
21
+ const rel = path.relative(parentPath, childPath);
22
+ return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
17
23
  };
18
- const isWywCssAssetName = value => value.endsWith('.wyw-in-js.css');
19
- const normalizeAssetRelativePath = value => {
20
- const normalized = path.posix.normalize(normalizeToPosix(value).replace(/^\/+/, ''));
21
- if (normalized.startsWith('..') || path.posix.isAbsolute(normalized)) {
22
- return null;
23
- }
24
- return normalized;
24
+ const isWywCssAssetName = (value) => value.endsWith(".wyw-in-js.css");
25
+ const normalizeAssetRelativePath = (value) => {
26
+ const normalized = path.posix.normalize(normalizeToPosix(value).replace(/^\/+/, ""));
27
+ if (normalized.startsWith("..") || path.posix.isAbsolute(normalized)) {
28
+ return null;
29
+ }
30
+ return normalized;
25
31
  };
26
- const stripExtension = value => {
27
- const ext = path.posix.extname(value);
28
- return ext ? value.slice(0, -ext.length) : value;
32
+ const stripExtension = (value) => {
33
+ const ext = path.posix.extname(value);
34
+ return ext ? value.slice(0, -ext.length) : value;
29
35
  };
30
36
  const getComparableAssetPaths = (value, rootDir) => {
31
- const variants = new Set();
32
- const normalized = normalizeToPosix(value);
33
- variants.add(normalized);
34
- if (path.isAbsolute(value) || isWindowsAbsolutePath(normalized)) {
35
- if (isInside(value, rootDir)) {
36
- const relativeToRoot = normalizeAssetRelativePath(path.relative(rootDir, value));
37
- if (relativeToRoot) {
38
- variants.add(relativeToRoot);
39
- }
40
- }
41
- return variants;
42
- }
43
- const relativePath = normalizeAssetRelativePath(value);
44
- if (relativePath) {
45
- variants.add(relativePath);
46
- }
47
- return variants;
37
+ const variants = new Set();
38
+ const normalized = normalizeToPosix(value);
39
+ variants.add(normalized);
40
+ if (path.isAbsolute(value) || isWindowsAbsolutePath(normalized)) {
41
+ if (isInside(value, rootDir)) {
42
+ const relativeToRoot = normalizeAssetRelativePath(path.relative(rootDir, value));
43
+ if (relativeToRoot) {
44
+ variants.add(relativeToRoot);
45
+ }
46
+ }
47
+ return variants;
48
+ }
49
+ const relativePath = normalizeAssetRelativePath(value);
50
+ if (relativePath) {
51
+ variants.add(relativePath);
52
+ }
53
+ return variants;
48
54
  };
49
- const getStringValues = value => {
50
- if (!Array.isArray(value)) return [];
51
- return value.filter(item => typeof item === 'string');
55
+ const getStringValues = (value) => {
56
+ if (!Array.isArray(value)) return [];
57
+ return value.filter((item) => typeof item === "string");
52
58
  };
53
- const getOutputAssetNames = asset => [...(typeof asset.name === 'string' ? [asset.name] : []), ...getStringValues(asset.names), ...(typeof asset.originalFileName === 'string' ? [asset.originalFileName] : []), ...getStringValues(asset.originalFileNames)];
54
- const isOutputAssetLike = value => !!value && typeof value === 'object' && value.type === 'asset' && typeof value.fileName === 'string';
55
- const isOutputChunkLike = value => !!value && typeof value === 'object' && value.type === 'chunk' && typeof value.fileName === 'string' && typeof value.code === 'string';
59
+ const getOutputAssetNames = (asset) => [
60
+ ...typeof asset.name === "string" ? [asset.name] : [],
61
+ ...getStringValues(asset.names),
62
+ ...typeof asset.originalFileName === "string" ? [asset.originalFileName] : [],
63
+ ...getStringValues(asset.originalFileNames)
64
+ ];
65
+ const isOutputAssetLike = (value) => !!value && typeof value === "object" && value.type === "asset" && typeof value.fileName === "string";
66
+ const isOutputChunkLike = (value) => !!value && typeof value === "object" && value.type === "chunk" && typeof value.fileName === "string" && typeof value.code === "string";
56
67
  const getTrackedModuleIdForChunk = (chunk, cssFilesByModuleId) => {
57
- if (typeof chunk.facadeModuleId === 'string' && cssFilesByModuleId.has(chunk.facadeModuleId)) {
58
- return chunk.facadeModuleId;
59
- }
60
- if (!Array.isArray(chunk.moduleIds)) {
61
- return null;
62
- }
63
- const moduleId = chunk.moduleIds.find(id => typeof id === 'string' && cssFilesByModuleId.has(id));
64
- return moduleId ?? null;
68
+ if (typeof chunk.facadeModuleId === "string" && cssFilesByModuleId.has(chunk.facadeModuleId)) {
69
+ return chunk.facadeModuleId;
70
+ }
71
+ if (!Array.isArray(chunk.moduleIds)) {
72
+ return null;
73
+ }
74
+ const moduleId = chunk.moduleIds.find((id) => typeof id === "string" && cssFilesByModuleId.has(id));
75
+ return moduleId ?? null;
65
76
  };
66
77
  const findWywCssAssetFileName = (bundle, cssFilename, rootDir) => {
67
- const expectedNames = getComparableAssetPaths(cssFilename, rootDir);
68
- for (const item of Object.values(bundle)) {
69
- if (isOutputAssetLike(item) && item.fileName.endsWith('.css')) {
70
- const isMatch = getOutputAssetNames(item).some(assetName => {
71
- const variants = getComparableAssetPaths(assetName, rootDir);
72
- return Array.from(variants).some(variant => expectedNames.has(variant));
73
- });
74
- if (isMatch) {
75
- return normalizeToPosix(item.fileName);
76
- }
77
- }
78
- }
79
- return null;
78
+ const expectedNames = getComparableAssetPaths(cssFilename, rootDir);
79
+ for (const item of Object.values(bundle)) {
80
+ if (isOutputAssetLike(item) && item.fileName.endsWith(".css")) {
81
+ const isMatch = getOutputAssetNames(item).some((assetName) => {
82
+ const variants = getComparableAssetPaths(assetName, rootDir);
83
+ return Array.from(variants).some((variant) => expectedNames.has(variant));
84
+ });
85
+ if (isMatch) {
86
+ return normalizeToPosix(item.fileName);
87
+ }
88
+ }
89
+ }
90
+ return null;
80
91
  };
81
92
  const getRelativeImportPath = (fromFileName, toFileName) => {
82
- const fromDir = path.posix.dirname(normalizeToPosix(fromFileName));
83
- const relativePath = path.posix.relative(fromDir, normalizeToPosix(toFileName));
84
- return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
93
+ const fromDir = path.posix.dirname(normalizeToPosix(fromFileName));
94
+ const relativePath = path.posix.relative(fromDir, normalizeToPosix(toFileName));
95
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
85
96
  };
86
- const escapeForRegExp = value => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
87
- const hasStaticImport = (code, specifier) => new RegExp(`(^|\\n)\\s*import\\s*(?:["']${escapeForRegExp(specifier)}["']|[^\\n;]+\\s+from\\s+["']${escapeForRegExp(specifier)}["'])`, 'm').test(code);
88
- const hasRequireCall = (code, specifier) => new RegExp(`(^|[;\\n])\\s*require\\(\\s*["']${escapeForRegExp(specifier)}["']\\s*\\)`, 'm').test(code);
89
- const getCssLoadStatement = (format, specifier) => format === 'cjs' ? `require(${JSON.stringify(specifier)});\n` : `import ${JSON.stringify(specifier)};\n`;
90
- const hasCssLoadStatement = (code, specifier, format) => format === 'cjs' ? hasRequireCall(code, specifier) : hasStaticImport(code, specifier);
97
+ const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
98
+ const hasStaticImport = (code, specifier) => new RegExp(`(^|\\n)\\s*import\\s*(?:["']${escapeForRegExp(specifier)}["']|[^\\n;]+\\s+from\\s+["']${escapeForRegExp(specifier)}["'])`, "m").test(code);
99
+ const hasRequireCall = (code, specifier) => new RegExp(`(^|[;\\n])\\s*require\\(\\s*["']${escapeForRegExp(specifier)}["']\\s*\\)`, "m").test(code);
100
+ const getCssLoadStatement = (format, specifier) => format === "cjs" ? `require(${JSON.stringify(specifier)});\n` : `import ${JSON.stringify(specifier)};\n`;
101
+ const hasCssLoadStatement = (code, specifier, format) => format === "cjs" ? hasRequireCall(code, specifier) : hasStaticImport(code, specifier);
91
102
  const prependCssLoadStatement = (code, specifier, format) => {
92
- const statement = getCssLoadStatement(format, specifier);
93
- let insertAt = 0;
94
- if (code.startsWith('#!')) {
95
- const lineBreakIndex = code.indexOf('\n');
96
- if (lineBreakIndex >= 0) {
97
- insertAt = lineBreakIndex + 1;
98
- } else {
99
- return `${code}\n${statement}`;
100
- }
101
- }
102
- if (format === 'cjs') {
103
- const directiveMatch = /^(?:\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*');)+/.exec(code.slice(insertAt));
104
- if (directiveMatch) {
105
- insertAt += directiveMatch[0].length;
106
- }
107
- }
108
- return `${code.slice(0, insertAt)}${statement}${code.slice(insertAt)}`;
103
+ const statement = getCssLoadStatement(format, specifier);
104
+ let insertAt = 0;
105
+ if (code.startsWith("#!")) {
106
+ const lineBreakIndex = code.indexOf("\n");
107
+ if (lineBreakIndex >= 0) {
108
+ insertAt = lineBreakIndex + 1;
109
+ } else {
110
+ return `${code}\n${statement}`;
111
+ }
112
+ }
113
+ if (format === "cjs") {
114
+ const directiveMatch = /^(?:\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*');)+/.exec(code.slice(insertAt));
115
+ if (directiveMatch) {
116
+ insertAt += directiveMatch[0].length;
117
+ }
118
+ }
119
+ return `${code.slice(0, insertAt)}${statement}${code.slice(insertAt)}`;
109
120
  };
110
- const VITE_FS_PREFIX = '/@fs/';
111
- const safeDecodeURIComponent = value => {
112
- try {
113
- return decodeURIComponent(value);
114
- } catch {
115
- return value;
116
- }
121
+ const VITE_FS_PREFIX = "/@fs/";
122
+ const safeDecodeURIComponent = (value) => {
123
+ try {
124
+ return decodeURIComponent(value);
125
+ } catch {
126
+ return value;
127
+ }
117
128
  };
118
- const normalizeViteFsPath = value => {
119
- const fsPath = value.slice(VITE_FS_PREFIX.length);
120
- return path.normalize(safeDecodeURIComponent(fsPath));
129
+ const normalizeViteFsPath = (value) => {
130
+ const fsPath = value.slice(VITE_FS_PREFIX.length);
131
+ return path.normalize(safeDecodeURIComponent(fsPath));
121
132
  };
122
- const isCssReloadTarget = value => {
123
- if (!value || typeof value !== 'object') return false;
124
- const reloadTarget = value;
125
- return !!reloadTarget.moduleGraph && typeof reloadTarget.moduleGraph.getModuleById === 'function' && typeof reloadTarget.reloadModule === 'function';
133
+ const isCssReloadTarget = (value) => {
134
+ if (!value || typeof value !== "object") return false;
135
+ const reloadTarget = value;
136
+ return !!reloadTarget.moduleGraph && typeof reloadTarget.moduleGraph.getModuleById === "function" && typeof reloadTarget.reloadModule === "function";
126
137
  };
127
138
  const getCssReloadTarget = (environment, server) => {
128
- if (isCssReloadTarget(environment)) {
129
- return environment;
130
- }
131
- if (server?.moduleGraph) {
132
- return {
133
- moduleGraph: server.moduleGraph,
134
- reloadModule: module => server.reloadModule(module)
135
- };
136
- }
137
- return null;
139
+ if (isCssReloadTarget(environment)) {
140
+ return environment;
141
+ }
142
+ if (server?.moduleGraph) {
143
+ return {
144
+ moduleGraph: server.moduleGraph,
145
+ reloadModule: (module) => server.reloadModule(module)
146
+ };
147
+ }
148
+ return null;
138
149
  };
139
150
  const getWywCssAssetFileNames = (resolvedConfig, output, originalAssetFileNames) => {
140
- if (!output.preserveModules) return null;
141
- const rootDir = resolvedConfig.root;
142
- const preserveModulesRootValue = output.preserveModulesRoot;
143
- let preserveModulesRootAbs = null;
144
- if (typeof preserveModulesRootValue === 'string') {
145
- preserveModulesRootAbs = path.isAbsolute(preserveModulesRootValue) ? preserveModulesRootValue : path.resolve(rootDir, preserveModulesRootValue);
146
- }
147
- const preserveModulesRootRel = preserveModulesRootAbs && isInside(preserveModulesRootAbs, rootDir) ? normalizeToPosix(path.relative(rootDir, preserveModulesRootAbs)) : null;
148
- return assetInfo => {
149
- const template = typeof originalAssetFileNames === 'function' ? originalAssetFileNames(assetInfo) : originalAssetFileNames;
150
- const assetName = assetInfo?.name;
151
- if (typeof assetName !== 'string' || !isWywCssAssetName(assetName)) {
152
- return template;
153
- }
154
- if (!template.includes('[')) {
155
- return template;
156
- }
157
- let relativePath = null;
158
- const assetNameNormalized = normalizeToPosix(assetName);
159
- if (path.isAbsolute(assetName) || isWindowsAbsolutePath(assetNameNormalized)) {
160
- const preserveRel = preserveModulesRootAbs && isInside(assetName, preserveModulesRootAbs) ? path.relative(preserveModulesRootAbs, assetName) : null;
161
- if (preserveRel && !path.isAbsolute(preserveRel) && !preserveRel.startsWith('..')) {
162
- relativePath = preserveRel;
163
- } else if (isInside(assetName, rootDir)) {
164
- relativePath = path.relative(rootDir, assetName);
165
- }
166
- } else if (preserveModulesRootRel && assetNameNormalized.startsWith(`${preserveModulesRootRel}/`)) {
167
- relativePath = assetNameNormalized.slice(preserveModulesRootRel.length + 1);
168
- } else {
169
- relativePath = assetNameNormalized;
170
- }
171
- const normalized = relativePath ? normalizeAssetRelativePath(relativePath) : null;
172
- if (!normalized) {
173
- return template;
174
- }
175
- const withoutExt = stripExtension(normalized);
176
- if (template.includes('[name]')) {
177
- const dir = path.posix.dirname(withoutExt);
178
- if (dir === '.' || dir === '') {
179
- return template;
180
- }
181
- return template.replace(/\[name\]/g, `${dir}/[name]`);
182
- }
183
- const dir = path.posix.dirname(withoutExt);
184
- if (dir === '.' || dir === '') {
185
- return template;
186
- }
187
- const idx = template.indexOf('[');
188
- if (idx < 0) {
189
- return template;
190
- }
191
- const prefix = template.slice(0, idx);
192
- if (prefix !== '' && !prefix.endsWith('/')) {
193
- return template;
194
- }
195
- return `${prefix}${dir}/${template.slice(idx)}`;
196
- };
151
+ if (!output.preserveModules) return null;
152
+ const rootDir = resolvedConfig.root;
153
+ const preserveModulesRootValue = output.preserveModulesRoot;
154
+ let preserveModulesRootAbs = null;
155
+ if (typeof preserveModulesRootValue === "string") {
156
+ preserveModulesRootAbs = path.isAbsolute(preserveModulesRootValue) ? preserveModulesRootValue : path.resolve(rootDir, preserveModulesRootValue);
157
+ }
158
+ const preserveModulesRootRel = preserveModulesRootAbs && isInside(preserveModulesRootAbs, rootDir) ? normalizeToPosix(path.relative(rootDir, preserveModulesRootAbs)) : null;
159
+ return (assetInfo) => {
160
+ const template = typeof originalAssetFileNames === "function" ? originalAssetFileNames(assetInfo) : originalAssetFileNames;
161
+ const assetName = assetInfo?.name;
162
+ if (typeof assetName !== "string" || !isWywCssAssetName(assetName)) {
163
+ return template;
164
+ }
165
+ if (!template.includes("[")) {
166
+ return template;
167
+ }
168
+ let relativePath = null;
169
+ const assetNameNormalized = normalizeToPosix(assetName);
170
+ if (path.isAbsolute(assetName) || isWindowsAbsolutePath(assetNameNormalized)) {
171
+ const preserveRel = preserveModulesRootAbs && isInside(assetName, preserveModulesRootAbs) ? path.relative(preserveModulesRootAbs, assetName) : null;
172
+ if (preserveRel && !path.isAbsolute(preserveRel) && !preserveRel.startsWith("..")) {
173
+ relativePath = preserveRel;
174
+ } else if (isInside(assetName, rootDir)) {
175
+ relativePath = path.relative(rootDir, assetName);
176
+ }
177
+ } else if (preserveModulesRootRel && assetNameNormalized.startsWith(`${preserveModulesRootRel}/`)) {
178
+ relativePath = assetNameNormalized.slice(preserveModulesRootRel.length + 1);
179
+ } else {
180
+ relativePath = assetNameNormalized;
181
+ }
182
+ const normalized = relativePath ? normalizeAssetRelativePath(relativePath) : null;
183
+ if (!normalized) {
184
+ return template;
185
+ }
186
+ const withoutExt = stripExtension(normalized);
187
+ if (template.includes("[name]")) {
188
+ const dir = path.posix.dirname(withoutExt);
189
+ if (dir === "." || dir === "") {
190
+ return template;
191
+ }
192
+ return template.replace(/\[name\]/g, `${dir}/[name]`);
193
+ }
194
+ const dir = path.posix.dirname(withoutExt);
195
+ if (dir === "." || dir === "") {
196
+ return template;
197
+ }
198
+ const idx = template.indexOf("[");
199
+ if (idx < 0) {
200
+ return template;
201
+ }
202
+ const prefix = template.slice(0, idx);
203
+ if (prefix !== "" && !prefix.endsWith("/")) {
204
+ return template;
205
+ }
206
+ return `${prefix}${dir}/${template.slice(idx)}`;
207
+ };
197
208
  };
198
- export default function wywInJS({
199
- debug,
200
- include,
201
- exclude,
202
- sourceMap,
203
- preserveCssPaths,
204
- keepComments,
205
- prefixer,
206
- preprocessor,
207
- ssrDevCss,
208
- ssrDevCssPath,
209
- transformLibraries,
210
- ...rest
211
- } = {}) {
212
- const filter = createFilter(include, exclude);
213
- const cssLookup = {};
214
- const cssFileLookup = {};
215
- const cssFilesByModuleId = new Map();
216
- const pendingCssReloads = new WeakMap();
217
- let ssrDevCssVersion = 0;
218
- let config;
219
- let devServer;
220
- let importMetaEnvForEval = null;
221
- const ssrDevCssEnabled = Boolean(ssrDevCss);
222
- const [ssrDevCssPathname, ssrDevCssQuery] = (ssrDevCssPath ?? '/_wyw-in-js/ssr.css').split('?', 2);
223
- const ssrDevCssRoute = ssrDevCssPathname.startsWith('/') ? ssrDevCssPathname : `/${ssrDevCssPathname}`;
224
- const getSsrDevCssHref = () => {
225
- const versionParam = `v=${ssrDevCssVersion}`;
226
- const query = ssrDevCssQuery ? `${ssrDevCssQuery}&${versionParam}` : versionParam;
227
- return `${ssrDevCssRoute}?${query}`;
228
- };
229
- const getSsrDevCssContents = () => {
230
- const entries = Object.entries(cssLookup);
231
- if (entries.length === 0) return '';
232
- const merged = entries.sort(([a], [b]) => a.localeCompare(b)).map(([, cssText]) => cssText).join('\n');
233
- return `${merged}\n`;
234
- };
235
- const {
236
- emitter,
237
- onDone
238
- } = createFileReporter(debug ?? false);
239
- const scheduleCssReload = (reloadTarget, cssFilename) => {
240
- let state = pendingCssReloads.get(reloadTarget);
241
- if (!state) {
242
- state = {
243
- files: new Set()
244
- };
245
- pendingCssReloads.set(reloadTarget, state);
246
- }
247
- state.files.add(cssFilename);
248
- if (state.timer) return;
249
- state.timer = setTimeout(() => {
250
- state.timer = undefined;
251
- const ids = Array.from(state.files);
252
- state.files.clear();
253
- const {
254
- moduleGraph
255
- } = reloadTarget;
256
- for (const id of ids) {
257
- const module = moduleGraph.getModuleById(id);
258
- if (module) reloadTarget.reloadModule(module);
259
- }
260
- }, 0);
261
- };
262
-
263
- // <dependency id, targets>
264
- const targets = [];
265
- const clientCache = new TransformCacheCollection();
266
- const ssrCache = new TransformCacheCollection();
267
- const caches = new Set([clientCache, ssrCache]);
268
- const getCache = isSsr => isSsr ? ssrCache : clientCache;
269
- const isInsideCacheDir = filename => {
270
- if (!config.cacheDir) {
271
- return false;
272
- }
273
- const relative = path.relative(config.cacheDir, filename);
274
- return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);
275
- };
276
- const getDepsOptimizer = () => {
277
- if (!devServer) return null;
278
- const server = devServer;
279
- return server.environments?.client?.depsOptimizer ?? server.depsOptimizer ?? server._depsOptimizer ?? null;
280
- };
281
- const waitForOptimizedDep = async filename => {
282
- const depsOptimizer = getDepsOptimizer();
283
- if (!depsOptimizer?.isOptimizedDepFile?.(filename)) {
284
- return false;
285
- }
286
- await depsOptimizer.init?.();
287
- await depsOptimizer.scanProcessing;
288
- const info = depsOptimizer.metadata?.depInfoList?.find(item => item.file === filename);
289
- if (info?.processing) {
290
- await info.processing;
291
- }
292
- return true;
293
- };
294
- let viteResolver = null;
295
- const resolveClient = (what, importer) => {
296
- if (!viteResolver) {
297
- throw new Error('Vite resolver is not initialized yet');
298
- }
299
- return viteResolver(what, importer, false, false);
300
- };
301
- const resolveSsr = (what, importer) => {
302
- if (!viteResolver) {
303
- throw new Error('Vite resolver is not initialized yet');
304
- }
305
- return viteResolver(what, importer, false, true);
306
- };
307
- const createAsyncResolver = asyncResolverFactory(async (resolved, what, importer, stack) => {
308
- const log = logger.extend('vite').extend(getFileIdx(importer));
309
- if (resolved) {
310
- log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
311
-
312
- // Vite adds param like `?v=667939b3` to cached modules
313
- let resolvedId = resolved.split('?', 1)[0];
314
- if (resolvedId.startsWith('\0')) {
315
- // \0 is a special character in Rollup that tells Rollup to not include this in the bundle
316
- // https://rollupjs.org/guide/en/#outputexports
317
- return null;
318
- }
319
- if (resolvedId.startsWith(VITE_FS_PREFIX)) {
320
- resolvedId = normalizeViteFsPath(resolvedId);
321
- }
322
- if (resolvedId.startsWith('/@')) {
323
- return null;
324
- }
325
- if (!existsSync(resolvedId)) {
326
- // When Vite resolves to an optimized deps entry (cacheDir) it may not be written yet.
327
- // Wait for Vite's optimizer instead of calling optimizeDeps() manually (deprecated in Vite 7).
328
- try {
329
- await waitForOptimizedDep(resolvedId);
330
- } catch {
331
- // If optimizer failed, fall through to preserve previous behavior and surface the error.
332
- }
333
-
334
- // Vite can return an optimized deps entry (from cacheDir) before it's written to disk.
335
- // Manually calling optimizeDeps is deprecated in Vite 7 and can also get called many times.
336
- // Instead, fall back to resolving the original module path directly.
337
- if (!existsSync(resolvedId) && isInsideCacheDir(resolvedId)) {
338
- try {
339
- return syncResolve(what, importer, stack);
340
- } catch {
341
- // Fall through to preserve previous behavior: return resolvedId and let WyW surface the error.
342
- }
343
- }
344
- }
345
- if (!existsSync(resolvedId) && !path.isAbsolute(resolvedId)) {
346
- // Vite can resolve an import to a bare specifier when bundling for SSR and marking it as external.
347
- // In that case we still need a real file path for WyW evaluation.
348
- return syncResolve(what, importer, stack);
349
- }
350
- return resolvedId;
351
- }
352
- log("resolve '%s'@'%s", what, importer);
353
-
354
- // Vite can inject virtual ids like /@react-refresh in dev.
355
- if (what.startsWith('/@') || what.startsWith('\0')) {
356
- return null;
357
- }
358
- if (!what.startsWith('.') && !what.startsWith('/') && !path.isAbsolute(what)) {
359
- // Keep compatibility with SSR externalization: fall back to Node resolution for bare specifiers.
360
- return syncResolve(what, importer, stack);
361
- }
362
- throw new Error(`Could not resolve ${what}`);
363
- }, (what, importer) => [what, importer]);
364
- const asyncResolveClient = createAsyncResolver(resolveClient);
365
- const asyncResolveSsr = createAsyncResolver(resolveSsr);
366
- return {
367
- name: 'wyw-in-js',
368
- enforce: 'post',
369
- buildEnd() {
370
- onDone(process.cwd());
371
- },
372
- configResolved(resolvedConfig) {
373
- config = resolvedConfig;
374
- viteResolver = config.createResolver();
375
- if (preserveCssPaths && config.command === 'build') {
376
- const outputs = config.build.rollupOptions.output;
377
- let outputEntries = [];
378
- if (Array.isArray(outputs)) {
379
- outputEntries = outputs;
380
- } else if (outputs) {
381
- outputEntries = [outputs];
382
- }
383
- outputEntries.forEach(entry => {
384
- if (!entry || typeof entry !== 'object') return;
385
- const output = entry;
386
- if (!output.preserveModules) return;
387
- const template = output.assetFileNames ?? `${config.build.assetsDir ?? 'assets'}/[name].[hash].[ext]`;
388
- const assetFileNames = getWywCssAssetFileNames(config, output, template);
389
- if (assetFileNames) output.assetFileNames = assetFileNames;
390
- });
391
- }
392
- const envPrefix = config.envPrefix ?? 'VITE_';
393
- const envDir =
394
- // envDir is absolute in modern Vite, but keep a fallback for older versions
395
- 'envDir' in config && typeof config.envDir === 'string' ? config.envDir : config.root;
396
- const loaded = loadEnv(config.mode, envDir, envPrefix);
397
- const base = {
398
- ...loaded,
399
- BASE_URL: config.base,
400
- MODE: config.mode,
401
- DEV: config.command === 'serve',
402
- PROD: config.command === 'build'
403
- };
404
- importMetaEnvForEval = {
405
- client: {
406
- ...base,
407
- SSR: false
408
- },
409
- ssr: {
410
- ...base,
411
- SSR: true
412
- }
413
- };
414
- },
415
- configureServer(_server) {
416
- devServer = _server;
417
- if (!ssrDevCssEnabled || config.command !== 'serve') return;
418
- devServer.middlewares.use((req, res, next) => {
419
- const {
420
- url
421
- } = req;
422
- if (!url) {
423
- next();
424
- return;
425
- }
426
- const [pathname] = url.split('?', 1);
427
- if (pathname !== ssrDevCssRoute) {
428
- next();
429
- return;
430
- }
431
- const etag = `W/"${ssrDevCssVersion}"`;
432
- const ifNoneMatch = req.headers['if-none-match'];
433
- if (ifNoneMatch === etag) {
434
- res.statusCode = 304;
435
- res.end();
436
- return;
437
- }
438
- res.statusCode = 200;
439
- res.setHeader('Content-Type', 'text/css; charset=utf-8');
440
- res.setHeader('Cache-Control', 'no-cache');
441
- res.setHeader('ETag', etag);
442
- res.end(getSsrDevCssContents());
443
- });
444
- },
445
- transformIndexHtml(html) {
446
- if (!ssrDevCssEnabled || config.command !== 'serve') return undefined;
447
- return {
448
- html,
449
- tags: [{
450
- tag: 'link',
451
- attrs: {
452
- rel: 'stylesheet',
453
- href: getSsrDevCssHref()
454
- },
455
- injectTo: 'head-prepend'
456
- }]
457
- };
458
- },
459
- load(url) {
460
- const [id] = url.split('?', 1);
461
- return cssLookup[id];
462
- },
463
- /* eslint-disable-next-line consistent-return */
464
- resolveId(importeeUrl) {
465
- const [id] = importeeUrl.split('?', 1);
466
- if (cssLookup[id]) return id;
467
- return cssFileLookup[id];
468
- },
469
- handleHotUpdate(ctx) {
470
- // it's module, so just transform it
471
- if (ctx.modules.length) return ctx.modules;
472
-
473
- // Select affected modules of changed dependency
474
- const affected = targets.filter(x =>
475
- // file is dependency of any target
476
- x.dependencies.some(dep => dep === ctx.file) ||
477
- // or changed module is a dependency of any target
478
- x.dependencies.some(dep => ctx.modules.some(m => m.file === dep)));
479
- const deps = affected.flatMap(target => target.dependencies);
480
-
481
- // eslint-disable-next-line no-restricted-syntax
482
- for (const depId of deps) {
483
- for (const cache of caches) {
484
- cache.invalidateForFile(depId);
485
- }
486
- }
487
- return affected.map(target => devServer.moduleGraph.getModuleById(target.id)).concat(ctx.modules).filter(m => !!m);
488
- },
489
- generateBundle(outputOptions, bundle) {
490
- if (config.command !== 'build') return;
491
- if (!outputOptions.preserveModules) return;
492
- if (config.build.cssCodeSplit === false) return;
493
- Object.values(bundle).forEach(item => {
494
- if (!isOutputChunkLike(item)) {
495
- return;
496
- }
497
- const chunk = item;
498
- const moduleId = getTrackedModuleIdForChunk(chunk, cssFilesByModuleId);
499
- if (!moduleId) {
500
- return;
501
- }
502
- const cssFilename = cssFilesByModuleId.get(moduleId);
503
- if (!cssFilename) {
504
- return;
505
- }
506
- const emittedCssFileName = findWywCssAssetFileName(bundle, cssFilename, config.root);
507
- if (!emittedCssFileName) {
508
- return;
509
- }
510
- const relativeCssImport = getRelativeImportPath(chunk.fileName, emittedCssFileName);
511
- if (hasCssLoadStatement(chunk.code, relativeCssImport, outputOptions.format)) {
512
- return;
513
- }
514
- chunk.code = prependCssLoadStatement(chunk.code, relativeCssImport, outputOptions.format);
515
- });
516
- },
517
- async transform(code, url, transformOptions) {
518
- const [id] = url.split('?', 1);
519
-
520
- // Do not transform ignored and generated files
521
- if (!transformLibraries && url.includes('node_modules') || !filter(url) || id in cssLookup) return;
522
- const log = logger.extend('vite').extend(getFileIdx(id));
523
- log('transform %s', id);
524
- const isSsr = typeof transformOptions === 'boolean' ? transformOptions : Boolean(transformOptions?.ssr);
525
- const overrideContext = (context, filename) => {
526
- const env = importMetaEnvForEval?.[isSsr ? 'ssr' : 'client'];
527
- const withEnv = env ? {
528
- ...context,
529
- __wyw_import_meta_env: env
530
- } : context;
531
- return rest.overrideContext ? rest.overrideContext(withEnv, filename) : withEnv;
532
- };
533
- const transformServices = {
534
- options: {
535
- filename: id,
536
- root: process.cwd(),
537
- prefixer,
538
- keepComments,
539
- preprocessor,
540
- pluginOptions: {
541
- ...rest,
542
- overrideContext
543
- }
544
- },
545
- cache: getCache(isSsr),
546
- emitWarning: message => this.warn(message),
547
- eventEmitter: emitter
548
- };
549
- const asyncResolve = isSsr ? asyncResolveSsr : asyncResolveClient;
550
- const result = await transform(transformServices, code, asyncResolve);
551
- let {
552
- cssText,
553
- dependencies
554
- } = result;
555
-
556
- // Heads up, there are three cases:
557
- // 1. cssText is undefined, it means that file was not transformed
558
- // 2. cssText is empty, it means that file was transformed, but it does not contain any styles
559
- // 3. cssText is not empty, it means that file was transformed and it contains styles
560
-
561
- if (typeof cssText === 'undefined') {
562
- cssFilesByModuleId.delete(id);
563
- return;
564
- }
565
- if (cssText === '') {
566
- cssFilesByModuleId.delete(id);
567
- /* eslint-disable-next-line consistent-return */
568
- return {
569
- code: result.code,
570
- map: result.sourceMap
571
- };
572
- }
573
- dependencies ??= [];
574
- const cssFilename = path.normalize(`${id.replace(/\.[jt]sx?$/, '')}.wyw-in-js.css`).replace(/\\/g, path.posix.sep);
575
- cssFilesByModuleId.set(id, cssFilename);
576
- const cssRelativePath = path.relative(config.root, cssFilename).replace(/\\/g, path.posix.sep);
577
- const cssId = `/${cssRelativePath}`;
578
- if (sourceMap && result.cssSourceMapText) {
579
- const map = Buffer.from(result.cssSourceMapText).toString('base64');
580
- cssText += `/*# sourceMappingURL=data:application/json;base64,${map}*/`;
581
- }
582
- const didCssChange = cssLookup[cssFilename] !== cssText;
583
- cssLookup[cssFilename] = cssText;
584
- cssFileLookup[cssId] = cssFilename;
585
- result.code += `\nimport ${JSON.stringify(cssFilename)};\n`;
586
- for (let i = 0, end = dependencies.length; i < end; i++) {
587
- // eslint-disable-next-line no-await-in-loop
588
- const depModule = await this.resolve(dependencies[i], url, {
589
- isEntry: false
590
- });
591
- if (depModule) dependencies[i] = depModule.id;
592
- }
593
- const target = targets.find(t => t.id === id);
594
- if (!target) targets.push({
595
- id,
596
- dependencies
597
- });else target.dependencies = dependencies;
598
- if (didCssChange) {
599
- const reloadTarget = getCssReloadTarget(this.environment, devServer);
600
- if (reloadTarget) {
601
- scheduleCssReload(reloadTarget, cssFilename);
602
- }
603
- if (ssrDevCssEnabled && config.command === 'serve') {
604
- ssrDevCssVersion += 1;
605
- }
606
- }
607
- /* eslint-disable-next-line consistent-return */
608
- return {
609
- code: result.code,
610
- map: result.sourceMap
611
- };
612
- }
613
- };
209
+ export default function wywInJS({ debug, include, exclude, sourceMap, preserveCssPaths, keepComments, prefixer, preprocessor, ssrDevCss, ssrDevCssPath, transformLibraries, ...rest } = {}) {
210
+ const supportedModuleExtensions = new Set([
211
+ ".cjs",
212
+ ".cts",
213
+ ".js",
214
+ ".jsx",
215
+ ".mjs",
216
+ ".mts",
217
+ ".ts",
218
+ ".tsx"
219
+ ]);
220
+ const filter = createFilter(include, exclude);
221
+ const cssLookup = {};
222
+ const cssFileLookup = {};
223
+ const metadataLookup = {};
224
+ const cssFilesByModuleId = new Map();
225
+ const pendingCssReloads = new WeakMap();
226
+ let ssrDevCssVersion = 0;
227
+ let config;
228
+ let devServer;
229
+ let importMetaEnvForEval = null;
230
+ const buildOverrideContext = (getEnv) => (context, filename) => {
231
+ const env = getEnv();
232
+ const withEnv = env ? {
233
+ ...context,
234
+ __wyw_import_meta_env: env
235
+ } : context;
236
+ return rest.overrideContext ? rest.overrideContext(withEnv, filename) : withEnv;
237
+ };
238
+ const overrideContextClient = buildOverrideContext(() => importMetaEnvForEval?.client);
239
+ const overrideContextSsr = buildOverrideContext(() => importMetaEnvForEval?.ssr);
240
+ const ssrDevCssEnabled = Boolean(ssrDevCss);
241
+ const [ssrDevCssPathname, ssrDevCssQuery] = (ssrDevCssPath ?? "/_wyw-in-js/ssr.css").split("?", 2);
242
+ const ssrDevCssRoute = ssrDevCssPathname.startsWith("/") ? ssrDevCssPathname : `/${ssrDevCssPathname}`;
243
+ const getSsrDevCssHref = () => {
244
+ const versionParam = `v=${ssrDevCssVersion}`;
245
+ const query = ssrDevCssQuery ? `${ssrDevCssQuery}&${versionParam}` : versionParam;
246
+ return `${ssrDevCssRoute}?${query}`;
247
+ };
248
+ const getSsrDevCssContents = () => {
249
+ const entries = Object.entries(cssLookup);
250
+ if (entries.length === 0) return "";
251
+ const merged = entries.sort(([a], [b]) => a.localeCompare(b)).map(([, cssText]) => cssText).join("\n");
252
+ return `${merged}\n`;
253
+ };
254
+ const { emitter, onDone } = createFileReporter(debug ?? false);
255
+ const isSafeAssetPath = (fileName) => fileName !== "" && fileName !== ".." && !fileName.startsWith(`..${path.posix.sep}`) && !path.posix.isAbsolute(fileName) && !isWindowsAbsolutePath(fileName);
256
+ const replaceModuleExtension = (filename, nextExtension) => {
257
+ const extension = path.extname(filename);
258
+ return supportedModuleExtensions.has(extension) ? `${filename.slice(0, -extension.length)}${nextExtension}` : `${filename}${nextExtension}`;
259
+ };
260
+ const toBundleRelativePath = (filename) => {
261
+ const relativePath = normalizeToPosix(path.relative(config.root, filename));
262
+ if (isSafeAssetPath(relativePath)) {
263
+ return relativePath;
264
+ }
265
+ if (!path.isAbsolute(relativePath) && !isWindowsAbsolutePath(relativePath)) {
266
+ return path.posix.join("_wyw-in-js", "external", ...relativePath.split(path.posix.sep).filter(Boolean).map((segment) => segment === ".." ? "__up__" : segment));
267
+ }
268
+ return path.posix.join("_wyw-in-js", "external", ...normalizeToPosix(path.resolve(filename)).split(path.posix.sep).filter(Boolean).map((segment) => segment.replace(/:$/, "")));
269
+ };
270
+ const scheduleCssReload = (reloadTarget, cssFilename) => {
271
+ let state = pendingCssReloads.get(reloadTarget);
272
+ if (!state) {
273
+ state = { files: new Set() };
274
+ pendingCssReloads.set(reloadTarget, state);
275
+ }
276
+ state.files.add(cssFilename);
277
+ if (state.timer) return;
278
+ state.timer = setTimeout(() => {
279
+ state.timer = undefined;
280
+ const ids = Array.from(state.files);
281
+ state.files.clear();
282
+ const { moduleGraph } = reloadTarget;
283
+ for (const id of ids) {
284
+ const module = moduleGraph.getModuleById(id);
285
+ if (module) reloadTarget.reloadModule(module);
286
+ }
287
+ }, 0);
288
+ };
289
+ // <dependency id, targets>
290
+ const targets = [];
291
+ const clientCache = new TransformCacheCollection();
292
+ const ssrCache = new TransformCacheCollection();
293
+ const caches = new Set([clientCache, ssrCache]);
294
+ let evalBrokersDisposed = false;
295
+ const disposeEvalBrokers = () => {
296
+ if (evalBrokersDisposed) return;
297
+ evalBrokersDisposed = true;
298
+ for (const cache of caches) {
299
+ disposeEvalBroker(cache);
300
+ }
301
+ };
302
+ const getCache = (isSsr) => isSsr ? ssrCache : clientCache;
303
+ const isInsideCacheDir = (filename) => {
304
+ if (!config.cacheDir) {
305
+ return false;
306
+ }
307
+ const relative = path.relative(config.cacheDir, filename);
308
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
309
+ };
310
+ const getDepsOptimizer = () => {
311
+ if (!devServer) return null;
312
+ const server = devServer;
313
+ return server.environments?.client?.depsOptimizer ?? server.depsOptimizer ?? server._depsOptimizer ?? null;
314
+ };
315
+ const waitForOptimizedDep = async (filename) => {
316
+ const depsOptimizer = getDepsOptimizer();
317
+ if (!depsOptimizer?.isOptimizedDepFile?.(filename)) {
318
+ return false;
319
+ }
320
+ await depsOptimizer.init?.();
321
+ await depsOptimizer.scanProcessing;
322
+ const info = depsOptimizer.metadata?.depInfoList?.find((item) => item.file === filename);
323
+ if (info?.processing) {
324
+ await info.processing;
325
+ }
326
+ return true;
327
+ };
328
+ let viteResolver = null;
329
+ const resolveClient = (what, importer) => {
330
+ if (!viteResolver) {
331
+ throw new Error("Vite resolver is not initialized yet");
332
+ }
333
+ return viteResolver(what, importer, false, false);
334
+ };
335
+ const resolveSsr = (what, importer) => {
336
+ if (!viteResolver) {
337
+ throw new Error("Vite resolver is not initialized yet");
338
+ }
339
+ return viteResolver(what, importer, false, true);
340
+ };
341
+ const createAsyncResolver = asyncResolverFactory(async (resolved, what, importer, stack) => {
342
+ const log = logger.extend("vite").extend(getFileIdx(importer));
343
+ if (resolved) {
344
+ log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
345
+ // Vite adds param like `?v=667939b3` to cached modules
346
+ let resolvedId = resolved.split("?", 1)[0];
347
+ if (resolvedId.startsWith("\0")) {
348
+ // \0 is a special character in Rollup that tells Rollup to not include this in the bundle
349
+ // https://rollupjs.org/guide/en/#outputexports
350
+ return null;
351
+ }
352
+ if (resolvedId.startsWith(VITE_FS_PREFIX)) {
353
+ resolvedId = normalizeViteFsPath(resolvedId);
354
+ }
355
+ if (resolvedId.startsWith("/@")) {
356
+ return null;
357
+ }
358
+ if (!existsSync(resolvedId)) {
359
+ // When Vite resolves to an optimized deps entry (cacheDir) it may not be written yet.
360
+ // Wait for Vite's optimizer instead of calling optimizeDeps() manually (deprecated in Vite 7).
361
+ try {
362
+ await waitForOptimizedDep(resolvedId);
363
+ } catch {}
364
+ // Vite can return an optimized deps entry (from cacheDir) before it's written to disk.
365
+ // Manually calling optimizeDeps is deprecated in Vite 7 and can also get called many times.
366
+ // Instead, fall back to resolving the original module path directly.
367
+ if (!existsSync(resolvedId) && isInsideCacheDir(resolvedId)) {
368
+ try {
369
+ return syncResolve(what, importer, stack);
370
+ } catch {}
371
+ }
372
+ }
373
+ if (!existsSync(resolvedId) && !path.isAbsolute(resolvedId)) {
374
+ // Vite can resolve an import to a bare specifier when bundling for SSR and marking it as external.
375
+ // In that case we still need a real file path for WyW evaluation.
376
+ return syncResolve(what, importer, stack);
377
+ }
378
+ return resolvedId;
379
+ }
380
+ log("resolve ❌ '%s'@'%s", what, importer);
381
+ // Vite can inject virtual ids like /@react-refresh in dev.
382
+ if (what.startsWith("/@") || what.startsWith("\0")) {
383
+ return null;
384
+ }
385
+ if (!what.startsWith(".") && !what.startsWith("/") && !path.isAbsolute(what)) {
386
+ // Keep compatibility with SSR externalization: fall back to Node resolution for bare specifiers.
387
+ return syncResolve(what, importer, stack);
388
+ }
389
+ throw new Error(`Could not resolve ${what}`);
390
+ }, (what, importer) => [what, importer]);
391
+ const asyncResolveClient = createAsyncResolver(resolveClient);
392
+ const asyncResolveSsr = createAsyncResolver(resolveSsr);
393
+ return {
394
+ name: "wyw-in-js",
395
+ enforce: "post",
396
+ buildStart() {
397
+ Object.keys(metadataLookup).forEach((key) => {
398
+ delete metadataLookup[key];
399
+ });
400
+ },
401
+ buildEnd() {
402
+ onDone(process.cwd());
403
+ if (config.command === "build") {
404
+ disposeEvalBrokers();
405
+ }
406
+ },
407
+ configResolved(resolvedConfig) {
408
+ config = resolvedConfig;
409
+ viteResolver = config.createResolver();
410
+ if (preserveCssPaths && config.command === "build") {
411
+ const outputs = config.build.rollupOptions.output;
412
+ let outputEntries = [];
413
+ if (Array.isArray(outputs)) {
414
+ outputEntries = outputs;
415
+ } else if (outputs) {
416
+ outputEntries = [outputs];
417
+ }
418
+ outputEntries.forEach((entry) => {
419
+ if (!entry || typeof entry !== "object") return;
420
+ const output = entry;
421
+ if (!output.preserveModules) return;
422
+ const template = output.assetFileNames ?? `${config.build.assetsDir ?? "assets"}/[name].[hash].[ext]`;
423
+ const assetFileNames = getWywCssAssetFileNames(config, output, template);
424
+ if (assetFileNames) output.assetFileNames = assetFileNames;
425
+ });
426
+ }
427
+ const envPrefix = config.envPrefix ?? "VITE_";
428
+ const envDir = "envDir" in config && typeof config.envDir === "string" ? config.envDir : config.root;
429
+ const loaded = loadEnv(config.mode, envDir, envPrefix);
430
+ const base = {
431
+ ...loaded,
432
+ BASE_URL: config.base,
433
+ MODE: config.mode,
434
+ DEV: config.command === "serve",
435
+ PROD: config.command === "build"
436
+ };
437
+ importMetaEnvForEval = {
438
+ client: {
439
+ ...base,
440
+ SSR: false
441
+ },
442
+ ssr: {
443
+ ...base,
444
+ SSR: true
445
+ }
446
+ };
447
+ },
448
+ configureServer(_server) {
449
+ devServer = _server;
450
+ devServer.httpServer?.once("close", disposeEvalBrokers);
451
+ if (ssrDevCssEnabled && config.command === "serve") {
452
+ devServer.middlewares.use((req, res, next) => {
453
+ const { url } = req;
454
+ if (!url) {
455
+ next();
456
+ return;
457
+ }
458
+ const [pathname] = url.split("?", 1);
459
+ if (pathname !== ssrDevCssRoute) {
460
+ next();
461
+ return;
462
+ }
463
+ const etag = `W/"${ssrDevCssVersion}"`;
464
+ const ifNoneMatch = req.headers["if-none-match"];
465
+ if (ifNoneMatch === etag) {
466
+ res.statusCode = 304;
467
+ res.end();
468
+ return;
469
+ }
470
+ res.statusCode = 200;
471
+ res.setHeader("Content-Type", "text/css; charset=utf-8");
472
+ res.setHeader("Cache-Control", "no-cache");
473
+ res.setHeader("ETag", etag);
474
+ res.end(getSsrDevCssContents());
475
+ });
476
+ }
477
+ },
478
+ transformIndexHtml(html) {
479
+ if (!ssrDevCssEnabled || config.command !== "serve") return undefined;
480
+ return {
481
+ html,
482
+ tags: [{
483
+ tag: "link",
484
+ attrs: {
485
+ rel: "stylesheet",
486
+ href: getSsrDevCssHref()
487
+ },
488
+ injectTo: "head-prepend"
489
+ }]
490
+ };
491
+ },
492
+ load(url) {
493
+ const [id] = url.split("?", 1);
494
+ return cssLookup[id];
495
+ },
496
+ /* eslint-disable-next-line consistent-return */
497
+ resolveId(importeeUrl) {
498
+ const [id] = importeeUrl.split("?", 1);
499
+ if (cssLookup[id]) return id;
500
+ return cssFileLookup[id];
501
+ },
502
+ handleHotUpdate(ctx) {
503
+ // it's module, so just transform it
504
+ if (ctx.modules.length) return ctx.modules;
505
+ // Select affected modules of changed dependency
506
+ const affected = targets.filter((x) => x.dependencies.some((dep) => dep === ctx.file) || x.dependencies.some((dep) => ctx.modules.some((m) => m.file === dep)));
507
+ const deps = affected.flatMap((target) => target.dependencies);
508
+ // eslint-disable-next-line no-restricted-syntax
509
+ for (const depId of deps) {
510
+ for (const cache of caches) {
511
+ cache.invalidateForFile(depId);
512
+ }
513
+ }
514
+ return affected.map((target) => devServer.moduleGraph.getModuleById(target.id)).concat(ctx.modules).filter((m) => !!m);
515
+ },
516
+ generateBundle(outputOptions, bundle) {
517
+ Object.entries(metadataLookup).forEach(([fileName, source]) => {
518
+ this.emitFile({
519
+ fileName,
520
+ source,
521
+ type: "asset"
522
+ });
523
+ });
524
+ if (config.command !== "build") return;
525
+ if (!outputOptions.preserveModules) return;
526
+ if (config.build.cssCodeSplit === false) return;
527
+ Object.values(bundle).forEach((item) => {
528
+ if (!isOutputChunkLike(item)) {
529
+ return;
530
+ }
531
+ const chunk = item;
532
+ const moduleId = getTrackedModuleIdForChunk(chunk, cssFilesByModuleId);
533
+ if (!moduleId) {
534
+ return;
535
+ }
536
+ const cssFilename = cssFilesByModuleId.get(moduleId);
537
+ if (!cssFilename) {
538
+ return;
539
+ }
540
+ const emittedCssFileName = findWywCssAssetFileName(bundle, cssFilename, config.root);
541
+ if (!emittedCssFileName) {
542
+ return;
543
+ }
544
+ const relativeCssImport = getRelativeImportPath(chunk.fileName, emittedCssFileName);
545
+ if (hasCssLoadStatement(chunk.code, relativeCssImport, outputOptions.format)) {
546
+ return;
547
+ }
548
+ chunk.code = prependCssLoadStatement(chunk.code, relativeCssImport, outputOptions.format);
549
+ });
550
+ },
551
+ async transform(code, url, transformOptions) {
552
+ const [id] = url.split("?", 1);
553
+ // Do not transform ignored and generated files
554
+ if (!transformLibraries && url.includes("node_modules") || !filter(url) || id in cssLookup) return;
555
+ const log = logger.extend("vite").extend(getFileIdx(id));
556
+ log("transform %s", id);
557
+ const isSsr = typeof transformOptions === "boolean" ? transformOptions : Boolean(transformOptions?.ssr);
558
+ const overrideContext = isSsr ? overrideContextSsr : overrideContextClient;
559
+ const transformServices = {
560
+ options: {
561
+ filename: id,
562
+ root: process.cwd(),
563
+ prefixer,
564
+ keepComments,
565
+ preprocessor,
566
+ pluginOptions: {
567
+ ...rest,
568
+ overrideContext
569
+ }
570
+ },
571
+ cache: getCache(isSsr),
572
+ emitWarning: (message) => this.warn(message),
573
+ eventEmitter: emitter
574
+ };
575
+ const asyncResolve = isSsr ? asyncResolveSsr : asyncResolveClient;
576
+ const result = await transform(transformServices, code, asyncResolve);
577
+ result.diagnostics?.forEach((diagnostic) => {
578
+ this.warn({
579
+ id: diagnostic.filename,
580
+ loc: diagnostic.start ? {
581
+ column: diagnostic.start.column,
582
+ file: diagnostic.filename,
583
+ line: diagnostic.start.line
584
+ } : undefined,
585
+ message: `[wyw-in-js] ${diagnostic.severity} [${diagnostic.category}] ${diagnostic.message}`,
586
+ pluginCode: diagnostic.category
587
+ });
588
+ });
589
+ const relativeId = normalizeToPosix(path.relative(config.root, id));
590
+ const metadataFilename = replaceModuleExtension(id, ".wyw-in-js.json");
591
+ const metadataRelativePath = toBundleRelativePath(metadataFilename);
592
+ delete metadataLookup[metadataRelativePath];
593
+ if (result.metadata) {
594
+ const cssFile = typeof result.cssText === "string" && result.cssText !== "" ? replaceModuleExtension(relativeId, ".wyw-in-js.css") : undefined;
595
+ metadataLookup[metadataRelativePath] = stringifyMetadataManifest(createMetadataManifest(result.metadata, {
596
+ cssFile,
597
+ source: relativeId
598
+ }));
599
+ }
600
+ let { cssText, dependencies } = result;
601
+ // Heads up, there are three cases:
602
+ // 1. cssText is undefined, it means that file was not transformed
603
+ // 2. cssText is empty, it means that file was transformed, but it does not contain any styles
604
+ // 3. cssText is not empty, it means that file was transformed and it contains styles
605
+ if (typeof cssText === "undefined") {
606
+ cssFilesByModuleId.delete(id);
607
+ return;
608
+ }
609
+ if (cssText === "") {
610
+ cssFilesByModuleId.delete(id);
611
+ /* eslint-disable-next-line consistent-return */
612
+ return {
613
+ code: result.code,
614
+ map: result.sourceMap
615
+ };
616
+ }
617
+ dependencies ??= [];
618
+ const cssFilename = normalizeToPosix(replaceModuleExtension(id, ".wyw-in-js.css"));
619
+ cssFilesByModuleId.set(id, cssFilename);
620
+ const cssRelativePath = normalizeToPosix(path.relative(config.root, cssFilename));
621
+ const cssId = `/${cssRelativePath}`;
622
+ if (sourceMap && result.cssSourceMapText) {
623
+ const map = Buffer.from(result.cssSourceMapText).toString("base64");
624
+ cssText += `/*# sourceMappingURL=data:application/json;base64,${map}*/`;
625
+ }
626
+ const didCssChange = cssLookup[cssFilename] !== cssText;
627
+ cssLookup[cssFilename] = cssText;
628
+ cssFileLookup[cssId] = cssFilename;
629
+ result.code += `\nimport ${JSON.stringify(cssFilename)};\n`;
630
+ for (let i = 0, end = dependencies.length; i < end; i++) {
631
+ // eslint-disable-next-line no-await-in-loop
632
+ const depModule = await this.resolve(dependencies[i], url, { isEntry: false });
633
+ if (depModule) dependencies[i] = depModule.id;
634
+ }
635
+ const target = targets.find((t) => t.id === id);
636
+ if (!target) targets.push({
637
+ id,
638
+ dependencies
639
+ });
640
+ else target.dependencies = dependencies;
641
+ if (didCssChange) {
642
+ const reloadTarget = getCssReloadTarget(this.environment, devServer);
643
+ if (reloadTarget) {
644
+ scheduleCssReload(reloadTarget, cssFilename);
645
+ }
646
+ if (ssrDevCssEnabled && config.command === "serve") {
647
+ ssrDevCssVersion += 1;
648
+ }
649
+ }
650
+ /* eslint-disable-next-line consistent-return */
651
+ return {
652
+ code: result.code,
653
+ map: result.sourceMap
654
+ };
655
+ }
656
+ };
614
657
  }
615
- //# sourceMappingURL=index.mjs.map
658
+ //# sourceMappingURL=index.mjs.map