@wbc-ui/core2 1.0.0-r01

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,32 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 140" width="400" height="140">
2
+ <defs>
3
+ <linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#1976D2"/>
5
+ <stop offset="100%" style="stop-color:#42A5F5"/>
6
+ </linearGradient>
7
+ <linearGradient id="g2" x1="0%" y1="0%" x2="100%" y2="0%">
8
+ <stop offset="0%" style="stop-color:#0D47A1"/>
9
+ <stop offset="100%" style="stop-color:#1976D2"/>
10
+ </linearGradient>
11
+ </defs>
12
+
13
+ <!-- WBC hex icon -->
14
+ <polygon points="70,20 120,45 120,95 70,120 20,95 20,45" fill="url(#g)" stroke="#0D47A1" stroke-width="2"/>
15
+ <text x="70" y="82" text-anchor="middle" font-family="monospace" font-weight="bold" font-size="32" fill="white">WBC</text>
16
+
17
+ <!-- subtag -->
18
+ <rect x="50" y="88" width="40" height="14" rx="3" fill="#0D47A1" opacity="0.85"/>
19
+ <text x="70" y="99" text-anchor="middle" font-family="monospace" font-size="8" fill="white">CORE</text>
20
+
21
+ <!-- Type label -->
22
+ <text x="150" y="50" font-family="Arial, sans-serif" font-weight="900" font-size="36" fill="url(#g2)">@wbc-ui</text>
23
+ <text x="150" y="72" font-family="Arial, sans-serif" font-weight="700" font-size="24" fill="#1565C0">/ core2</text>
24
+
25
+ <!-- Tagline -->
26
+ <text x="150" y="100" font-family="Arial, sans-serif" font-size="13" fill="#546E7A">UI as Data · Vue 2 Engine · JSON → VNodes</text>
27
+
28
+ <!-- Decorative dots -->
29
+ <circle cx="155" cy="112" r="2.5" fill="#1976D2" opacity="0.5"/>
30
+ <circle cx="165" cy="112" r="2.5" fill="#42A5F5" opacity="0.5"/>
31
+ <circle cx="175" cy="112" r="2.5" fill="#90CAF9" opacity="0.5"/>
32
+ </svg>
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "@wbc-ui/core2",
3
+ "version": "1.0.0-r01",
4
+ "description": "Advanced Dynamic UI Engine for Vue 2 — Part of the @wbc-ui ecosystem",
5
+ "author": "Wissem Boughamoura <wissemb11@gmail.com> (https://www.wi-bg.com)",
6
+ "homepage": "https://wbc-ui.com",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/wbc-ui/core2.git",
11
+ "directory": "frontEnd/wbc-ui/core2/packages/wb-core"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/wbc-ui/core2/issues"
15
+ },
16
+ "main": "dist-free/core2.umd.js",
17
+ "module": "dist-free/core2.es.js",
18
+ "exports": {
19
+ ".": {
20
+ "import": "./dist-free/core2.es.js",
21
+ "require": "./dist-free/core2.umd.js"
22
+ },
23
+ "./vite-plugin": "./vite-plugin.mjs",
24
+ "./vite-plugin.js": "./vite-plugin.mjs",
25
+ "./vite-plugin.mjs": "./vite-plugin.mjs",
26
+ "./wbc.webpack.js": "./wbc.webpack.js",
27
+ "./dist-free/*": "./dist-free/*"
28
+ },
29
+ "files": [
30
+ "dist-free",
31
+ "README.md",
32
+ "LICENSE",
33
+ "logo/",
34
+ "vite-plugin.mjs",
35
+ "wbc.webpack.js"
36
+ ],
37
+ "dependencies": {
38
+ "acorn": "^8.16.0",
39
+ "acorn-walk": "^8.3.5",
40
+ "bootstrap": "^4.6.2",
41
+ "dompurify": "^3.3.3",
42
+ "jsep": "^1.4.0",
43
+ "lz-string": "^1.5.0",
44
+ "markdown-it": "^14.1.0",
45
+ "markdown-it-anchor": "^9.2.0",
46
+ "markdown-it-code-copy": "^0.2.2",
47
+ "turndown": "^7.2.0"
48
+ },
49
+ "peerDependencies": {
50
+ "vue": "^2.7.16",
51
+ "vue-router": "^3.0.0",
52
+ "vuetify": "^2.7.2",
53
+ "vuex": "^3.6.2",
54
+ "bootstrap-vue": "^2.22.0",
55
+ "vue-json-viewer": "^2.2.22",
56
+ "vue-cookies": "^1.8.3",
57
+ "vue-cryptojs": "^2.3.1",
58
+ "vue-dayjs": "^1.0.2"
59
+ },
60
+ "peerDependenciesMeta": {
61
+ "echarts": {
62
+ "optional": true
63
+ },
64
+ "mermaid": {
65
+ "optional": true
66
+ },
67
+ "codemirror": {
68
+ "optional": true
69
+ },
70
+ "vue-codemirror": {
71
+ "optional": true
72
+ },
73
+ "vue-cryptojs": {
74
+ "optional": true
75
+ },
76
+ "vue-dayjs": {
77
+ "optional": true
78
+ }
79
+ },
80
+ "publishConfig": {
81
+ "access": "public",
82
+ "tag": "next"
83
+ },
84
+ "engines": {
85
+ "node": ">=18"
86
+ },
87
+ "keywords": [
88
+ "vue",
89
+ "vue2",
90
+ "ui-engine",
91
+ "dynamic-ui",
92
+ "wbc-ui",
93
+ "dynamic-components",
94
+ "no-code",
95
+ "low-code",
96
+ "json-driven",
97
+ "vuetify"
98
+ ],
99
+ "sideEffects": false
100
+ }
@@ -0,0 +1,354 @@
1
+ /**
2
+ * wbc-ui2 — Vite Plugins
3
+ *
4
+ * Provides Vite plugins that replicate the Webpack features
5
+ * required by wbc-ui2 (WBC) to resolve local files.
6
+ *
7
+ * Usage in your vite.config.js:
8
+ *
9
+ * import { wbcVitePlugin } from '@wbc-ui/core2/vite-plugin';
10
+ *
11
+ * export default defineConfig({
12
+ * plugins: [
13
+ * vue(),
14
+ * ...wbcVitePlugin(), // ← spread! returns an array of plugins
15
+ * ],
16
+ * });
17
+ */
18
+ // ─── Extension Lists ───
19
+
20
+ // Standard modules (handled by Vite's normal pipeline)
21
+ // NOTE: JS/TS are NOT here — they go through raw + lazy to avoid side-effect execution
22
+ const STANDARD_EXTENSIONS =
23
+ '{jpeg,jpg,png,gif,svg,webp,avif,bmp,ico,' +
24
+ 'mp4,webm,mov,avi,mp3,wav,flac,ogg,' +
25
+ 'json,pdf,' +
26
+ 'vue}';
27
+
28
+ // Raw files (handled as strings via ?raw)
29
+ // JS/TS are included here — loaded as raw text (no execution, no side effects)
30
+ const RAW_EXTENSIONS = '{md,txt,py,rs,php,sh,tex,html,mmd,css,scss,sass,less,styl,stylus,js,jsx,ts,tsx}';
31
+
32
+ // Directories to exclude from context scanning
33
+ const EXCLUDED_DIRS = [
34
+ '!**/node_modules/**',
35
+ '!**/dist/**',
36
+ '!**/dist-vite/**',
37
+ '!**/dist-rollup/**',
38
+ '!**/public/**',
39
+ '!**/.vite/**',
40
+ '!**/.git/**',
41
+ ];
42
+
43
+ // ─── Extensions loaded as raw strings (not parsed as modules) ───
44
+ // Used by rawFileLoaderPlugin for direct imports
45
+ const RAW_EXTENSIONS_RE = /\.(md|txt|py|rs|sh|tex|php|html|mmd)$/;
46
+ const DOUBLE_UNDERSCORE_RE = /__[^/]+\.(vue|js|ts|md|php|sh|html|tex|txt|py|rs|css|scss|sass)$/;
47
+
48
+ /**
49
+ * Plugin 1: require.context() → import.meta.glob()
50
+ */
51
+ function requireContextPlugin() {
52
+ return {
53
+ name: 'wbc-ui2:require-context',
54
+ enforce: 'pre',
55
+ transform(code, id) {
56
+ // Only process JS/TS source files
57
+ if (!id.endsWith('.js') && !id.endsWith('.ts')) return null;
58
+ if (id.includes('node_modules')) return null;
59
+ if (!code.includes('require.context')) return null;
60
+
61
+ const transformed = code.replace(
62
+ /require\.context\(\s*["']([^"']+)["']\s*,\s*true\s*\)/g,
63
+ (_match, dir) => {
64
+ const glob = dir.endsWith('/') ? dir : dir + '/';
65
+
66
+ const mainPattern = `${glob}**/*.${STANDARD_EXTENSIONS}`;
67
+ const excludeUnderscore = `${glob}**/__*.*`;
68
+
69
+ const rawPattern1 = `${glob}**/*.${RAW_EXTENSIONS}`;
70
+ const rawPattern2 = `${glob}**/__*.*`;
71
+
72
+ const jsLazyPattern = `${glob}**/*.{js,jsx,ts,tsx}`;
73
+
74
+ const mainGlobArray = JSON.stringify([
75
+ mainPattern,
76
+ '!' + excludeUnderscore,
77
+ ...EXCLUDED_DIRS
78
+ ]);
79
+
80
+ const rawGlobArray = JSON.stringify([
81
+ rawPattern1,
82
+ rawPattern2,
83
+ ...EXCLUDED_DIRS
84
+ ]);
85
+
86
+ const jsLazyGlobArray = JSON.stringify([
87
+ jsLazyPattern,
88
+ ...EXCLUDED_DIRS
89
+ ]);
90
+
91
+ return `(function(){` +
92
+ // 1. Standard modules (images, vue, json — NOT js)
93
+ `var _std=import.meta.glob(${mainGlobArray},{eager:true});` +
94
+ // 2. Raw text (md, html, css, js — everything as text)
95
+ `var _r=import.meta.glob(${rawGlobArray},{eager:true,query:"?raw",import:"default"});` +
96
+ // 3. Lazy JS modules (for on-demand Vue component loading)
97
+ `var _lazy=import.meta.glob(${jsLazyGlobArray},{eager:false});` +
98
+ // Build merged map + separate lazy map
99
+ `var _prefix=${JSON.stringify(glob)};` +
100
+ `var _m={};` +
101
+ `var _lazyMap={};` +
102
+ // First: add all standard entries
103
+ `Object.keys(_std).forEach(function(k){` +
104
+ `var nk=k.indexOf(_prefix)===0?"./"+k.slice(_prefix.length):k;` +
105
+ `_m[nk]=_std[k]` +
106
+ `});` +
107
+ // Then: add raw entries (wrap as {default: text}), don't overwrite standard
108
+ `Object.keys(_r).forEach(function(k){` +
109
+ `var nk=k.indexOf(_prefix)===0?"./"+k.slice(_prefix.length):k;` +
110
+ `if(!_m[nk]){_m[nk]={default:_r[nk]||_r[k]}}` +
111
+ `});` +
112
+ // Store lazy importers in separate map (avoids frozen object issue)
113
+ `Object.keys(_lazy).forEach(function(k){` +
114
+ `var nk=k.indexOf(_prefix)===0?"./"+k.slice(_prefix.length):k;` +
115
+ `_lazyMap[nk]=_lazy[k]` +
116
+ `});` +
117
+ // Build context function
118
+ `function _c(k){if(!(k in _m)){throw new Error(\"Cannot find module '\"+k+\"'\")}return _m[k]}` +
119
+ `_c.keys=function(){return Object.keys(_m)};` +
120
+ // rawSource: returns raw text for code display
121
+ `_c.rawSource=function(k){` +
122
+ `var nk=k;` +
123
+ `var rk=k.indexOf("./")===0?_prefix+k.slice(2):k;` +
124
+ `return _r[rk]||_r[nk]||_r[k]||null` +
125
+ `};` +
126
+ // lazyImport: returns Promise resolving to JS module (for Vue components)
127
+ `_c.lazyImport=function(k){` +
128
+ `var norm=function(p){return p.replace(/\\/\\//g,'/').replace(/^\\.\\//,'')};` +
129
+ `var nk=norm(k);` +
130
+ `var fn=_lazyMap[k]||_lazyMap["./"+nk]||_lazyMap[nk]||_lazyMap[_prefix+nk]||_lazyMap["./"+_prefix+nk];` +
131
+ `if(!fn){` +
132
+ ` Object.keys(_lazyMap).forEach(function(lk){ if(norm(lk)===nk) fn=_lazyMap[lk] });` +
133
+ `}` +
134
+ `if(!fn && k.indexOf(_prefix)===0) fn=_lazyMap["./"+k.slice(_prefix.length)];` +
135
+ `if(!fn) console.warn("WBC lazyImport: could not find '"+k+"' in", Object.keys(_lazyMap));` +
136
+ `return fn?fn():null` +
137
+ `};` +
138
+ `return _c` +
139
+ `})()`;
140
+ }
141
+ );
142
+
143
+ if (transformed !== code) {
144
+ return { code: transformed, map: null };
145
+ }
146
+ }
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Plugin 2: Raw file loader
152
+ * Handles direct imports of raw files (not via require.context)
153
+ */
154
+ function rawFileLoaderPlugin() {
155
+ return {
156
+ name: 'wbc-ui2:raw-file-loader',
157
+ enforce: 'pre',
158
+ transform(code, id) {
159
+ // Skip files already loaded with ?raw query
160
+ if (id.includes('?raw')) return null;
161
+
162
+ // Exclude index.html (Vite entry point) from raw transformation
163
+ if (id.endsWith('index.html')) return null;
164
+
165
+ // Exclude public directory files so they are served verbatim by Vite's static file server
166
+ if (id.includes('/public/')) return null;
167
+
168
+ const isRawExt = RAW_EXTENSIONS_RE.test(id);
169
+ const isDoubleUnderscore = DOUBLE_UNDERSCORE_RE.test(id);
170
+ if (isRawExt || isDoubleUnderscore) {
171
+ const json = JSON.stringify(code);
172
+ return {
173
+ code: `export default ${json}`,
174
+ map: null
175
+ };
176
+ }
177
+ }
178
+ };
179
+ }
180
+
181
+ import fs from 'fs';
182
+ import path from 'path';
183
+ import { fileURLToPath } from 'url';
184
+
185
+ function monorepoDevConfigPlugin() {
186
+ return {
187
+ name: 'wbc-ui2:monorepo-dev-config',
188
+ config(config, env) {
189
+ let pluginDir;
190
+ try {
191
+ pluginDir = path.dirname(fileURLToPath(import.meta.url));
192
+ } catch (e) {
193
+ pluginDir = __dirname;
194
+ }
195
+
196
+ const coreSrcPath = path.resolve(pluginDir, './src');
197
+ if (!fs.existsSync(coreSrcPath)) return null;
198
+
199
+ // Dependencies are resolved from the common monorepo root or root node_modules.
200
+ // Plugin lives at packages/wb-core/core/, so monoRoot = packages/
201
+ const monoRoot = path.resolve(pluginDir, '../../');
202
+ const rootNodeModules = path.resolve(monoRoot, '../node_modules');
203
+
204
+ const DEPS = [
205
+ 'echarts', 'mermaid', 'leaflet',
206
+ 'highlight.js', '@guolao/vue-monaco-editor', 'html2pdf.js',
207
+ 'pdfjs-dist', 'vuetify', 'lz-string', 'markdown-it',
208
+ 'vue-doc-preview', '@vue-office/excel',
209
+ '@vue-office/docx',
210
+ 'prettier',
211
+ 'vue2-leaflet'
212
+ ];
213
+
214
+ const depAliases = {};
215
+ DEPS.forEach(d => {
216
+ try {
217
+ depAliases[d] = path.dirname(require.resolve(`${d}/package.json`));
218
+ } catch (e) {
219
+ depAliases[d] = path.resolve(rootNodeModules, d);
220
+ }
221
+ });
222
+
223
+ // ── Build the alias list ──
224
+ // Each package needs TWO aliases:
225
+ // 1. Exact match (RegExp): import X from '@wbc-ui2/table' → wb-table/src/index.js
226
+ // 2. Prefix match (string): import Y from '@wbc-ui2/table/src/WBTable.js' → wb-table/src/WBTable.js
227
+ const WBC_PACKAGES = {
228
+ '@wbc-ui/core2': 'wb-core',
229
+ '@wbc-ui/code2': 'wb-code',
230
+ '@wbc-ui/js2': 'wb-js',
231
+ '@wbc-ui/gis2': 'wb-gis',
232
+ '@wbc-ui/chart2': 'wb-chart',
233
+ '@wbc-ui/mermaid2': 'wb-mermaid',
234
+ '@wbc-ui/latex2': 'wb-latex',
235
+ '@wbc-ui/dataviewer2': 'wb-dataviewer',
236
+ '@wbc-ui/alert2': 'wb-alert',
237
+ '@wbc-ui/wiki-obj2': 'wbc-wiki-obj',
238
+ '@wbc-ui/office2': 'wb-office',
239
+ };
240
+
241
+ const wbcAliases = [];
242
+ for (const [pkg, dir] of Object.entries(WBC_PACKAGES)) {
243
+ const escaped = pkg.replace(/[/]/g, '\\/');
244
+ // 1. Exact match → core/src/index.js (for: import X from '@wbc-ui/code2')
245
+ wbcAliases.push({ find: new RegExp(`^${escaped}$`), replacement: path.resolve(monoRoot, `${dir}/core/src/index.js`) });
246
+ // 2. Prefix match → package's core dir (for: import Y from '@wbc-ui/code2/src/WBCode.js')
247
+ wbcAliases.push({ find: pkg, replacement: path.resolve(monoRoot, `${dir}/core`) });
248
+ }
249
+
250
+ // Add external dependency aliases + specific subpath overrides
251
+ wbcAliases.push(
252
+ ...Object.keys(depAliases).map(k => ({ find: k, replacement: depAliases[k] })),
253
+ { find: 'vuetify/lib', replacement: path.resolve(rootNodeModules, 'vuetify/lib') },
254
+ { find: 'lodash/debounce', replacement: path.resolve(rootNodeModules, 'lodash/debounce') },
255
+ );
256
+
257
+
258
+ // ── MUTATE config in-place to avoid merge conflicts ──
259
+ // Vite's mergeConfig can overwrite resolve.alias if the app also defines it.
260
+ // By mutating directly, we guarantee the aliases are applied.
261
+ config.resolve = config.resolve || {};
262
+
263
+ // Convert existing alias object to array format if needed
264
+ const existingAliases = config.resolve.alias || {};
265
+ let aliasArray = [];
266
+ if (Array.isArray(existingAliases)) {
267
+ aliasArray = existingAliases;
268
+ } else if (typeof existingAliases === 'object') {
269
+ aliasArray = Object.keys(existingAliases).map(k => ({
270
+ find: k,
271
+ replacement: existingAliases[k]
272
+ }));
273
+ }
274
+ // Prepend our aliases (higher priority) before existing ones
275
+ config.resolve.alias = [...wbcAliases, ...aliasArray];
276
+ config.resolve.dedupe = ['vue', 'vuetify', 'vuex', 'vue-router'];
277
+
278
+ // ── Prettier v3 subpath aliases ──
279
+ // Prettier v3 renamed parser-* to plugins/*. Add aliases for legacy imports.
280
+ let prettierDir;
281
+ try {
282
+ prettierDir = path.dirname(require.resolve('prettier/package.json'));
283
+ } catch (e) {
284
+ prettierDir = path.resolve(rootNodeModules, 'prettier');
285
+ }
286
+ const prettierSubpathAliases = [
287
+ { find: 'prettier/parser-html', replacement: path.resolve(prettierDir, 'plugins/html.mjs') },
288
+ { find: 'prettier/parser-babel', replacement: path.resolve(prettierDir, 'plugins/babel.mjs') },
289
+ { find: 'prettier/parser-postcss', replacement: path.resolve(prettierDir, 'plugins/postcss.mjs') },
290
+ { find: 'prettier/parser-yaml', replacement: path.resolve(prettierDir, 'plugins/yaml.mjs') },
291
+ { find: 'prettier/parser-typescript', replacement: path.resolve(prettierDir, 'plugins/typescript.mjs') },
292
+ { find: 'prettier/standalone', replacement: path.resolve(prettierDir, 'standalone.mjs') },
293
+ ];
294
+
295
+ // Prepend prettier subpath aliases (before the generic 'prettier' alias)
296
+ config.resolve.alias = [...prettierSubpathAliases, ...config.resolve.alias];
297
+
298
+ // Return remaining config to be merged normally
299
+ return {
300
+ define: {
301
+ 'process.env.NODE_ENV': JSON.stringify('development'),
302
+ 'process.env.VUE_APP_BUILD_MODE': JSON.stringify('local'),
303
+ 'process.env.IS_ROLLUP': JSON.stringify('false'),
304
+ 'process.env.BASE_URL': JSON.stringify('/'),
305
+ 'global': 'globalThis',
306
+ '__WBC_PRO__': JSON.stringify(process.env.WBC_PRO === 'true'),
307
+ '__WBC_DEV__': JSON.stringify(process.env.WBC_PRO === undefined),
308
+ },
309
+ assetsInclude: ['**/*.xlsx', '**/*.xls', '**/*.doc', '**/*.docx', '**/*.ppt', '**/*.pptx', '**/*.zip', '**/*.pdf'],
310
+ optimizeDeps: {
311
+ entries: ['index.html'],
312
+ include: [...DEPS.filter(d => !['vuetify', 'prettier'].includes(d)), 'vuetify/lib', 'lodash/debounce']
313
+ },
314
+ css: {
315
+ preprocessorOptions: {
316
+ sass: {
317
+ api: 'legacy',
318
+ quietDeps: true,
319
+ silenceDeprecations: ['slash-div']
320
+ },
321
+ scss: {
322
+ api: 'legacy',
323
+ quietDeps: true,
324
+ silenceDeprecations: ['slash-div']
325
+ }
326
+ }
327
+ },
328
+ server: {
329
+ fs: {
330
+ allow: [
331
+ path.resolve(pluginDir, './'),
332
+ path.resolve(monoRoot),
333
+ path.resolve(monoRoot, '../../'),
334
+ ]
335
+ },
336
+ watch: {
337
+ ignored: ['**/dist-cli/**', '**/dist-vite/**', '**/dist-webpack/**', '**/node_modules/**']
338
+ }
339
+ }
340
+ };
341
+ }
342
+ }
343
+ }
344
+
345
+ export function wbcVitePlugin() {
346
+ return [
347
+ requireContextPlugin(),
348
+ rawFileLoaderPlugin(),
349
+ monorepoDevConfigPlugin(),
350
+ ];
351
+ }
352
+
353
+ export { requireContextPlugin, rawFileLoaderPlugin };
354
+ export default wbcVitePlugin;
package/wbc.webpack.js ADDED
@@ -0,0 +1,95 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+
4
+ module.exports = {
5
+ chainWebpack: (config) => {
6
+ // 1. Basic Rules
7
+ if (!config.module.rules.has('vue')) {
8
+ config.module.rule('vue').test(/\.vue$/).use('vue-loader').loader('vue-loader').end();
9
+ }
10
+ if (!config.module.rules.has('js')) {
11
+ config.module.rule('js')
12
+ .test(/\.(js|jsx)$/)
13
+ .exclude.add(/node_modules/).end()
14
+ .use('babel-loader')
15
+ .loader(require.resolve('babel-loader'))
16
+ .options({
17
+ presets: [
18
+ [require.resolve('@babel/preset-env'), { modules: false }],
19
+ require.resolve('@babel/preset-react')
20
+ ]
21
+ });
22
+ }
23
+ config.module.rule('mjs-interop').test(/\.m?js/).merge({ resolve: { fullySpecified: false } });
24
+
25
+ // 2. Asset Rules (REPAIRED: Support for images/videos/fonts) 🛡️🚀
26
+ config.module.rule('assets')
27
+ .test(/\.(png|jpe?g|gif|svg|webp|avif|mp4|webm|ogg|mp3|wav|flac|aac|woff2?|eot|ttf|otf)$/i)
28
+ .type('asset/resource')
29
+ .set('generator', { filename: 'assets/[name].[hash:8][ext]' });
30
+
31
+ // 3. Style Rules
32
+ const styleRules = [
33
+ { name: 'css', test: /\.css$/ },
34
+ { name: 'scss', test: /\.scss$/, loader: 'sass-loader', options: {} },
35
+ { name: 'sass', test: /\.sass$/, loader: 'sass-loader', options: { sassOptions: { indentedSyntax: true } } }
36
+ ];
37
+
38
+ styleRules.forEach(r => {
39
+ if (!config.module.rules.has(r.name)) {
40
+ const rule = config.module.rule(r.name).test(r.test);
41
+ rule.use('style-loader').loader(require.resolve('style-loader')).end();
42
+ rule.use('css-loader').loader(require.resolve('css-loader')).end();
43
+ rule.use('postcss-loader').loader(require.resolve('postcss-loader')).end();
44
+ if (r.loader) {
45
+ rule.use(r.name + '-loader').loader(require.resolve(r.loader)).options({
46
+ ...r.options,
47
+ sassOptions: {
48
+ ...(r.options.sassOptions || {}),
49
+ api: 'modern-compiler',
50
+ silenceDeprecations: ['slash-div']
51
+ }
52
+ }).end();
53
+ }
54
+ }
55
+ });
56
+
57
+ // 4. Raw loaders
58
+ config.module.rule('wbc-raw').test(/\.(txt|py|php|sh|rs|md|tex|html|xml|yaml|yml)$/).exclude.add(/index\.html/).end().use('raw-loader').loader(require.resolve('raw-loader')).end();
59
+ config.module.rule('wbc-html-snippets').test(/sampleHtml\.js$/).use('raw-loader').loader(require.resolve('raw-loader')).end();
60
+
61
+ // 5. Aliases ⚓️
62
+ const projectRoot = process.cwd();
63
+ const localR = (p) => path.resolve(projectRoot, p);
64
+ const libDir = __dirname;
65
+ const monoRoot = path.dirname(libDir);
66
+
67
+ const resolveSafe = (m) => {
68
+ try { return path.dirname(require.resolve(`${m}/package.json`)); } catch(e) { return localR(`node_modules/${m}`); }
69
+ };
70
+
71
+ config.resolve.alias
72
+ .set('@', projectRoot)
73
+ .set('@src', localR('src'))
74
+ .set('vue$', require.resolve('vue/dist/vue.esm.js'))
75
+ .set('vue', resolveSafe('vue'))
76
+ .set('vuetify$', resolveSafe('vuetify'))
77
+ .set('wbc-ui2', libDir)
78
+ .set('@wbc-ui/code2', path.join(monoRoot, 'wb-code'))
79
+ .set('@wbc-ui/chart2', path.join(monoRoot, 'wb-chart'))
80
+ .set('@wbc-ui/latex2', path.join(monoRoot, 'wb-latex'))
81
+ .set('@wbc-ui/mermaid2', path.join(monoRoot, 'wb-mermaid'))
82
+ .set('@wbc-ui/wiki-obj2', path.join(monoRoot, 'wbc-wiki-obj'));
83
+
84
+ config.resolve.modules
85
+ .add(localR('node_modules'))
86
+ .add(path.join(monoRoot, 'node_modules'))
87
+ .add('node_modules');
88
+
89
+ // Avoid HMR conflicts in monorepo if explicitly requested
90
+ if (config.devServer.has('hot')) {
91
+ // If HMR is still causing browser crashes, we force liveReload instead
92
+ // config.devServer.hot(false).liveReload(true);
93
+ }
94
+ }
95
+ };