@flairjs/webpack-loader 0.0.1-beta.1 → 0.0.1-beta.10
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/README.md +146 -0
- package/dist/cjs/index.js +104 -47
- package/dist/esm/index.js +105 -47
- package/dist/types/index.d.ts +33 -6
- package/package.json +12 -7
- package/dist/types/index.js +0 -4148
package/README.md
CHANGED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @flairjs/webpack-loader
|
|
2
|
+
|
|
3
|
+
Webpack loader for Flair CSS-in-JSX.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @flairjs/webpack-loader
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// webpack.config.js
|
|
15
|
+
module.exports = {
|
|
16
|
+
module: {
|
|
17
|
+
rules: [
|
|
18
|
+
{
|
|
19
|
+
test: /\.(tsx|jsx)$/,
|
|
20
|
+
use: [
|
|
21
|
+
'@flairjs/webpack-loader'
|
|
22
|
+
],
|
|
23
|
+
exclude: /node_modules/
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// webpack.config.js
|
|
34
|
+
module.exports = {
|
|
35
|
+
module: {
|
|
36
|
+
rules: [
|
|
37
|
+
{
|
|
38
|
+
test: /\.(tsx|jsx)$/,
|
|
39
|
+
use: [
|
|
40
|
+
'babel-loader',
|
|
41
|
+
{
|
|
42
|
+
loader: '@flairjs/webpack-loader',
|
|
43
|
+
options: {
|
|
44
|
+
// Configuration options
|
|
45
|
+
cssPreprocessor: (css, id) => {
|
|
46
|
+
// Custom CSS preprocessing
|
|
47
|
+
return css
|
|
48
|
+
},
|
|
49
|
+
include: ['src/**/*.{tsx,jsx}'],
|
|
50
|
+
exclude: ['**/*.test.{tsx,jsx}'],
|
|
51
|
+
classNameList: ["className", "class"]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Configuration Options
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
interface FlairJsWebpackLoaderOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Preprocess the extracted CSS before it is passed to lightningcss
|
|
67
|
+
* @experimental
|
|
68
|
+
*/
|
|
69
|
+
cssPreprocessor?: (css: string, id: string) => string
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Files to include (default: all .tsx/.jsx files)
|
|
73
|
+
*/
|
|
74
|
+
include?: string | string[]
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Files to exclude (default: node_modules)
|
|
78
|
+
*/
|
|
79
|
+
exclude?: string | string[]
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Override the default theme file content
|
|
83
|
+
*/
|
|
84
|
+
buildThemeFile?: (theme: FlairThemeConfig) => string
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* List of class names used in the project. Supports regex.
|
|
88
|
+
*/
|
|
89
|
+
classNameList?: string[]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## CSS Handling
|
|
94
|
+
|
|
95
|
+
Ensure your webpack configuration can handle CSS files:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
// webpack.config.js
|
|
99
|
+
module.exports = {
|
|
100
|
+
module: {
|
|
101
|
+
rules: [
|
|
102
|
+
// Flair loader
|
|
103
|
+
{
|
|
104
|
+
test: /\.(tsx|jsx)$/,
|
|
105
|
+
use: ['@flairjs/webpack-loader']
|
|
106
|
+
},
|
|
107
|
+
// CSS loader for generated styles
|
|
108
|
+
{
|
|
109
|
+
test: /\.css$/,
|
|
110
|
+
use: ['style-loader', 'css-loader']
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
## Theme Integration
|
|
119
|
+
|
|
120
|
+
Create a `flair.theme.ts` in your project root:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// flair.theme.ts
|
|
124
|
+
import { defineConfig } from '@flairjs/client'
|
|
125
|
+
|
|
126
|
+
export default defineConfig({
|
|
127
|
+
tokens: {
|
|
128
|
+
colors: {
|
|
129
|
+
primary: '#3b82f6',
|
|
130
|
+
secondary: '#64748b'
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Import the theme in your entry file:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
// src/index.js
|
|
140
|
+
import '@flairjs/client/theme.css'
|
|
141
|
+
import './App'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
package/dist/cjs/index.js
CHANGED
|
@@ -71,8 +71,33 @@ function tokensToCSSVars(tokens, prefix = []) {
|
|
|
71
71
|
}
|
|
72
72
|
return css$1;
|
|
73
73
|
}
|
|
74
|
+
var Store = class {
|
|
75
|
+
fileNameToGeneratedCssNameMap = /* @__PURE__ */ new Map();
|
|
76
|
+
lastThemeUpdate = Date.now();
|
|
77
|
+
userTheme = /* @__PURE__ */ new Map();
|
|
78
|
+
getUserTheme() {
|
|
79
|
+
return this.userTheme.get(this.lastThemeUpdate) ?? null;
|
|
80
|
+
}
|
|
81
|
+
setUserTheme(theme) {
|
|
82
|
+
this.userTheme.set(this.lastThemeUpdate, theme);
|
|
83
|
+
}
|
|
84
|
+
getLastThemeUpdate() {
|
|
85
|
+
return this.lastThemeUpdate;
|
|
86
|
+
}
|
|
87
|
+
setLastThemeUpdate(timestamp) {
|
|
88
|
+
this.lastThemeUpdate = timestamp;
|
|
89
|
+
}
|
|
90
|
+
setFileNameToGeneratedCssNameMap(fileName, generatedCssName) {
|
|
91
|
+
this.fileNameToGeneratedCssNameMap.set(fileName, generatedCssName);
|
|
92
|
+
}
|
|
93
|
+
getGeneratedCssName(fileName) {
|
|
94
|
+
return this.fileNameToGeneratedCssNameMap.get(fileName);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const store = new Store();
|
|
74
98
|
const __dirname$1 = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
75
|
-
const getUserTheme = async () => {
|
|
99
|
+
const getUserTheme = async (options) => {
|
|
100
|
+
if (store.getUserTheme() && !options?.ignoreCache) return store.getUserTheme();
|
|
76
101
|
try {
|
|
77
102
|
let userThemeFilePath = path.default.resolve(process.cwd(), "flair.theme.ts");
|
|
78
103
|
if (!(0, fs.existsSync)(userThemeFilePath)) userThemeFilePath = path.default.resolve(process.cwd(), "flair.theme.js");
|
|
@@ -88,10 +113,17 @@ const getUserTheme = async () => {
|
|
|
88
113
|
});
|
|
89
114
|
const cacheBuster = Date.now();
|
|
90
115
|
const userTheme = await import(`${(0, url.pathToFileURL)(outFile).href}?update=${cacheBuster}`);
|
|
91
|
-
if (userTheme.default)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
if (userTheme.default) {
|
|
117
|
+
store.setUserTheme({
|
|
118
|
+
theme: userTheme.default,
|
|
119
|
+
originalPath: userThemeFilePath
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
theme: userTheme.default,
|
|
123
|
+
originalPath: userThemeFilePath
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
store.setUserTheme(null);
|
|
95
127
|
return null;
|
|
96
128
|
} catch (error) {
|
|
97
129
|
console.error("Error loading user theme:", error);
|
|
@@ -99,57 +131,60 @@ const getUserTheme = async () => {
|
|
|
99
131
|
}
|
|
100
132
|
};
|
|
101
133
|
const require$1 = node_module.default.createRequire(require("url").pathToFileURL(__filename).href);
|
|
102
|
-
|
|
103
|
-
* Initialize the shared plugin context
|
|
104
|
-
*/
|
|
105
|
-
async function initializeSharedContext(options = {}) {
|
|
106
|
-
const { buildThemeFile } = options;
|
|
107
|
-
const fileNameToGeneratedCssNameMap = /* @__PURE__ */ new Map();
|
|
134
|
+
const getGeneratedCssDir = () => {
|
|
108
135
|
const flairThemeFile = require$1.resolve("@flairjs/client/theme.css");
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
return node_path.default.resolve(flairThemeFile, "../generated-css");
|
|
137
|
+
};
|
|
138
|
+
const setupGeneratedCssDir = async (options) => {
|
|
139
|
+
const flairGeneratedCssDir = getGeneratedCssDir();
|
|
140
|
+
const { clearExisting = true } = options ?? {};
|
|
141
|
+
try {
|
|
142
|
+
if (!(0, node_fs.existsSync)(flairGeneratedCssDir)) await (0, node_fs_promises.mkdir)(flairGeneratedCssDir);
|
|
143
|
+
else if (clearExisting) {
|
|
144
|
+
await (0, node_fs_promises.rm)(flairGeneratedCssDir, {
|
|
145
|
+
recursive: true,
|
|
146
|
+
force: true
|
|
147
|
+
});
|
|
148
|
+
await (0, node_fs_promises.mkdir)(flairGeneratedCssDir);
|
|
149
|
+
}
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (err?.code === "EEXIST") return flairGeneratedCssDir;
|
|
152
|
+
else if ((0, node_fs.existsSync)(flairGeneratedCssDir)) return flairGeneratedCssDir;
|
|
153
|
+
console.error(`[flairjs] Could not create generated CSS directory: ${flairGeneratedCssDir}`, err);
|
|
154
|
+
return null;
|
|
117
155
|
}
|
|
156
|
+
return flairGeneratedCssDir;
|
|
157
|
+
};
|
|
158
|
+
const setupUserThemeFile = async ({ buildThemeFile, onThemeFileChange, deleteBeforeWrite = false }) => {
|
|
159
|
+
const flairThemeFile = require$1.resolve("@flairjs/client/theme.css");
|
|
118
160
|
let userTheme = await getUserTheme();
|
|
119
161
|
const buildThemeCSS = buildThemeFile ?? buildThemeTokens;
|
|
120
162
|
if (userTheme) {
|
|
121
163
|
const themeCSS = buildThemeCSS(userTheme.theme);
|
|
164
|
+
store.setLastThemeUpdate(Date.now());
|
|
165
|
+
if (deleteBeforeWrite) await (0, node_fs_promises.rm)(flairThemeFile, { force: true });
|
|
122
166
|
await (0, node_fs_promises.writeFile)(flairThemeFile, themeCSS, "utf-8");
|
|
123
167
|
(0, node_fs.watch)(userTheme.originalPath, async () => {
|
|
124
168
|
userTheme = await getUserTheme();
|
|
169
|
+
store.setLastThemeUpdate(Date.now());
|
|
125
170
|
if (!userTheme) return;
|
|
126
171
|
const themeCSS$1 = buildThemeCSS(userTheme.theme);
|
|
172
|
+
onThemeFileChange?.();
|
|
127
173
|
await (0, node_fs_promises.writeFile)(flairThemeFile, themeCSS$1, "utf-8");
|
|
128
174
|
});
|
|
129
175
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const previousGeneratedCssName = fileNameToGeneratedCssNameMap.get(filePath);
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
(0, node_fs_promises.rm)(node_path.default.join(flairGeneratedCssDir, previousGeneratedCssName));
|
|
135
|
-
}, 2e3);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
return {
|
|
139
|
-
flairThemeFile,
|
|
140
|
-
flairGeneratedCssDir,
|
|
141
|
-
userTheme,
|
|
142
|
-
buildThemeCSS,
|
|
143
|
-
refreshCssFile
|
|
144
|
-
};
|
|
145
|
-
}
|
|
176
|
+
return userTheme;
|
|
177
|
+
};
|
|
146
178
|
function shouldProcessFile(id, include, exclude) {
|
|
147
|
-
const isIncluded = (0, picomatch.default)(include
|
|
148
|
-
const isExcluded = (0, picomatch.default)(exclude
|
|
149
|
-
if (!isIncluded(id)) return false;
|
|
150
|
-
if (isExcluded(id)) return false;
|
|
179
|
+
const isIncluded = (0, picomatch.default)(include ?? ["**/*.{js,ts,jsx,tsx}"]);
|
|
180
|
+
const isExcluded = (0, picomatch.default)(exclude ?? ["**/node_modules/**"]);
|
|
181
|
+
if (!isIncluded(normalizeFilePath(id))) return false;
|
|
182
|
+
if (isExcluded(normalizeFilePath(id))) return false;
|
|
151
183
|
return true;
|
|
152
184
|
}
|
|
185
|
+
function normalizeFilePath(filePath) {
|
|
186
|
+
return filePath.replace(/\\/g, "/");
|
|
187
|
+
}
|
|
153
188
|
const colors = {
|
|
154
189
|
reset: "\x1B[0m",
|
|
155
190
|
fg: {
|
|
@@ -191,28 +226,50 @@ const transformCode = (code, filePath, options) => {
|
|
|
191
226
|
|
|
192
227
|
//#endregion
|
|
193
228
|
//#region src/index.ts
|
|
229
|
+
let initialized = false;
|
|
194
230
|
async function flairJsLoader(source, sourceMap) {
|
|
195
231
|
const callback = this.async();
|
|
232
|
+
const options = this.getOptions() || {};
|
|
196
233
|
if (!callback) {
|
|
197
234
|
console.error("@flairjs/webpack-loader requires async support");
|
|
198
235
|
return;
|
|
199
236
|
}
|
|
200
|
-
const options = this.getOptions() || {};
|
|
201
|
-
const context = await initializeSharedContext(options);
|
|
202
237
|
const fileName = this.resourcePath;
|
|
203
238
|
if (!shouldProcessFile(fileName, options?.include, options?.exclude)) return callback(null, source, sourceMap);
|
|
239
|
+
let cssGeneratedDir = null;
|
|
240
|
+
let userTheme = null;
|
|
241
|
+
if (!initialized) {
|
|
242
|
+
cssGeneratedDir = await setupGeneratedCssDir();
|
|
243
|
+
userTheme = await setupUserThemeFile({
|
|
244
|
+
buildThemeFile: options.buildThemeFile,
|
|
245
|
+
deleteBeforeWrite: true
|
|
246
|
+
});
|
|
247
|
+
initialized = true;
|
|
248
|
+
} else {
|
|
249
|
+
cssGeneratedDir = getGeneratedCssDir();
|
|
250
|
+
userTheme = await getUserTheme();
|
|
251
|
+
}
|
|
252
|
+
if (!cssGeneratedDir) {
|
|
253
|
+
console.error("[flairjs] Could not find generated CSS directory. Skipping processing.");
|
|
254
|
+
return callback(null, source, sourceMap);
|
|
255
|
+
}
|
|
204
256
|
try {
|
|
205
257
|
const result = transformCode(source, fileName, {
|
|
206
|
-
appendTimestampToCssFile:
|
|
258
|
+
appendTimestampToCssFile: false,
|
|
207
259
|
classNameList: options?.classNameList,
|
|
208
260
|
cssPreprocessor: options?.cssPreprocessor ? (css) => options.cssPreprocessor(css, fileName) : void 0,
|
|
209
|
-
theme:
|
|
210
|
-
useTheme: !!
|
|
211
|
-
cssOutDir:
|
|
261
|
+
theme: userTheme?.theme,
|
|
262
|
+
useTheme: !!userTheme,
|
|
263
|
+
cssOutDir: cssGeneratedDir
|
|
212
264
|
});
|
|
213
265
|
if (!result) return callback(null, source, sourceMap);
|
|
214
|
-
if (result.generatedCssName)
|
|
215
|
-
|
|
266
|
+
if (!result.generatedCssName) return callback(null, source, sourceMap);
|
|
267
|
+
this.addDependency(path.resolve(result.generatedCssName));
|
|
268
|
+
let resultSourcemap = null;
|
|
269
|
+
if (result.sourcemap) try {
|
|
270
|
+
resultSourcemap = JSON.parse(result.sourcemap);
|
|
271
|
+
} catch {}
|
|
272
|
+
callback(null, result.code, resultSourcemap ?? sourceMap);
|
|
216
273
|
} catch (error) {
|
|
217
274
|
console.error("[@flairjs/webpack-loader]", error);
|
|
218
275
|
callback(error, source, sourceMap);
|
package/dist/esm/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import path, { dirname } from "node:path";
|
|
|
5
5
|
import * as esbuild from "esbuild";
|
|
6
6
|
import { existsSync as existsSync$1 } from "fs";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import * as path$2 from "path";
|
|
8
9
|
import path$1 from "path";
|
|
9
10
|
import { pathToFileURL } from "url";
|
|
10
11
|
import picomatch from "picomatch";
|
|
@@ -37,8 +38,33 @@ function tokensToCSSVars(tokens, prefix = []) {
|
|
|
37
38
|
}
|
|
38
39
|
return css$1;
|
|
39
40
|
}
|
|
41
|
+
var Store = class {
|
|
42
|
+
fileNameToGeneratedCssNameMap = /* @__PURE__ */ new Map();
|
|
43
|
+
lastThemeUpdate = Date.now();
|
|
44
|
+
userTheme = /* @__PURE__ */ new Map();
|
|
45
|
+
getUserTheme() {
|
|
46
|
+
return this.userTheme.get(this.lastThemeUpdate) ?? null;
|
|
47
|
+
}
|
|
48
|
+
setUserTheme(theme) {
|
|
49
|
+
this.userTheme.set(this.lastThemeUpdate, theme);
|
|
50
|
+
}
|
|
51
|
+
getLastThemeUpdate() {
|
|
52
|
+
return this.lastThemeUpdate;
|
|
53
|
+
}
|
|
54
|
+
setLastThemeUpdate(timestamp) {
|
|
55
|
+
this.lastThemeUpdate = timestamp;
|
|
56
|
+
}
|
|
57
|
+
setFileNameToGeneratedCssNameMap(fileName, generatedCssName) {
|
|
58
|
+
this.fileNameToGeneratedCssNameMap.set(fileName, generatedCssName);
|
|
59
|
+
}
|
|
60
|
+
getGeneratedCssName(fileName) {
|
|
61
|
+
return this.fileNameToGeneratedCssNameMap.get(fileName);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const store = new Store();
|
|
40
65
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
41
|
-
const getUserTheme = async () => {
|
|
66
|
+
const getUserTheme = async (options) => {
|
|
67
|
+
if (store.getUserTheme() && !options?.ignoreCache) return store.getUserTheme();
|
|
42
68
|
try {
|
|
43
69
|
let userThemeFilePath = path$1.resolve(process.cwd(), "flair.theme.ts");
|
|
44
70
|
if (!existsSync$1(userThemeFilePath)) userThemeFilePath = path$1.resolve(process.cwd(), "flair.theme.js");
|
|
@@ -54,10 +80,17 @@ const getUserTheme = async () => {
|
|
|
54
80
|
});
|
|
55
81
|
const cacheBuster = Date.now();
|
|
56
82
|
const userTheme = await import(`${pathToFileURL(outFile).href}?update=${cacheBuster}`);
|
|
57
|
-
if (userTheme.default)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
if (userTheme.default) {
|
|
84
|
+
store.setUserTheme({
|
|
85
|
+
theme: userTheme.default,
|
|
86
|
+
originalPath: userThemeFilePath
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
theme: userTheme.default,
|
|
90
|
+
originalPath: userThemeFilePath
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
store.setUserTheme(null);
|
|
61
94
|
return null;
|
|
62
95
|
} catch (error) {
|
|
63
96
|
console.error("Error loading user theme:", error);
|
|
@@ -65,57 +98,60 @@ const getUserTheme = async () => {
|
|
|
65
98
|
}
|
|
66
99
|
};
|
|
67
100
|
const require = module.createRequire(import.meta.url);
|
|
68
|
-
|
|
69
|
-
* Initialize the shared plugin context
|
|
70
|
-
*/
|
|
71
|
-
async function initializeSharedContext(options = {}) {
|
|
72
|
-
const { buildThemeFile } = options;
|
|
73
|
-
const fileNameToGeneratedCssNameMap = /* @__PURE__ */ new Map();
|
|
101
|
+
const getGeneratedCssDir = () => {
|
|
74
102
|
const flairThemeFile = require.resolve("@flairjs/client/theme.css");
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
103
|
+
return path.resolve(flairThemeFile, "../generated-css");
|
|
104
|
+
};
|
|
105
|
+
const setupGeneratedCssDir = async (options) => {
|
|
106
|
+
const flairGeneratedCssDir = getGeneratedCssDir();
|
|
107
|
+
const { clearExisting = true } = options ?? {};
|
|
108
|
+
try {
|
|
109
|
+
if (!existsSync(flairGeneratedCssDir)) await mkdir(flairGeneratedCssDir);
|
|
110
|
+
else if (clearExisting) {
|
|
111
|
+
await rm(flairGeneratedCssDir, {
|
|
112
|
+
recursive: true,
|
|
113
|
+
force: true
|
|
114
|
+
});
|
|
115
|
+
await mkdir(flairGeneratedCssDir);
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if (err?.code === "EEXIST") return flairGeneratedCssDir;
|
|
119
|
+
else if (existsSync(flairGeneratedCssDir)) return flairGeneratedCssDir;
|
|
120
|
+
console.error(`[flairjs] Could not create generated CSS directory: ${flairGeneratedCssDir}`, err);
|
|
121
|
+
return null;
|
|
83
122
|
}
|
|
123
|
+
return flairGeneratedCssDir;
|
|
124
|
+
};
|
|
125
|
+
const setupUserThemeFile = async ({ buildThemeFile, onThemeFileChange, deleteBeforeWrite = false }) => {
|
|
126
|
+
const flairThemeFile = require.resolve("@flairjs/client/theme.css");
|
|
84
127
|
let userTheme = await getUserTheme();
|
|
85
128
|
const buildThemeCSS = buildThemeFile ?? buildThemeTokens;
|
|
86
129
|
if (userTheme) {
|
|
87
130
|
const themeCSS = buildThemeCSS(userTheme.theme);
|
|
131
|
+
store.setLastThemeUpdate(Date.now());
|
|
132
|
+
if (deleteBeforeWrite) await rm(flairThemeFile, { force: true });
|
|
88
133
|
await writeFile(flairThemeFile, themeCSS, "utf-8");
|
|
89
134
|
watch(userTheme.originalPath, async () => {
|
|
90
135
|
userTheme = await getUserTheme();
|
|
136
|
+
store.setLastThemeUpdate(Date.now());
|
|
91
137
|
if (!userTheme) return;
|
|
92
138
|
const themeCSS$1 = buildThemeCSS(userTheme.theme);
|
|
139
|
+
onThemeFileChange?.();
|
|
93
140
|
await writeFile(flairThemeFile, themeCSS$1, "utf-8");
|
|
94
141
|
});
|
|
95
142
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const previousGeneratedCssName = fileNameToGeneratedCssNameMap.get(filePath);
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
rm(path.join(flairGeneratedCssDir, previousGeneratedCssName));
|
|
101
|
-
}, 2e3);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
return {
|
|
105
|
-
flairThemeFile,
|
|
106
|
-
flairGeneratedCssDir,
|
|
107
|
-
userTheme,
|
|
108
|
-
buildThemeCSS,
|
|
109
|
-
refreshCssFile
|
|
110
|
-
};
|
|
111
|
-
}
|
|
143
|
+
return userTheme;
|
|
144
|
+
};
|
|
112
145
|
function shouldProcessFile(id, include, exclude) {
|
|
113
|
-
const isIncluded = picomatch(include
|
|
114
|
-
const isExcluded = picomatch(exclude
|
|
115
|
-
if (!isIncluded(id)) return false;
|
|
116
|
-
if (isExcluded(id)) return false;
|
|
146
|
+
const isIncluded = picomatch(include ?? ["**/*.{js,ts,jsx,tsx}"]);
|
|
147
|
+
const isExcluded = picomatch(exclude ?? ["**/node_modules/**"]);
|
|
148
|
+
if (!isIncluded(normalizeFilePath(id))) return false;
|
|
149
|
+
if (isExcluded(normalizeFilePath(id))) return false;
|
|
117
150
|
return true;
|
|
118
151
|
}
|
|
152
|
+
function normalizeFilePath(filePath) {
|
|
153
|
+
return filePath.replace(/\\/g, "/");
|
|
154
|
+
}
|
|
119
155
|
const colors = {
|
|
120
156
|
reset: "\x1B[0m",
|
|
121
157
|
fg: {
|
|
@@ -157,28 +193,50 @@ const transformCode$1 = (code, filePath, options) => {
|
|
|
157
193
|
|
|
158
194
|
//#endregion
|
|
159
195
|
//#region src/index.ts
|
|
196
|
+
let initialized = false;
|
|
160
197
|
async function flairJsLoader(source, sourceMap) {
|
|
161
198
|
const callback = this.async();
|
|
199
|
+
const options = this.getOptions() || {};
|
|
162
200
|
if (!callback) {
|
|
163
201
|
console.error("@flairjs/webpack-loader requires async support");
|
|
164
202
|
return;
|
|
165
203
|
}
|
|
166
|
-
const options = this.getOptions() || {};
|
|
167
|
-
const context = await initializeSharedContext(options);
|
|
168
204
|
const fileName = this.resourcePath;
|
|
169
205
|
if (!shouldProcessFile(fileName, options?.include, options?.exclude)) return callback(null, source, sourceMap);
|
|
206
|
+
let cssGeneratedDir = null;
|
|
207
|
+
let userTheme = null;
|
|
208
|
+
if (!initialized) {
|
|
209
|
+
cssGeneratedDir = await setupGeneratedCssDir();
|
|
210
|
+
userTheme = await setupUserThemeFile({
|
|
211
|
+
buildThemeFile: options.buildThemeFile,
|
|
212
|
+
deleteBeforeWrite: true
|
|
213
|
+
});
|
|
214
|
+
initialized = true;
|
|
215
|
+
} else {
|
|
216
|
+
cssGeneratedDir = getGeneratedCssDir();
|
|
217
|
+
userTheme = await getUserTheme();
|
|
218
|
+
}
|
|
219
|
+
if (!cssGeneratedDir) {
|
|
220
|
+
console.error("[flairjs] Could not find generated CSS directory. Skipping processing.");
|
|
221
|
+
return callback(null, source, sourceMap);
|
|
222
|
+
}
|
|
170
223
|
try {
|
|
171
224
|
const result = transformCode$1(source, fileName, {
|
|
172
|
-
appendTimestampToCssFile:
|
|
225
|
+
appendTimestampToCssFile: false,
|
|
173
226
|
classNameList: options?.classNameList,
|
|
174
227
|
cssPreprocessor: options?.cssPreprocessor ? (css) => options.cssPreprocessor(css, fileName) : void 0,
|
|
175
|
-
theme:
|
|
176
|
-
useTheme: !!
|
|
177
|
-
cssOutDir:
|
|
228
|
+
theme: userTheme?.theme,
|
|
229
|
+
useTheme: !!userTheme,
|
|
230
|
+
cssOutDir: cssGeneratedDir
|
|
178
231
|
});
|
|
179
232
|
if (!result) return callback(null, source, sourceMap);
|
|
180
|
-
if (result.generatedCssName)
|
|
181
|
-
|
|
233
|
+
if (!result.generatedCssName) return callback(null, source, sourceMap);
|
|
234
|
+
this.addDependency(path$2.resolve(result.generatedCssName));
|
|
235
|
+
let resultSourcemap = null;
|
|
236
|
+
if (result.sourcemap) try {
|
|
237
|
+
resultSourcemap = JSON.parse(result.sourcemap);
|
|
238
|
+
} catch {}
|
|
239
|
+
callback(null, result.code, resultSourcemap ?? sourceMap);
|
|
182
240
|
} catch (error) {
|
|
183
241
|
console.error("[@flairjs/webpack-loader]", error);
|
|
184
242
|
callback(error, source, sourceMap);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LoaderContext } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export default
|
|
6
|
-
|
|
1
|
+
import { FlairThemeConfig } from '@flairjs/client';
|
|
2
|
+
import { LoaderContext } from 'webpack';
|
|
3
|
+
|
|
4
|
+
declare function flairJsLoader(this: LoaderContext<FlairJsWebpackLoaderOptions>, source: string, sourceMap: string): Promise<void>;
|
|
5
|
+
export default flairJsLoader;
|
|
6
|
+
|
|
7
|
+
declare interface FlairJsWebpackLoaderOptions extends SharedPluginOptions {
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare interface SharedPluginOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Preprocess the extracted CSS before it is passed to lightningcss
|
|
13
|
+
* @experimental
|
|
14
|
+
* @param css the extracted css
|
|
15
|
+
* @param id the id of the file being processed
|
|
16
|
+
* @returns the processed css
|
|
17
|
+
*/
|
|
18
|
+
cssPreprocessor?: (css: string, id: string) => string;
|
|
19
|
+
include?: string | string[];
|
|
20
|
+
exclude?: string | string[];
|
|
21
|
+
/**
|
|
22
|
+
* Override the default theme file content based on the user theme
|
|
23
|
+
* @param theme the user theme
|
|
24
|
+
* @returns the theme file content
|
|
25
|
+
*/
|
|
26
|
+
buildThemeFile?: (theme: FlairThemeConfig) => string;
|
|
27
|
+
/**
|
|
28
|
+
* List of class names used in the project. Supports regex.
|
|
29
|
+
*/
|
|
30
|
+
classNameList?: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flairjs/webpack-loader",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.10",
|
|
4
4
|
"main": "./dist/cjs/index.js",
|
|
5
5
|
"module": "./dist/esm/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -13,20 +13,25 @@
|
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"webpack": ">=5.0.0",
|
|
16
|
-
"@flairjs/core": "0.0.1-beta.
|
|
16
|
+
"@flairjs/core": "0.0.1-beta.8"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@biomejs/biome": "^
|
|
20
|
-
"@
|
|
19
|
+
"@biomejs/biome": "^2.2.4",
|
|
20
|
+
"@microsoft/api-extractor": "^7.52.13",
|
|
21
21
|
"@types/node": "^22.8.1",
|
|
22
|
+
"rimraf": "^6.0.1",
|
|
22
23
|
"rolldown": "1.0.0-beta.37",
|
|
23
24
|
"typescript": "^5.8.2",
|
|
24
25
|
"webpack": "^5.101.0",
|
|
25
|
-
"@flairjs/bundler-shared": "0.0.1-beta.
|
|
26
|
-
"@flairjs/core": "0.0.1-beta.
|
|
26
|
+
"@flairjs/bundler-shared": "0.0.1-beta.14",
|
|
27
|
+
"@flairjs/core": "0.0.1-beta.8"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"esbuild": "^0.25.10",
|
|
31
|
+
"picomatch": "^4.0.3"
|
|
27
32
|
},
|
|
28
33
|
"scripts": {
|
|
29
|
-
"build": "rolldown -c",
|
|
34
|
+
"build": "rimraf dist && tsc && api-extractor run --local && rolldown -c && rimraf dist/types-temp",
|
|
30
35
|
"check": "biome check --write",
|
|
31
36
|
"dev": "rolldown -w -c",
|
|
32
37
|
"format": "biome format --write"
|