-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathvite-plugin-lit-css.ts
More file actions
103 lines (85 loc) · 3.09 KB
/
vite-plugin-lit-css.ts
File metadata and controls
103 lines (85 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import type { Plugin } from 'vite';
import type { Options } from '@pwrs/lit-css';
import { createFilter, type FilterPattern } from '@rollup/pluginutils';
import { transform } from '@pwrs/lit-css';
import { readFile } from 'node:fs/promises';
export interface LitCSSOptions extends Omit<Options, 'css'> {
/**
* Files to include for transformation
* @default /\.css$/i
*/
include?: FilterPattern;
/**
* Files to exclude from transformation
*/
exclude?: FilterPattern;
}
/**
* Vite plugin to import CSS files as tagged template literals
*
* @param options - Plugin configuration options
* @returns Vite plugin
*/
export function litCSS(options?: LitCSSOptions): Plugin {
const {
exclude,
include = /\.css$/i,
specifier,
tag,
...rest
} = options ?? {};
const filter = createFilter(include, exclude);
return {
name: '@pwrs/vite-plugin-lit-css',
enforce: 'pre', // Run before Vite's built-in CSS plugin
async resolveId(source, importer, options) {
// Don't process external imports
if (!importer) return null;
// Quick check: only process potential CSS-like files
// This is just a performance optimization - the filter does the real check
if (!source.endsWith('.css') && !source.endsWith('.scss') && !source.endsWith('.sass') &&
!source.endsWith('.less') && !source.endsWith('.styl'))
return null;
// Let other plugins (alias, etc.) resolve the import first
// This handles bare specifiers, relative paths, and absolute paths
const resolution = await this.resolve(source, importer, {
skipSelf: true, // Don't call ourselves recursively
...options,
});
// If resolution failed or is marked external, let other plugins handle it
if (!resolution || resolution.external) return null;
// Now check if the RESOLVED path matches our filter
if (filter(resolution.id)) {
// Return with .js extension so Rollup treats it as JavaScript
// We use \0 prefix to mark this as a virtual module
return `\0${resolution.id}.lit-css.js`;
}
return null;
},
async load(id) {
// Check if this is one of our virtual modules
if (!id.includes('.lit-css.js')) return null;
// Extract the original CSS file path (remove \0 prefix and .lit-css.js suffix)
const cleanId = id.replace(/^\0/, '').replace(/\.lit-css\.js$/, '');
// Watch the original CSS file so changes trigger rebuilds
this.addWatchFile(cleanId);
try {
const css = await readFile(cleanId, 'utf8');
const code = await transform({ css, specifier, tag, filePath: cleanId, ...rest });
return {
code,
map: { mappings: '' },
};
} catch (error) {
this.error(error?.message ?? String(error));
}
},
handleHotUpdate({ file, server, modules }) {
if (!filter(file)) return;
const virtualId = `\0${file}.lit-css.js`;
const module = server.moduleGraph.getModuleById(virtualId);
if (module) return [...modules, module];
},
};
}
export default litCSS;