@wyw-in-js/vite 0.8.0 → 1.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/types/index.js CHANGED
@@ -8,21 +8,238 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
8
8
  return (mod && mod.__esModule) ? mod : { "default": mod };
9
9
  };
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.default = wywInJS;
11
12
  const fs_1 = require("fs");
12
13
  const path_1 = __importDefault(require("path"));
13
14
  const vite_1 = require("vite");
14
15
  const shared_1 = require("@wyw-in-js/shared");
15
16
  const transform_1 = require("@wyw-in-js/transform");
16
- function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, ...rest } = {}) {
17
+ const isWindowsAbsolutePath = (value) => /^[a-zA-Z]:[\\/]/.test(value);
18
+ const normalizeToPosix = (value) => value.replace(/\\/g, path_1.default.posix.sep);
19
+ const isInside = (childPath, parentPath) => {
20
+ const rel = path_1.default.relative(parentPath, childPath);
21
+ return rel === '' || (!rel.startsWith('..') && !path_1.default.isAbsolute(rel));
22
+ };
23
+ const isWywCssAssetName = (value) => value.endsWith('.wyw-in-js.css');
24
+ const normalizeAssetRelativePath = (value) => {
25
+ const normalized = path_1.default.posix.normalize(normalizeToPosix(value).replace(/^\/+/, ''));
26
+ if (normalized.startsWith('..') || path_1.default.posix.isAbsolute(normalized)) {
27
+ return null;
28
+ }
29
+ return normalized;
30
+ };
31
+ const stripExtension = (value) => {
32
+ const ext = path_1.default.posix.extname(value);
33
+ return ext ? value.slice(0, -ext.length) : value;
34
+ };
35
+ const getWywCssAssetFileNames = (resolvedConfig, output, originalAssetFileNames) => {
36
+ if (!output.preserveModules)
37
+ return null;
38
+ const rootDir = resolvedConfig.root;
39
+ const preserveModulesRootValue = output.preserveModulesRoot;
40
+ let preserveModulesRootAbs = null;
41
+ if (typeof preserveModulesRootValue === 'string') {
42
+ preserveModulesRootAbs = path_1.default.isAbsolute(preserveModulesRootValue)
43
+ ? preserveModulesRootValue
44
+ : path_1.default.resolve(rootDir, preserveModulesRootValue);
45
+ }
46
+ const preserveModulesRootRel = preserveModulesRootAbs && isInside(preserveModulesRootAbs, rootDir)
47
+ ? normalizeToPosix(path_1.default.relative(rootDir, preserveModulesRootAbs))
48
+ : null;
49
+ return (assetInfo) => {
50
+ const template = typeof originalAssetFileNames === 'function'
51
+ ? originalAssetFileNames(assetInfo)
52
+ : originalAssetFileNames;
53
+ const assetName = assetInfo?.name;
54
+ if (typeof assetName !== 'string' || !isWywCssAssetName(assetName)) {
55
+ return template;
56
+ }
57
+ if (!template.includes('[')) {
58
+ return template;
59
+ }
60
+ let relativePath = null;
61
+ const assetNameNormalized = normalizeToPosix(assetName);
62
+ if (path_1.default.isAbsolute(assetName) ||
63
+ isWindowsAbsolutePath(assetNameNormalized)) {
64
+ const preserveRel = preserveModulesRootAbs && isInside(assetName, preserveModulesRootAbs)
65
+ ? path_1.default.relative(preserveModulesRootAbs, assetName)
66
+ : null;
67
+ if (preserveRel &&
68
+ !path_1.default.isAbsolute(preserveRel) &&
69
+ !preserveRel.startsWith('..')) {
70
+ relativePath = preserveRel;
71
+ }
72
+ else if (isInside(assetName, rootDir)) {
73
+ relativePath = path_1.default.relative(rootDir, assetName);
74
+ }
75
+ }
76
+ else if (preserveModulesRootRel &&
77
+ assetNameNormalized.startsWith(`${preserveModulesRootRel}/`)) {
78
+ relativePath = assetNameNormalized.slice(preserveModulesRootRel.length + 1);
79
+ }
80
+ else {
81
+ relativePath = assetNameNormalized;
82
+ }
83
+ const normalized = relativePath
84
+ ? normalizeAssetRelativePath(relativePath)
85
+ : null;
86
+ if (!normalized) {
87
+ return template;
88
+ }
89
+ const withoutExt = stripExtension(normalized);
90
+ if (template.includes('[name]')) {
91
+ const dir = path_1.default.posix.dirname(withoutExt);
92
+ if (dir === '.' || dir === '') {
93
+ return template;
94
+ }
95
+ return template.replace(/\[name\]/g, `${dir}/[name]`);
96
+ }
97
+ const dir = path_1.default.posix.dirname(withoutExt);
98
+ if (dir === '.' || dir === '') {
99
+ return template;
100
+ }
101
+ const idx = template.indexOf('[');
102
+ if (idx < 0) {
103
+ return template;
104
+ }
105
+ const prefix = template.slice(0, idx);
106
+ if (prefix !== '' && !prefix.endsWith('/')) {
107
+ return template;
108
+ }
109
+ return `${prefix}${dir}/${template.slice(idx)}`;
110
+ };
111
+ };
112
+ function wywInJS({ debug, include, exclude, sourceMap, preserveCssPaths, keepComments, prefixer, preprocessor, ssrDevCss, ssrDevCssPath, transformLibraries, ...rest } = {}) {
17
113
  const filter = (0, vite_1.createFilter)(include, exclude);
18
114
  const cssLookup = {};
19
115
  const cssFileLookup = {};
116
+ const pendingCssReloads = new Set();
117
+ let pendingCssReloadTimer;
118
+ let ssrDevCssVersion = 0;
20
119
  let config;
21
120
  let devServer;
121
+ let importMetaEnvForEval = null;
122
+ const ssrDevCssEnabled = Boolean(ssrDevCss);
123
+ const [ssrDevCssPathname, ssrDevCssQuery] = (ssrDevCssPath ?? '/_wyw-in-js/ssr.css').split('?', 2);
124
+ const ssrDevCssRoute = ssrDevCssPathname.startsWith('/')
125
+ ? ssrDevCssPathname
126
+ : `/${ssrDevCssPathname}`;
127
+ const getSsrDevCssHref = () => {
128
+ const versionParam = `v=${ssrDevCssVersion}`;
129
+ const query = ssrDevCssQuery
130
+ ? `${ssrDevCssQuery}&${versionParam}`
131
+ : versionParam;
132
+ return `${ssrDevCssRoute}?${query}`;
133
+ };
134
+ const getSsrDevCssContents = () => {
135
+ const entries = Object.entries(cssLookup);
136
+ if (entries.length === 0)
137
+ return '';
138
+ const merged = entries
139
+ .sort(([a], [b]) => a.localeCompare(b))
140
+ .map(([, cssText]) => cssText)
141
+ .join('\n');
142
+ return `${merged}\n`;
143
+ };
22
144
  const { emitter, onDone } = (0, transform_1.createFileReporter)(debug ?? false);
145
+ const scheduleCssReload = (cssFilename) => {
146
+ if (!devServer?.moduleGraph)
147
+ return;
148
+ pendingCssReloads.add(cssFilename);
149
+ if (pendingCssReloadTimer)
150
+ return;
151
+ pendingCssReloadTimer = setTimeout(() => {
152
+ pendingCssReloadTimer = undefined;
153
+ const ids = Array.from(pendingCssReloads);
154
+ pendingCssReloads.clear();
155
+ for (const id of ids) {
156
+ const module = devServer.moduleGraph.getModuleById(id);
157
+ if (module)
158
+ devServer.reloadModule(module);
159
+ }
160
+ }, 0);
161
+ };
23
162
  // <dependency id, targets>
24
163
  const targets = [];
25
164
  const cache = new transform_1.TransformCacheCollection();
165
+ const isInsideCacheDir = (filename) => {
166
+ if (!config.cacheDir) {
167
+ return false;
168
+ }
169
+ const relative = path_1.default.relative(config.cacheDir, filename);
170
+ return (relative !== '' &&
171
+ !relative.startsWith('..') &&
172
+ !path_1.default.isAbsolute(relative));
173
+ };
174
+ const getDepsOptimizer = () => {
175
+ if (!devServer)
176
+ return null;
177
+ const server = devServer;
178
+ return (server.environments?.client?.depsOptimizer ??
179
+ server.depsOptimizer ??
180
+ server._depsOptimizer ??
181
+ null);
182
+ };
183
+ const waitForOptimizedDep = async (filename) => {
184
+ const depsOptimizer = getDepsOptimizer();
185
+ if (!depsOptimizer?.isOptimizedDepFile?.(filename)) {
186
+ return false;
187
+ }
188
+ await depsOptimizer.init?.();
189
+ await depsOptimizer.scanProcessing;
190
+ const info = depsOptimizer.metadata?.depInfoList?.find((item) => item.file === filename);
191
+ if (info?.processing) {
192
+ await info.processing;
193
+ }
194
+ return true;
195
+ };
196
+ const createAsyncResolver = (0, shared_1.asyncResolverFactory)(async (resolved, what, importer, stack) => {
197
+ const log = shared_1.logger.extend('vite').extend((0, transform_1.getFileIdx)(importer));
198
+ if (resolved) {
199
+ if (resolved.external) {
200
+ // If module is marked as external, Rollup will not resolve it,
201
+ // so we need to resolve it ourselves with default resolver
202
+ const resolvedId = (0, shared_1.syncResolve)(what, importer, stack);
203
+ log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
204
+ return resolvedId;
205
+ }
206
+ log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
207
+ // Vite adds param like `?v=667939b3` to cached modules
208
+ const resolvedId = resolved.id.split('?', 1)[0];
209
+ if (resolvedId.startsWith('\0')) {
210
+ // \0 is a special character in Rollup that tells Rollup to not include this in the bundle
211
+ // https://rollupjs.org/guide/en/#outputexports
212
+ return null;
213
+ }
214
+ if (resolvedId.startsWith('/@')) {
215
+ return null;
216
+ }
217
+ if (!(0, fs_1.existsSync)(resolvedId)) {
218
+ // When Vite resolves to an optimized deps entry (cacheDir) it may not be written yet.
219
+ // Wait for Vite's optimizer instead of calling optimizeDeps() manually (deprecated in Vite 7).
220
+ try {
221
+ await waitForOptimizedDep(resolvedId);
222
+ }
223
+ catch {
224
+ // If optimizer failed, fall through to preserve previous behavior and surface the error.
225
+ }
226
+ // Vite can return an optimized deps entry (from cacheDir) before it's written to disk.
227
+ // Manually calling optimizeDeps is deprecated in Vite 7 and can also get called many times.
228
+ // Instead, fall back to resolving the original module path directly.
229
+ if (!(0, fs_1.existsSync)(resolvedId) && isInsideCacheDir(resolvedId)) {
230
+ try {
231
+ return (0, shared_1.syncResolve)(what, importer, stack);
232
+ }
233
+ catch {
234
+ // Fall through to preserve previous behavior: return resolvedId and let WyW surface the error.
235
+ }
236
+ }
237
+ }
238
+ return resolvedId;
239
+ }
240
+ log("resolve ❌ '%s'@'%s", what, importer);
241
+ throw new Error(`Could not resolve ${what}`);
242
+ }, (what, importer) => [what, importer]);
26
243
  return {
27
244
  name: 'wyw-in-js',
28
245
  enforce: 'post',
@@ -31,9 +248,89 @@ function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, .
31
248
  },
32
249
  configResolved(resolvedConfig) {
33
250
  config = resolvedConfig;
251
+ if (preserveCssPaths && config.command === 'build') {
252
+ const outputs = config.build.rollupOptions.output;
253
+ let outputEntries = [];
254
+ if (Array.isArray(outputs)) {
255
+ outputEntries = outputs;
256
+ }
257
+ else if (outputs) {
258
+ outputEntries = [outputs];
259
+ }
260
+ outputEntries.forEach((entry) => {
261
+ if (!entry || typeof entry !== 'object')
262
+ return;
263
+ const output = entry;
264
+ if (!output.preserveModules)
265
+ return;
266
+ const template = output.assetFileNames ??
267
+ `${config.build.assetsDir ?? 'assets'}/[name].[hash].[ext]`;
268
+ const assetFileNames = getWywCssAssetFileNames(config, output, template);
269
+ if (assetFileNames)
270
+ output.assetFileNames = assetFileNames;
271
+ });
272
+ }
273
+ const envPrefix = config.envPrefix ?? 'VITE_';
274
+ const envDir =
275
+ // envDir is absolute in modern Vite, but keep a fallback for older versions
276
+ 'envDir' in config && typeof config.envDir === 'string'
277
+ ? config.envDir
278
+ : config.root;
279
+ const loaded = (0, vite_1.loadEnv)(config.mode, envDir, envPrefix);
280
+ const base = {
281
+ ...loaded,
282
+ BASE_URL: config.base,
283
+ MODE: config.mode,
284
+ DEV: config.command === 'serve',
285
+ PROD: config.command === 'build',
286
+ };
287
+ importMetaEnvForEval = {
288
+ client: { ...base, SSR: false },
289
+ ssr: { ...base, SSR: true },
290
+ };
34
291
  },
35
292
  configureServer(_server) {
36
293
  devServer = _server;
294
+ if (!ssrDevCssEnabled || config.command !== 'serve')
295
+ return;
296
+ devServer.middlewares.use((req, res, next) => {
297
+ const { url } = req;
298
+ if (!url) {
299
+ next();
300
+ return;
301
+ }
302
+ const [pathname] = url.split('?', 1);
303
+ if (pathname !== ssrDevCssRoute) {
304
+ next();
305
+ return;
306
+ }
307
+ const etag = `W/"${ssrDevCssVersion}"`;
308
+ const ifNoneMatch = req.headers['if-none-match'];
309
+ if (ifNoneMatch === etag) {
310
+ res.statusCode = 304;
311
+ res.end();
312
+ return;
313
+ }
314
+ res.statusCode = 200;
315
+ res.setHeader('Content-Type', 'text/css; charset=utf-8');
316
+ res.setHeader('Cache-Control', 'no-cache');
317
+ res.setHeader('ETag', etag);
318
+ res.end(getSsrDevCssContents());
319
+ });
320
+ },
321
+ transformIndexHtml(html) {
322
+ if (!ssrDevCssEnabled || config.command !== 'serve')
323
+ return undefined;
324
+ return {
325
+ html,
326
+ tags: [
327
+ {
328
+ tag: 'link',
329
+ attrs: { rel: 'stylesheet', href: getSsrDevCssHref() },
330
+ injectTo: 'head-prepend',
331
+ },
332
+ ],
333
+ };
37
334
  },
38
335
  load(url) {
39
336
  const [id] = url.split('?', 1);
@@ -66,51 +363,44 @@ function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, .
66
363
  .concat(ctx.modules)
67
364
  .filter((m) => !!m);
68
365
  },
69
- async transform(code, url) {
366
+ async transform(code, url, transformOptions) {
70
367
  const [id] = url.split('?', 1);
71
368
  // Do not transform ignored and generated files
72
- if (url.includes('node_modules') || !filter(url) || id in cssLookup)
369
+ if ((!transformLibraries && url.includes('node_modules')) ||
370
+ !filter(url) ||
371
+ id in cssLookup)
73
372
  return;
74
373
  const log = shared_1.logger.extend('vite').extend((0, transform_1.getFileIdx)(id));
75
374
  log('transform %s', id);
76
- const asyncResolve = async (what, importer, stack) => {
77
- const resolved = await this.resolve(what, importer);
78
- if (resolved) {
79
- if (resolved.external) {
80
- // If module is marked as external, Rollup will not resolve it,
81
- // so we need to resolve it ourselves with default resolver
82
- const resolvedId = (0, shared_1.syncResolve)(what, importer, stack);
83
- log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
84
- return resolvedId;
85
- }
86
- log("resolve ✅ '%s'@'%s -> %O\n%s", what, importer, resolved);
87
- // Vite adds param like `?v=667939b3` to cached modules
88
- const resolvedId = resolved.id.split('?', 1)[0];
89
- if (resolvedId.startsWith('\0')) {
90
- // \0 is a special character in Rollup that tells Rollup to not include this in the bundle
91
- // https://rollupjs.org/guide/en/#outputexports
92
- return null;
93
- }
94
- if (!(0, fs_1.existsSync)(resolvedId)) {
95
- await (0, vite_1.optimizeDeps)(config);
96
- }
97
- return resolvedId;
98
- }
99
- log("resolve ❌ '%s'@'%s", what, importer);
100
- throw new Error(`Could not resolve ${what}`);
375
+ const overrideContext = (context, filename) => {
376
+ const isSsr = typeof transformOptions === 'boolean'
377
+ ? transformOptions
378
+ : Boolean(transformOptions?.ssr);
379
+ const env = importMetaEnvForEval?.[isSsr ? 'ssr' : 'client'];
380
+ const withEnv = env
381
+ ? { ...context, __wyw_import_meta_env: env }
382
+ : context;
383
+ return rest.overrideContext
384
+ ? rest.overrideContext(withEnv, filename)
385
+ : withEnv;
101
386
  };
102
387
  const transformServices = {
103
388
  options: {
104
389
  filename: id,
105
390
  root: process.cwd(),
106
391
  prefixer,
392
+ keepComments,
107
393
  preprocessor,
108
- pluginOptions: rest,
394
+ pluginOptions: {
395
+ ...rest,
396
+ overrideContext,
397
+ },
109
398
  },
110
399
  cache,
400
+ emitWarning: (message) => this.warn(message),
111
401
  eventEmitter: emitter,
112
402
  };
113
- const result = await (0, transform_1.transform)(transformServices, code, asyncResolve);
403
+ const result = await (0, transform_1.transform)(transformServices, code, createAsyncResolver(this.resolve));
114
404
  let { cssText, dependencies } = result;
115
405
  // Heads up, there are three cases:
116
406
  // 1. cssText is undefined, it means that file was not transformed
@@ -138,6 +428,7 @@ function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, .
138
428
  const map = Buffer.from(result.cssSourceMapText).toString('base64');
139
429
  cssText += `/*# sourceMappingURL=data:application/json;base64,${map}*/`;
140
430
  }
431
+ const didCssChange = cssLookup[cssFilename] !== cssText;
141
432
  cssLookup[cssFilename] = cssText;
142
433
  cssFileLookup[cssId] = cssFilename;
143
434
  result.code += `\nimport ${JSON.stringify(cssFilename)};\n`;
@@ -154,10 +445,10 @@ function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, .
154
445
  targets.push({ id, dependencies });
155
446
  else
156
447
  target.dependencies = dependencies;
157
- if (devServer?.moduleGraph) {
158
- const module = devServer.moduleGraph.getModuleById(cssFilename);
159
- if (module) {
160
- devServer.reloadModule(module);
448
+ if (didCssChange) {
449
+ scheduleCssReload(cssFilename);
450
+ if (ssrDevCssEnabled && config.command === 'serve') {
451
+ ssrDevCssVersion += 1;
161
452
  }
162
453
  }
163
454
  /* eslint-disable-next-line consistent-return */
@@ -165,4 +456,3 @@ function wywInJS({ debug, include, exclude, sourceMap, prefixer, preprocessor, .
165
456
  },
166
457
  };
167
458
  }
168
- exports.default = wywInJS;
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Anton Evzhakov
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.