@lark-apaas/fullstack-rspack-preset 1.0.32-alpha.2 → 1.0.32-alpha.21

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.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Static Assets Plugin for Rspack
3
+ *
4
+ * Supports importing static files from shared/static directory:
5
+ * - JSON files → bundled into HTML, injected as window.__STATIC_JSON__
6
+ * - Other files → converted to URL {clientBasePath}/static/path
7
+ *
8
+ * Usage:
9
+ * import config from '@shared/static/config.json';
10
+ * import logoUrl from '@shared/static/images/logo.png';
11
+ */
12
+ import type { IncomingMessage, ServerResponse } from 'http';
13
+ import type { Compiler } from '@rspack/core';
14
+ export interface StaticAssetsPluginOptions {
15
+ /**
16
+ * Client base path prefix for routes
17
+ * @default ''
18
+ */
19
+ clientBasePath?: string;
20
+ /**
21
+ * Root directory for the project
22
+ * @default process.cwd()
23
+ */
24
+ rootDir?: string;
25
+ }
26
+ export declare class StaticAssetsPlugin {
27
+ private options;
28
+ private staticDir;
29
+ private jsonDataCache;
30
+ private virtualModules;
31
+ constructor(options?: StaticAssetsPluginOptions);
32
+ /**
33
+ * Get the key for a static file path (used for __STATIC_JSON__)
34
+ */
35
+ private getJsonKey;
36
+ /**
37
+ * Scan and read all JSON files from shared/static directory
38
+ */
39
+ private scanJsonFiles;
40
+ /**
41
+ * Get JSON data (with caching)
42
+ */
43
+ private getJsonData;
44
+ /**
45
+ * Generate virtual module content for a static file import
46
+ */
47
+ private generateVirtualModuleContent;
48
+ /**
49
+ * Get the script content for __STATIC_JSON__ injection
50
+ */
51
+ private getStaticJsonScriptContent;
52
+ /**
53
+ * Create a dev server middleware that serves static files from shared/static.
54
+ * This aligns with the Vite preset's configureServer middleware.
55
+ */
56
+ createDevMiddleware(): (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
57
+ apply(compiler: Compiler): void;
58
+ }
59
+ export default StaticAssetsPlugin;
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+ /**
3
+ * Static Assets Plugin for Rspack
4
+ *
5
+ * Supports importing static files from shared/static directory:
6
+ * - JSON files → bundled into HTML, injected as window.__STATIC_JSON__
7
+ * - Other files → converted to URL {clientBasePath}/static/path
8
+ *
9
+ * Usage:
10
+ * import config from '@shared/static/config.json';
11
+ * import logoUrl from '@shared/static/images/logo.png';
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.StaticAssetsPlugin = void 0;
48
+ /* eslint-disable @typescript-eslint/no-explicit-any */
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const glob_1 = require("glob");
52
+ const MIME_TYPES = {
53
+ '.json': 'application/json',
54
+ '.png': 'image/png',
55
+ '.jpg': 'image/jpeg',
56
+ '.jpeg': 'image/jpeg',
57
+ '.gif': 'image/gif',
58
+ '.svg': 'image/svg+xml',
59
+ '.webp': 'image/webp',
60
+ '.ico': 'image/x-icon',
61
+ '.woff': 'font/woff',
62
+ '.woff2': 'font/woff2',
63
+ '.ttf': 'font/ttf',
64
+ '.eot': 'application/vnd.ms-fontobject',
65
+ '.otf': 'font/otf',
66
+ '.css': 'text/css',
67
+ '.js': 'application/javascript',
68
+ '.txt': 'text/plain',
69
+ '.html': 'text/html',
70
+ '.xml': 'application/xml',
71
+ '.pdf': 'application/pdf',
72
+ '.mp3': 'audio/mpeg',
73
+ '.mp4': 'video/mp4',
74
+ '.webm': 'video/webm',
75
+ };
76
+ const SHARED_STATIC_PREFIX = '@shared/static/';
77
+ const PLUGIN_NAME = 'StaticAssetsPlugin';
78
+ /** File extensions that should be resolved as normal modules instead of static asset URLs */
79
+ const SCRIPT_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
80
+ /**
81
+ * Split a relative path into the clean path and optional query string.
82
+ * e.g. "file.md?raw" → ["file.md", "?raw"]
83
+ */
84
+ function splitQuery(relativePath) {
85
+ const qIndex = relativePath.indexOf('?');
86
+ if (qIndex === -1)
87
+ return [relativePath, ''];
88
+ return [relativePath.slice(0, qIndex), relativePath.slice(qIndex)];
89
+ }
90
+ /**
91
+ * Try to resolve a relative path to a real script file on disk.
92
+ * Handles both explicit extensions (utils.ts) and extensionless imports (utils).
93
+ * Returns the absolute path if found, null otherwise.
94
+ */
95
+ function resolveScriptFile(staticDir, cleanPath) {
96
+ const ext = path.extname(cleanPath).toLowerCase();
97
+ // Has explicit script extension → resolve directly
98
+ if (ext && SCRIPT_EXTENSIONS.includes(ext)) {
99
+ return path.join(staticDir, cleanPath);
100
+ }
101
+ // No extension → probe for matching script file on disk
102
+ if (!ext) {
103
+ for (const scriptExt of SCRIPT_EXTENSIONS) {
104
+ const fullPath = path.join(staticDir, cleanPath + scriptExt);
105
+ if (fs.existsSync(fullPath)) {
106
+ return fullPath;
107
+ }
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ class StaticAssetsPlugin {
113
+ constructor(options = {}) {
114
+ this.jsonDataCache = null;
115
+ this.virtualModules = new Map();
116
+ this.options = {
117
+ clientBasePath: '',
118
+ rootDir: process.cwd(),
119
+ ...options,
120
+ };
121
+ this.staticDir = path.resolve(this.options.rootDir, 'shared/static');
122
+ }
123
+ /**
124
+ * Get the key for a static file path (used for __STATIC_JSON__)
125
+ */
126
+ getJsonKey(filePath) {
127
+ return filePath.replace(/\.json$/, '');
128
+ }
129
+ /**
130
+ * Scan and read all JSON files from shared/static directory
131
+ */
132
+ scanJsonFiles() {
133
+ const jsonData = {};
134
+ if (!fs.existsSync(this.staticDir)) {
135
+ return jsonData;
136
+ }
137
+ const jsonFiles = glob_1.glob.sync('**/*.json', {
138
+ cwd: this.staticDir,
139
+ nodir: true,
140
+ });
141
+ for (const file of jsonFiles) {
142
+ const fullPath = path.join(this.staticDir, file);
143
+ try {
144
+ const content = fs.readFileSync(fullPath, 'utf-8');
145
+ const key = this.getJsonKey(file);
146
+ jsonData[key] = JSON.parse(content);
147
+ }
148
+ catch (e) {
149
+ console.warn(`[static-assets] Failed to parse JSON file: ${file}`, e);
150
+ }
151
+ }
152
+ return jsonData;
153
+ }
154
+ /**
155
+ * Get JSON data (with caching)
156
+ */
157
+ getJsonData() {
158
+ if (this.jsonDataCache) {
159
+ return this.jsonDataCache;
160
+ }
161
+ this.jsonDataCache = this.scanJsonFiles();
162
+ return this.jsonDataCache;
163
+ }
164
+ /**
165
+ * Generate virtual module content for a static file import
166
+ */
167
+ generateVirtualModuleContent(relativePath) {
168
+ const isJson = relativePath.endsWith('.json');
169
+ if (isJson) {
170
+ const key = this.getJsonKey(relativePath);
171
+ return `export default window.__STATIC_JSON__['${key}'];`;
172
+ }
173
+ else {
174
+ const envBaseUrl = typeof process !== 'undefined' ? process.env?.STATIC_ASSETS_BASE_URL : undefined;
175
+ // envBaseUrl (downloadURLPrefix) 已指向 static 目录,无需再拼 /static/
176
+ const staticUrl = envBaseUrl
177
+ ? `${envBaseUrl.replace(/\/+$/, '')}/${relativePath}`
178
+ : `${this.options.clientBasePath}/static/${relativePath}`;
179
+ return `export default ${JSON.stringify(staticUrl)};`;
180
+ }
181
+ }
182
+ /**
183
+ * Get the script content for __STATIC_JSON__ injection
184
+ */
185
+ getStaticJsonScriptContent() {
186
+ const jsonData = this.getJsonData();
187
+ if (Object.keys(jsonData).length === 0) {
188
+ return '';
189
+ }
190
+ return `window.__STATIC_JSON__ = ${JSON.stringify(jsonData)};`;
191
+ }
192
+ /**
193
+ * Create a dev server middleware that serves static files from shared/static.
194
+ * This aligns with the Vite preset's configureServer middleware.
195
+ */
196
+ createDevMiddleware() {
197
+ const staticDir = this.staticDir;
198
+ const clientBasePath = this.options.clientBasePath || '';
199
+ return (req, res, next) => {
200
+ const staticPrefix = `${clientBasePath}/static/`;
201
+ const url = req.url || '';
202
+ if (!url.startsWith(staticPrefix)) {
203
+ return next();
204
+ }
205
+ const rawRelativePath = url.slice(staticPrefix.length).split('?')[0];
206
+ let relativePath;
207
+ try {
208
+ relativePath = decodeURIComponent(rawRelativePath);
209
+ }
210
+ catch {
211
+ res.statusCode = 400;
212
+ res.end('Bad Request');
213
+ return;
214
+ }
215
+ const filePath = path.join(staticDir, relativePath);
216
+ // Security: prevent path traversal
217
+ const normalizedPath = path.normalize(filePath);
218
+ if (!normalizedPath.startsWith(staticDir)) {
219
+ res.statusCode = 403;
220
+ res.end('Forbidden');
221
+ return;
222
+ }
223
+ if (!fs.existsSync(filePath)) {
224
+ res.statusCode = 404;
225
+ res.end('Not Found');
226
+ return;
227
+ }
228
+ const stat = fs.statSync(filePath);
229
+ if (stat.isDirectory()) {
230
+ res.statusCode = 403;
231
+ res.end('Forbidden');
232
+ return;
233
+ }
234
+ const ext = path.extname(filePath).toLowerCase();
235
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
236
+ res.setHeader('Content-Type', contentType);
237
+ res.setHeader('Cache-Control', 'no-cache');
238
+ const stream = fs.createReadStream(filePath);
239
+ stream.pipe(res);
240
+ };
241
+ }
242
+ apply(compiler) {
243
+ // Create a temporary directory for virtual modules
244
+ const virtualModulesDir = path.join(this.options.rootDir, 'node_modules', '.cache', 'static-assets-virtual');
245
+ // Ensure virtual modules directory exists
246
+ if (!fs.existsSync(virtualModulesDir)) {
247
+ fs.mkdirSync(virtualModulesDir, { recursive: true });
248
+ }
249
+ // Cache for written virtual module contents to avoid repeated disk writes
250
+ const writtenModules = new Map();
251
+ // Hook into module resolution to intercept @shared/static/* imports
252
+ compiler.hooks.normalModuleFactory.tap(PLUGIN_NAME, (factory) => {
253
+ factory.hooks.beforeResolve.tap(PLUGIN_NAME, (resolveData) => {
254
+ const request = resolveData.request;
255
+ if (!request.startsWith(SHARED_STATIC_PREFIX)) {
256
+ return;
257
+ }
258
+ const relativePath = request.slice(SHARED_STATIC_PREFIX.length);
259
+ const [cleanPath, query] = splitQuery(relativePath);
260
+ // Imports with query params (e.g. ?raw) should resolve to real
261
+ // paths and let Rspack's native handling process them
262
+ if (query) {
263
+ resolveData.request = path.join(this.staticDir, cleanPath) + query;
264
+ return;
265
+ }
266
+ // JS/TS files should be resolved to their real paths and bundled normally
267
+ const scriptPath = resolveScriptFile(this.staticDir, cleanPath);
268
+ if (scriptPath) {
269
+ resolveData.request = scriptPath;
270
+ return;
271
+ }
272
+ // Generate virtual module content
273
+ const content = this.generateVirtualModuleContent(cleanPath);
274
+ // Create a virtual module file
275
+ const virtualModulePath = path.join(virtualModulesDir, cleanPath.replace(/[/\\]/g, '_') + '.js');
276
+ // Only write to disk if content has changed
277
+ const existingContent = writtenModules.get(virtualModulePath);
278
+ if (existingContent !== content) {
279
+ const dir = path.dirname(virtualModulePath);
280
+ if (!fs.existsSync(dir)) {
281
+ fs.mkdirSync(dir, { recursive: true });
282
+ }
283
+ fs.writeFileSync(virtualModulePath, content);
284
+ writtenModules.set(virtualModulePath, content);
285
+ }
286
+ // Redirect the request to the virtual module
287
+ resolveData.request = virtualModulePath;
288
+ });
289
+ });
290
+ // Inject __STATIC_JSON__ script into HTML
291
+ compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
292
+ try {
293
+ // Get HtmlRspackPlugin from compiler
294
+ let HtmlPlugin;
295
+ try {
296
+ const rspack = compiler.webpack ||
297
+ compiler.rspack ||
298
+ compiler.constructor.webpack;
299
+ if (rspack && rspack.HtmlRspackPlugin) {
300
+ HtmlPlugin = rspack.HtmlRspackPlugin;
301
+ }
302
+ else {
303
+ try {
304
+ HtmlPlugin = require('html-webpack-plugin');
305
+ }
306
+ catch {
307
+ console.warn(`${PLUGIN_NAME}: HtmlRspackPlugin not found`);
308
+ return;
309
+ }
310
+ }
311
+ }
312
+ catch {
313
+ console.warn(`${PLUGIN_NAME}: Failed to get HtmlRspackPlugin`);
314
+ return;
315
+ }
316
+ const hooks = HtmlPlugin.getHooks(compilation);
317
+ // Use alterAssetTagGroups hook to inject the script tag
318
+ hooks.alterAssetTagGroups.tap(PLUGIN_NAME, (data) => {
319
+ const scriptContent = this.getStaticJsonScriptContent();
320
+ if (!scriptContent) {
321
+ return data;
322
+ }
323
+ // Create the script tag
324
+ const staticJsonTag = {
325
+ tagName: 'script',
326
+ voidTag: false,
327
+ innerHTML: scriptContent,
328
+ attributes: {},
329
+ };
330
+ // Insert at the beginning of head tags (before other scripts)
331
+ data.headTags.unshift(staticJsonTag);
332
+ return data;
333
+ });
334
+ }
335
+ catch (error) {
336
+ console.error(`Error in ${PLUGIN_NAME}:`, error);
337
+ }
338
+ });
339
+ // Watch shared/static directory for changes (dev mode)
340
+ compiler.hooks.watchRun.tapAsync(PLUGIN_NAME, (_compiler, callback) => {
341
+ // Invalidate caches on watch run to pick up file changes
342
+ this.jsonDataCache = null;
343
+ writtenModules.clear();
344
+ callback();
345
+ });
346
+ // Add shared/static directory to watch dependencies
347
+ compiler.hooks.afterCompile.tap(PLUGIN_NAME, (compilation) => {
348
+ if (fs.existsSync(this.staticDir)) {
349
+ // Add the static directory to watch list
350
+ compilation.contextDependencies.add(this.staticDir);
351
+ // Also add individual JSON files
352
+ const jsonFiles = glob_1.glob.sync('**/*.json', {
353
+ cwd: this.staticDir,
354
+ nodir: true,
355
+ });
356
+ for (const file of jsonFiles) {
357
+ const fullPath = path.join(this.staticDir, file);
358
+ compilation.fileDependencies.add(fullPath);
359
+ }
360
+ }
361
+ });
362
+ // Copy shared/static to dist/shared/static for production serving
363
+ compiler.hooks.afterEmit.tap(PLUGIN_NAME, () => {
364
+ if (compiler.options.mode === 'development') {
365
+ return;
366
+ }
367
+ if (!fs.existsSync(this.staticDir)) {
368
+ return;
369
+ }
370
+ const outPath = compiler.options.output?.path;
371
+ if (!outPath) {
372
+ return;
373
+ }
374
+ // outPath is typically dist/client, target is dist/shared/static
375
+ const distRoot = path.resolve(outPath, '..');
376
+ const destDir = path.join(distRoot, 'shared', 'static');
377
+ fs.cpSync(this.staticDir, destDir, { recursive: true });
378
+ console.log(`[static-assets] Copied shared/static → ${path.relative(this.options.rootDir, destDir)}`);
379
+ });
380
+ }
381
+ }
382
+ exports.StaticAssetsPlugin = StaticAssetsPlugin;
383
+ exports.default = StaticAssetsPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-rspack-preset",
3
- "version": "1.0.32-alpha.2",
3
+ "version": "1.0.32-alpha.21",
4
4
  "files": [
5
5
  "lib",
6
6
  "patches",
@@ -31,17 +31,20 @@
31
31
  "@babel/parser": "^7.28.0",
32
32
  "@babel/traverse": "^7.28.0",
33
33
  "@babel/types": "^7.28.2",
34
- "@lark-apaas/devtool-kits": "^1.2.15",
35
- "@lark-apaas/miaoda-inspector-babel-plugin": "^1.0.0",
36
- "@lark-apaas/miaoda-inspector-jsx-runtime": "^1.0.0",
34
+ "@lark-apaas/devtool-kits": "1.2.17-alpha.34",
35
+ "@lark-apaas/miaoda-inspector-babel-plugin": "^1.0.2",
37
36
  "@lark-apaas/styled-jsx": "^1.0.1",
38
37
  "@rspack/plugin-react-refresh": "^1.5.1",
39
38
  "@swc/plugin-styled-jsx": "^11.0.0",
40
39
  "babel-loader": "^10.0.0",
41
40
  "clsx": "^2.1.1",
42
41
  "colorjs.io": "^0.5.2",
42
+ "core-js": "^3.40.0",
43
43
  "dotenv": "^16.4.5",
44
44
  "echarts": "^6.0.0",
45
+ "echarts-for-react": "^3.0.2",
46
+ "glob": "^11.0.0",
47
+ "lightningcss": "^1.28.0",
45
48
  "patch-package": "^8.0.0",
46
49
  "postcss-import": "^16.1.1",
47
50
  "react-refresh": "^0.17.0",
@@ -52,11 +55,11 @@
52
55
  "@rspack/core": "^1.5.5",
53
56
  "@types/node": "^22.15.30",
54
57
  "typescript": "^5.9.2",
55
- "vitest": "^2.1.8"
58
+ "vitest": "^3.2.4"
56
59
  },
57
60
  "peerDependencies": {
58
61
  "@rspack/core": "^1.5.5",
59
62
  "react": ">=16.14.0",
60
63
  "react-dom": ">=16.14.0"
61
64
  }
62
- }
65
+ }