ezfw-core 1.0.97 → 1.0.99
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/components/icon/icons.ts +11 -2
- package/core/ezEntryPlugin.js +14 -4
- package/package.json +1 -1
- package/template/vite.config.ts +34 -31
package/components/icon/icons.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icon utilities
|
|
3
|
-
*
|
|
3
|
+
* Uses a registry pattern - icons are registered by the project's icons.config
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// Icon registry - populated by registerIcons()
|
|
7
|
+
let ICONS: Record<string, string> = {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register icons from project's icons.config.ts
|
|
11
|
+
* Called automatically by the framework entry point
|
|
12
|
+
*/
|
|
13
|
+
export function registerIcons(icons: Record<string, string>): void {
|
|
14
|
+
ICONS = { ...ICONS, ...icons };
|
|
15
|
+
}
|
|
7
16
|
|
|
8
17
|
/**
|
|
9
18
|
* Get icon SVG content by name
|
package/core/ezEntryPlugin.js
CHANGED
|
@@ -38,11 +38,15 @@ function generateIndexCode(config, mode) {
|
|
|
38
38
|
? `import './ez/core/ez.js';
|
|
39
39
|
import './ez-base.css';
|
|
40
40
|
import './ez/themes/ez-theme.scss';
|
|
41
|
-
import './ez/themes/ez-theme-slate.scss'
|
|
41
|
+
import './ez/themes/ez-theme-slate.scss';
|
|
42
|
+
import { registerIcons } from './ez/components/icon/icons.js';
|
|
43
|
+
import { ICONS } from './app/config/icons.config.js';`
|
|
42
44
|
: `import 'ezfw-core';
|
|
43
45
|
import 'ezfw-core/ez-base.css';
|
|
44
46
|
import 'ezfw-core/themes/ez-theme.scss';
|
|
45
|
-
import 'ezfw-core/themes/ez-theme-slate.scss'
|
|
47
|
+
import 'ezfw-core/themes/ez-theme-slate.scss';
|
|
48
|
+
import { registerIcons } from 'ezfw-core/components/icon/icons.js';
|
|
49
|
+
import { ICONS } from './app/config/icons.config.js';`;
|
|
46
50
|
|
|
47
51
|
// Framework module globs differ based on mode
|
|
48
52
|
const fwModulesGlob = mode === 'local'
|
|
@@ -87,6 +91,7 @@ ${i18nCode}
|
|
|
87
91
|
|
|
88
92
|
// Framework internals
|
|
89
93
|
ez.defineStyles(stylesConfig);
|
|
94
|
+
registerIcons(ICONS);
|
|
90
95
|
|
|
91
96
|
${fwModulesGlob}
|
|
92
97
|
|
|
@@ -151,11 +156,15 @@ function generateHydrateCode(config, mode) {
|
|
|
151
156
|
? `import './ez/core/ez.js';
|
|
152
157
|
import './ez-base.css';
|
|
153
158
|
import './ez/themes/ez-theme.scss';
|
|
154
|
-
import './ez/themes/ez-theme-slate.scss'
|
|
159
|
+
import './ez/themes/ez-theme-slate.scss';
|
|
160
|
+
import { registerIcons } from './ez/components/icon/icons.js';
|
|
161
|
+
import { ICONS } from './app/config/icons.config.js';`
|
|
155
162
|
: `import 'ezfw-core';
|
|
156
163
|
import 'ezfw-core/ez-base.css';
|
|
157
164
|
import 'ezfw-core/themes/ez-theme.scss';
|
|
158
|
-
import 'ezfw-core/themes/ez-theme-slate.scss'
|
|
165
|
+
import 'ezfw-core/themes/ez-theme-slate.scss';
|
|
166
|
+
import { registerIcons } from 'ezfw-core/components/icon/icons.js';
|
|
167
|
+
import { ICONS } from './app/config/icons.config.js';`;
|
|
159
168
|
|
|
160
169
|
// Framework module globs differ based on mode
|
|
161
170
|
const fwModulesGlob = mode === 'local'
|
|
@@ -200,6 +209,7 @@ ${i18nCode}
|
|
|
200
209
|
|
|
201
210
|
// Framework internals
|
|
202
211
|
ez.defineStyles(stylesConfig);
|
|
212
|
+
registerIcons(ICONS);
|
|
203
213
|
|
|
204
214
|
${fwModulesGlob}
|
|
205
215
|
|
package/package.json
CHANGED
package/template/vite.config.ts
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
-
// vite.config.ts - Ez Framework
|
|
1
|
+
// vite.config.ts - Ez Framework
|
|
2
2
|
import { defineConfig, Plugin } from 'vite';
|
|
3
|
-
import { ezIslands } from '
|
|
3
|
+
import { ezEntry, ezIslands, ezJsx } from 'ezfw-core/vite';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
|
|
7
|
-
// Plugin to
|
|
7
|
+
// Plugin to inline critical CSS and make other CSS non-render-blocking
|
|
8
8
|
function asyncCssPlugin(): Plugin {
|
|
9
9
|
return {
|
|
10
10
|
name: 'async-css',
|
|
11
11
|
enforce: 'post',
|
|
12
12
|
transformIndexHtml(html) {
|
|
13
|
-
//
|
|
14
|
-
|
|
13
|
+
// Read and inline global.css (critical reset styles)
|
|
14
|
+
const globalCssPath = path.resolve(__dirname, 'public/global.css');
|
|
15
|
+
if (fs.existsSync(globalCssPath)) {
|
|
16
|
+
const globalCss = fs.readFileSync(globalCssPath, 'utf-8')
|
|
17
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
18
|
+
.replace(/\s+/g, ' ')
|
|
19
|
+
.trim();
|
|
20
|
+
html = html.replace(
|
|
21
|
+
/<!-- INLINE_GLOBAL_CSS -->/,
|
|
22
|
+
`<style>${globalCss}</style>`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
// Make main CSS async (ez.styles bundle)
|
|
26
|
+
html = html.replace(
|
|
27
|
+
/<link rel="stylesheet" crossorigin href="(\/assets\/ez\.styles[^"]+\.css)">/g,
|
|
28
|
+
'<link rel="stylesheet" href="$1" media="print" onload="this.media=\'all\'" crossorigin><noscript><link rel="stylesheet" href="$1" crossorigin></noscript>'
|
|
29
|
+
);
|
|
30
|
+
// Transform CSS links for non-critical component stylesheets
|
|
31
|
+
html = html.replace(
|
|
15
32
|
/<link rel="stylesheet" crossorigin href="(\/assets\/(EzToast|EzMask|EzTooltip|EzDialog|EzDrawer)[^"]+\.css)">/g,
|
|
16
33
|
'<link rel="stylesheet" href="$1" media="print" onload="this.media=\'all\'">'
|
|
17
34
|
);
|
|
35
|
+
return html;
|
|
18
36
|
}
|
|
19
37
|
};
|
|
20
38
|
}
|
|
@@ -26,16 +44,12 @@ function ssrPreviewPlugin(): Plugin {
|
|
|
26
44
|
configurePreviewServer(server) {
|
|
27
45
|
server.middlewares.use((req, res, next) => {
|
|
28
46
|
const url = req.url || '/';
|
|
29
|
-
// Skip assets and files with extensions
|
|
30
47
|
if (url.startsWith('/assets/') || url.includes('.')) {
|
|
31
48
|
return next();
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
// Try to find index.html in the subdirectory
|
|
35
51
|
const distPath = path.resolve(__dirname, 'dist');
|
|
36
52
|
let htmlPath = path.join(distPath, url, 'index.html');
|
|
37
|
-
|
|
38
|
-
// Normalize path for Windows
|
|
39
53
|
htmlPath = path.normalize(htmlPath);
|
|
40
54
|
|
|
41
55
|
if (fs.existsSync(htmlPath)) {
|
|
@@ -56,15 +70,21 @@ export default defineConfig({
|
|
|
56
70
|
root: '.',
|
|
57
71
|
appType: 'mpa',
|
|
58
72
|
resolve: {
|
|
59
|
-
extensions: ['.ts', '.js', '.mjs', '.json']
|
|
73
|
+
extensions: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.json']
|
|
60
74
|
},
|
|
61
75
|
css: {
|
|
62
76
|
modules: {
|
|
63
|
-
// Don't hash class names - SSR needs predictable names
|
|
64
77
|
generateScopedName: '[local]'
|
|
78
|
+
},
|
|
79
|
+
preprocessorOptions: {
|
|
80
|
+
scss: {
|
|
81
|
+
api: 'modern-compiler'
|
|
82
|
+
}
|
|
65
83
|
}
|
|
66
84
|
},
|
|
67
85
|
plugins: [
|
|
86
|
+
ezEntry(),
|
|
87
|
+
ezJsx(),
|
|
68
88
|
asyncCssPlugin(),
|
|
69
89
|
ssrPreviewPlugin(),
|
|
70
90
|
ezIslands({
|
|
@@ -77,15 +97,15 @@ export default defineConfig({
|
|
|
77
97
|
target: 'esnext',
|
|
78
98
|
minify: 'esbuild',
|
|
79
99
|
cssMinify: true,
|
|
100
|
+
outDir: 'dist',
|
|
80
101
|
rollupOptions: {
|
|
81
102
|
input: {
|
|
82
103
|
main: '/index.html',
|
|
83
104
|
hydrate: '/hydrate.js',
|
|
84
|
-
'islands-runtime': '/
|
|
105
|
+
'islands-runtime': 'ezfw-core/islands/runtime'
|
|
85
106
|
},
|
|
86
107
|
output: {
|
|
87
108
|
entryFileNames: (chunkInfo) => {
|
|
88
|
-
// Keep hydrate and islands-runtime without hash for SSR pages
|
|
89
109
|
if (chunkInfo.name === 'hydrate' || chunkInfo.name === 'islands-runtime') {
|
|
90
110
|
return `assets/${chunkInfo.name}.js`;
|
|
91
111
|
}
|
|
@@ -97,29 +117,16 @@ export default defineConfig({
|
|
|
97
117
|
preview: {
|
|
98
118
|
headers: {
|
|
99
119
|
'Cache-Control': 'no-store',
|
|
100
|
-
// Security headers for Best Practices
|
|
101
120
|
'X-Frame-Options': 'SAMEORIGIN',
|
|
102
121
|
'X-Content-Type-Options': 'nosniff',
|
|
103
122
|
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
104
123
|
'Cross-Origin-Opener-Policy': 'same-origin-allow-popups',
|
|
105
124
|
'Cross-Origin-Embedder-Policy': 'credentialless',
|
|
106
|
-
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()'
|
|
107
|
-
'Content-Security-Policy': [
|
|
108
|
-
"default-src 'self'",
|
|
109
|
-
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://apis.google.com https://*.firebaseapp.com https://*.googleapis.com",
|
|
110
|
-
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com",
|
|
111
|
-
"font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com",
|
|
112
|
-
"img-src 'self' data: https: blob:",
|
|
113
|
-
"connect-src 'self' https://*.googleapis.com https://*.firebaseio.com https://*.firebaseapp.com wss://*.firebaseio.com https://identitytoolkit.googleapis.com https://securetoken.googleapis.com",
|
|
114
|
-
"frame-src 'self' https://*.firebaseapp.com https://accounts.google.com",
|
|
115
|
-
"object-src 'none'",
|
|
116
|
-
"base-uri 'self'"
|
|
117
|
-
].join('; ')
|
|
125
|
+
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()'
|
|
118
126
|
}
|
|
119
127
|
},
|
|
120
128
|
server: {
|
|
121
129
|
headers: {
|
|
122
|
-
// Same headers for dev server
|
|
123
130
|
'X-Frame-Options': 'SAMEORIGIN',
|
|
124
131
|
'X-Content-Type-Options': 'nosniff',
|
|
125
132
|
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
@@ -127,7 +134,3 @@ export default defineConfig({
|
|
|
127
134
|
}
|
|
128
135
|
}
|
|
129
136
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|