juxscript 1.0.0
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 +292 -0
- package/bin/cli.js +149 -0
- package/lib/adapters/base-adapter.js +35 -0
- package/lib/adapters/index.js +33 -0
- package/lib/adapters/mysql-adapter.js +65 -0
- package/lib/adapters/postgres-adapter.js +70 -0
- package/lib/adapters/sqlite-adapter.js +56 -0
- package/lib/components/app.ts +124 -0
- package/lib/components/button.ts +136 -0
- package/lib/components/card.ts +205 -0
- package/lib/components/chart.ts +125 -0
- package/lib/components/code.ts +242 -0
- package/lib/components/container.ts +282 -0
- package/lib/components/data.ts +105 -0
- package/lib/components/docs-data.json +1211 -0
- package/lib/components/error-handler.ts +285 -0
- package/lib/components/footer.ts +146 -0
- package/lib/components/header.ts +167 -0
- package/lib/components/hero.ts +170 -0
- package/lib/components/import.ts +430 -0
- package/lib/components/input.ts +175 -0
- package/lib/components/layout.ts +113 -0
- package/lib/components/list.ts +392 -0
- package/lib/components/main.ts +111 -0
- package/lib/components/menu.ts +170 -0
- package/lib/components/modal.ts +216 -0
- package/lib/components/nav.ts +136 -0
- package/lib/components/node.ts +200 -0
- package/lib/components/reactivity.js +104 -0
- package/lib/components/script.ts +152 -0
- package/lib/components/sidebar.ts +168 -0
- package/lib/components/style.ts +129 -0
- package/lib/components/table.ts +279 -0
- package/lib/components/tabs.ts +191 -0
- package/lib/components/theme.ts +97 -0
- package/lib/components/view.ts +174 -0
- package/lib/jux.ts +203 -0
- package/lib/layouts/default.css +260 -0
- package/lib/layouts/default.jux +8 -0
- package/lib/layouts/figma.css +334 -0
- package/lib/layouts/figma.jux +0 -0
- package/lib/layouts/notion.css +258 -0
- package/lib/styles/base-theme.css +186 -0
- package/lib/styles/dark-theme.css +144 -0
- package/lib/styles/global.css +1131 -0
- package/lib/styles/light-theme.css +144 -0
- package/lib/styles/tokens/dark.css +86 -0
- package/lib/styles/tokens/light.css +86 -0
- package/lib/themes/dark.css +86 -0
- package/lib/themes/light.css +86 -0
- package/lib/utils/path-resolver.js +23 -0
- package/machinery/compiler.js +262 -0
- package/machinery/doc-generator.js +160 -0
- package/machinery/generators/css.js +128 -0
- package/machinery/generators/html.js +108 -0
- package/machinery/imports.js +155 -0
- package/machinery/server.js +185 -0
- package/machinery/validators/file-validator.js +123 -0
- package/machinery/watcher.js +148 -0
- package/package.json +58 -0
- package/types/globals.d.ts +16 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Reactive, getOrCreateContainer } from './reactivity.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hero component options
|
|
5
|
+
*/
|
|
6
|
+
export interface HeroOptions {
|
|
7
|
+
title?: string;
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
cta?: string;
|
|
10
|
+
ctaLink?: string;
|
|
11
|
+
backgroundImage?: string;
|
|
12
|
+
variant?: 'default' | 'centered' | 'split';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Hero component state
|
|
17
|
+
*/
|
|
18
|
+
type HeroState = {
|
|
19
|
+
title: string;
|
|
20
|
+
subtitle: string;
|
|
21
|
+
cta: string;
|
|
22
|
+
ctaLink: string;
|
|
23
|
+
backgroundImage: string;
|
|
24
|
+
variant: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hero component
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* const hero = jux.hero('myHero', {
|
|
32
|
+
* title: 'Welcome',
|
|
33
|
+
* subtitle: 'Get started today',
|
|
34
|
+
* cta: 'Learn More'
|
|
35
|
+
* });
|
|
36
|
+
* hero.render();
|
|
37
|
+
*/
|
|
38
|
+
export class Hero extends Reactive {
|
|
39
|
+
state!: HeroState;
|
|
40
|
+
container: HTMLElement | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(componentId: string, options: HeroOptions = {}) {
|
|
43
|
+
super();
|
|
44
|
+
this._setComponentId(componentId);
|
|
45
|
+
|
|
46
|
+
this.state = this._createReactiveState({
|
|
47
|
+
title: options.title ?? '',
|
|
48
|
+
subtitle: options.subtitle ?? '',
|
|
49
|
+
cta: options.cta ?? '',
|
|
50
|
+
ctaLink: options.ctaLink ?? '#',
|
|
51
|
+
backgroundImage: options.backgroundImage ?? '',
|
|
52
|
+
variant: options.variant ?? 'default'
|
|
53
|
+
}) as HeroState;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* -------------------------
|
|
57
|
+
* Fluent API
|
|
58
|
+
* ------------------------- */
|
|
59
|
+
|
|
60
|
+
title(value: string): this {
|
|
61
|
+
this.state.title = value;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
subtitle(value: string): this {
|
|
66
|
+
this.state.subtitle = value;
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
cta(value: string): this {
|
|
71
|
+
this.state.cta = value;
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ctaLink(value: string): this {
|
|
76
|
+
this.state.ctaLink = value;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
backgroundImage(value: string): this {
|
|
81
|
+
this.state.backgroundImage = value;
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
variant(value: string): this {
|
|
86
|
+
this.state.variant = value;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* -------------------------
|
|
91
|
+
* Render
|
|
92
|
+
* ------------------------- */
|
|
93
|
+
|
|
94
|
+
render(targetId?: string): this {
|
|
95
|
+
let container: HTMLElement;
|
|
96
|
+
|
|
97
|
+
if (targetId) {
|
|
98
|
+
const target = document.querySelector(targetId);
|
|
99
|
+
if (!target || !(target instanceof HTMLElement)) {
|
|
100
|
+
throw new Error(`Hero: Target element "${targetId}" not found`);
|
|
101
|
+
}
|
|
102
|
+
container = target;
|
|
103
|
+
} else {
|
|
104
|
+
container = getOrCreateContainer(this._componentId) as HTMLElement;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.container = container;
|
|
108
|
+
const { title, subtitle, cta, ctaLink, backgroundImage, variant } = this.state;
|
|
109
|
+
|
|
110
|
+
const hero = document.createElement('div');
|
|
111
|
+
hero.className = `jux-hero jux-hero-${variant}`;
|
|
112
|
+
hero.id = this._componentId;
|
|
113
|
+
|
|
114
|
+
if (backgroundImage) {
|
|
115
|
+
hero.style.backgroundImage = `url(${backgroundImage})`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const content = document.createElement('div');
|
|
119
|
+
content.className = 'jux-hero-content';
|
|
120
|
+
|
|
121
|
+
if (title) {
|
|
122
|
+
const titleEl = document.createElement('h1');
|
|
123
|
+
titleEl.className = 'jux-hero-title';
|
|
124
|
+
titleEl.textContent = title;
|
|
125
|
+
content.appendChild(titleEl);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (subtitle) {
|
|
129
|
+
const subtitleEl = document.createElement('p');
|
|
130
|
+
subtitleEl.className = 'jux-hero-subtitle';
|
|
131
|
+
subtitleEl.textContent = subtitle;
|
|
132
|
+
content.appendChild(subtitleEl);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (cta) {
|
|
136
|
+
const ctaEl = document.createElement('a');
|
|
137
|
+
ctaEl.className = 'jux-hero-cta jux-button jux-button-primary';
|
|
138
|
+
ctaEl.href = ctaLink;
|
|
139
|
+
ctaEl.textContent = cta;
|
|
140
|
+
content.appendChild(ctaEl);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
hero.appendChild(content);
|
|
144
|
+
container.appendChild(hero);
|
|
145
|
+
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Render to another Jux component's container
|
|
151
|
+
*/
|
|
152
|
+
renderTo(juxComponent: any): this {
|
|
153
|
+
if (!juxComponent || typeof juxComponent !== 'object') {
|
|
154
|
+
throw new Error('Hero.renderTo: Invalid component - not an object');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
|
|
158
|
+
throw new Error('Hero.renderTo: Invalid component - missing _componentId (not a Jux component)');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this.render(`#${juxComponent._componentId}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Factory helper
|
|
167
|
+
*/
|
|
168
|
+
export function hero(componentId: string, options: HeroOptions = {}): Hero {
|
|
169
|
+
return new Hero(componentId, options);
|
|
170
|
+
}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { ErrorHandler } from './error-handler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Import - Import external resources (CSS, JS, images, fonts, etc.)
|
|
5
|
+
* Provides flexible resource loading with control over injection location
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type ImportType = 'stylesheet' | 'script' | 'image' | 'font' | 'preload' | 'prefetch' | 'module';
|
|
9
|
+
export type ImportLocation = 'head' | 'body-start' | 'body-end';
|
|
10
|
+
|
|
11
|
+
export interface ImportOptions {
|
|
12
|
+
type?: ImportType;
|
|
13
|
+
href?: string;
|
|
14
|
+
src?: string;
|
|
15
|
+
as?: string;
|
|
16
|
+
crossOrigin?: 'anonymous' | 'use-credentials';
|
|
17
|
+
integrity?: string;
|
|
18
|
+
async?: boolean;
|
|
19
|
+
defer?: boolean;
|
|
20
|
+
module?: boolean;
|
|
21
|
+
location?: ImportLocation;
|
|
22
|
+
onLoad?: () => void;
|
|
23
|
+
onError?: (error: Error) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class Import {
|
|
27
|
+
private _type: ImportType = 'stylesheet';
|
|
28
|
+
private _href: string = '';
|
|
29
|
+
private _src: string = '';
|
|
30
|
+
private _as: string = '';
|
|
31
|
+
private _crossOrigin?: 'anonymous' | 'use-credentials';
|
|
32
|
+
private _integrity?: string;
|
|
33
|
+
private _async: boolean = false;
|
|
34
|
+
private _defer: boolean = false;
|
|
35
|
+
private _module: boolean = false;
|
|
36
|
+
private _location: ImportLocation = 'head';
|
|
37
|
+
private _onLoad?: () => void;
|
|
38
|
+
private _onError?: (error: Error) => void;
|
|
39
|
+
private _element: HTMLElement | null = null;
|
|
40
|
+
|
|
41
|
+
constructor(url: string, options: ImportOptions = {}) {
|
|
42
|
+
// Auto-detect type from URL if not specified
|
|
43
|
+
if (!options.type) {
|
|
44
|
+
if (url.endsWith('.css')) {
|
|
45
|
+
this._type = 'stylesheet';
|
|
46
|
+
} else if (url.endsWith('.js') || url.endsWith('.mjs')) {
|
|
47
|
+
this._type = 'script';
|
|
48
|
+
} else if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i)) {
|
|
49
|
+
this._type = 'image';
|
|
50
|
+
} else if (url.match(/\.(woff|woff2|ttf|otf|eot)$/i)) {
|
|
51
|
+
this._type = 'font';
|
|
52
|
+
} else {
|
|
53
|
+
this._type = 'preload';
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
this._type = options.type;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Set URL based on type
|
|
60
|
+
if (this._type === 'script') {
|
|
61
|
+
this._src = url;
|
|
62
|
+
} else {
|
|
63
|
+
this._href = url;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Apply options
|
|
67
|
+
if (options.as) this._as = options.as;
|
|
68
|
+
if (options.crossOrigin) this._crossOrigin = options.crossOrigin;
|
|
69
|
+
if (options.integrity) this._integrity = options.integrity;
|
|
70
|
+
if (options.async !== undefined) this._async = options.async;
|
|
71
|
+
if (options.defer !== undefined) this._defer = options.defer;
|
|
72
|
+
if (options.module !== undefined) this._module = options.module;
|
|
73
|
+
if (options.location) this._location = options.location;
|
|
74
|
+
if (options.onLoad) this._onLoad = options.onLoad;
|
|
75
|
+
if (options.onError) this._onError = options.onError;
|
|
76
|
+
|
|
77
|
+
// Auto-render
|
|
78
|
+
this.render();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Set the resource type
|
|
83
|
+
*/
|
|
84
|
+
type(type: ImportType): this {
|
|
85
|
+
this._type = type;
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set the URL (for link elements)
|
|
91
|
+
*/
|
|
92
|
+
href(url: string): this {
|
|
93
|
+
this._href = url;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the URL (for script elements)
|
|
99
|
+
*/
|
|
100
|
+
src(url: string): this {
|
|
101
|
+
this._src = url;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Set the 'as' attribute (for preload/prefetch)
|
|
107
|
+
*/
|
|
108
|
+
as(value: string): this {
|
|
109
|
+
this._as = value;
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Set crossorigin attribute
|
|
115
|
+
*/
|
|
116
|
+
crossOrigin(value: 'anonymous' | 'use-credentials'): this {
|
|
117
|
+
this._crossOrigin = value;
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Set integrity hash for SRI (Subresource Integrity)
|
|
123
|
+
*/
|
|
124
|
+
integrity(hash: string): this {
|
|
125
|
+
this._integrity = hash;
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Enable async loading (for scripts)
|
|
131
|
+
*/
|
|
132
|
+
async(value: boolean = true): this {
|
|
133
|
+
this._async = value;
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Enable defer loading (for scripts)
|
|
139
|
+
*/
|
|
140
|
+
defer(value: boolean = true): this {
|
|
141
|
+
this._defer = value;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Mark as ES module (for scripts)
|
|
147
|
+
*/
|
|
148
|
+
module(value: boolean = true): this {
|
|
149
|
+
this._module = value;
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Set injection location
|
|
155
|
+
*/
|
|
156
|
+
location(loc: ImportLocation): this {
|
|
157
|
+
this._location = loc;
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Set onload callback
|
|
163
|
+
*/
|
|
164
|
+
onLoad(callback: () => void): this {
|
|
165
|
+
this._onLoad = callback;
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Set onerror callback
|
|
171
|
+
*/
|
|
172
|
+
onError(callback: (error: Error) => void): this {
|
|
173
|
+
this._onError = callback;
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the target container for injection
|
|
179
|
+
*/
|
|
180
|
+
private getContainer(): HTMLElement {
|
|
181
|
+
switch (this._location) {
|
|
182
|
+
case 'head':
|
|
183
|
+
return document.head;
|
|
184
|
+
case 'body-start':
|
|
185
|
+
return document.body;
|
|
186
|
+
case 'body-end':
|
|
187
|
+
return document.body;
|
|
188
|
+
default:
|
|
189
|
+
return document.head;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Render the import element
|
|
195
|
+
*/
|
|
196
|
+
render(): this {
|
|
197
|
+
if (typeof document === 'undefined') {
|
|
198
|
+
return this;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
// Remove existing element
|
|
203
|
+
this.remove();
|
|
204
|
+
|
|
205
|
+
let element: HTMLElement;
|
|
206
|
+
|
|
207
|
+
switch (this._type) {
|
|
208
|
+
case 'stylesheet':
|
|
209
|
+
element = this.createStylesheet();
|
|
210
|
+
break;
|
|
211
|
+
case 'script':
|
|
212
|
+
element = this.createScript();
|
|
213
|
+
break;
|
|
214
|
+
case 'image':
|
|
215
|
+
element = this.createImage();
|
|
216
|
+
break;
|
|
217
|
+
case 'font':
|
|
218
|
+
element = this.createFont();
|
|
219
|
+
break;
|
|
220
|
+
case 'preload':
|
|
221
|
+
element = this.createPreload();
|
|
222
|
+
break;
|
|
223
|
+
case 'prefetch':
|
|
224
|
+
element = this.createPrefetch();
|
|
225
|
+
break;
|
|
226
|
+
case 'module':
|
|
227
|
+
element = this.createModule();
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
throw new Error(`Unknown import type: ${this._type}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const container = this.getContainer();
|
|
234
|
+
|
|
235
|
+
if (this._location === 'body-end') {
|
|
236
|
+
container.appendChild(element);
|
|
237
|
+
} else {
|
|
238
|
+
container.insertBefore(element, container.firstChild);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this._element = element;
|
|
242
|
+
} catch (error: any) {
|
|
243
|
+
ErrorHandler.captureError({
|
|
244
|
+
component: 'Import',
|
|
245
|
+
method: 'render',
|
|
246
|
+
message: error.message,
|
|
247
|
+
stack: error.stack,
|
|
248
|
+
timestamp: new Date(),
|
|
249
|
+
context: {
|
|
250
|
+
type: this._type,
|
|
251
|
+
href: this._href,
|
|
252
|
+
src: this._src,
|
|
253
|
+
location: this._location,
|
|
254
|
+
error: 'runtime_exception'
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Create a stylesheet link element
|
|
264
|
+
*/
|
|
265
|
+
private createStylesheet(): HTMLLinkElement {
|
|
266
|
+
const link = document.createElement('link');
|
|
267
|
+
link.rel = 'stylesheet';
|
|
268
|
+
link.href = this._href;
|
|
269
|
+
|
|
270
|
+
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
271
|
+
if (this._integrity) link.integrity = this._integrity;
|
|
272
|
+
|
|
273
|
+
link.onload = () => {
|
|
274
|
+
console.log(`✓ Stylesheet loaded: ${this._href}`);
|
|
275
|
+
if (this._onLoad) this._onLoad();
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
link.onerror = () => {
|
|
279
|
+
const error = new Error(`Failed to load stylesheet: ${this._href}`);
|
|
280
|
+
ErrorHandler.captureError({
|
|
281
|
+
component: 'Import',
|
|
282
|
+
method: 'createStylesheet',
|
|
283
|
+
message: error.message,
|
|
284
|
+
timestamp: new Date(),
|
|
285
|
+
context: { href: this._href, type: 'stylesheet', error: 'load_failed' }
|
|
286
|
+
});
|
|
287
|
+
if (this._onError) this._onError(error);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
return link;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Create a script element
|
|
295
|
+
*/
|
|
296
|
+
private createScript(): HTMLScriptElement {
|
|
297
|
+
const script = document.createElement('script');
|
|
298
|
+
script.src = this._src;
|
|
299
|
+
|
|
300
|
+
if (this._async) script.async = true;
|
|
301
|
+
if (this._defer) script.defer = true;
|
|
302
|
+
if (this._module) script.type = 'module';
|
|
303
|
+
if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
|
|
304
|
+
if (this._integrity) script.integrity = this._integrity;
|
|
305
|
+
|
|
306
|
+
script.onload = () => {
|
|
307
|
+
console.log(`✓ Script loaded: ${this._src}`);
|
|
308
|
+
if (this._onLoad) this._onLoad();
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
script.onerror = () => {
|
|
312
|
+
const error = new Error(`Failed to load script: ${this._src}`);
|
|
313
|
+
ErrorHandler.captureError({
|
|
314
|
+
component: 'Import',
|
|
315
|
+
method: 'createScript',
|
|
316
|
+
message: error.message,
|
|
317
|
+
timestamp: new Date(),
|
|
318
|
+
context: { src: this._src, type: 'script', error: 'load_failed' }
|
|
319
|
+
});
|
|
320
|
+
if (this._onError) this._onError(error);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
return script;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Create an image preload link
|
|
328
|
+
*/
|
|
329
|
+
private createImage(): HTMLLinkElement {
|
|
330
|
+
const link = document.createElement('link');
|
|
331
|
+
link.rel = 'preload';
|
|
332
|
+
link.as = 'image';
|
|
333
|
+
link.href = this._href;
|
|
334
|
+
|
|
335
|
+
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
336
|
+
|
|
337
|
+
return link;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Create a font preload link
|
|
342
|
+
*/
|
|
343
|
+
private createFont(): HTMLLinkElement {
|
|
344
|
+
const link = document.createElement('link');
|
|
345
|
+
link.rel = 'preload';
|
|
346
|
+
link.as = 'font';
|
|
347
|
+
link.href = this._href;
|
|
348
|
+
link.crossOrigin = this._crossOrigin || 'anonymous';
|
|
349
|
+
|
|
350
|
+
return link;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Create a preload link
|
|
355
|
+
*/
|
|
356
|
+
private createPreload(): HTMLLinkElement {
|
|
357
|
+
const link = document.createElement('link');
|
|
358
|
+
link.rel = 'preload';
|
|
359
|
+
link.href = this._href;
|
|
360
|
+
|
|
361
|
+
if (this._as) link.as = this._as;
|
|
362
|
+
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
363
|
+
if (this._integrity) link.integrity = this._integrity;
|
|
364
|
+
|
|
365
|
+
return link;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Create a prefetch link
|
|
370
|
+
*/
|
|
371
|
+
private createPrefetch(): HTMLLinkElement {
|
|
372
|
+
const link = document.createElement('link');
|
|
373
|
+
link.rel = 'prefetch';
|
|
374
|
+
link.href = this._href;
|
|
375
|
+
|
|
376
|
+
if (this._as) link.as = this._as;
|
|
377
|
+
if (this._crossOrigin) link.crossOrigin = this._crossOrigin;
|
|
378
|
+
|
|
379
|
+
return link;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Create a module script
|
|
384
|
+
*/
|
|
385
|
+
private createModule(): HTMLScriptElement {
|
|
386
|
+
const script = document.createElement('script');
|
|
387
|
+
script.type = 'module';
|
|
388
|
+
script.src = this._src;
|
|
389
|
+
|
|
390
|
+
if (this._crossOrigin) script.crossOrigin = this._crossOrigin;
|
|
391
|
+
if (this._integrity) script.integrity = this._integrity;
|
|
392
|
+
|
|
393
|
+
script.onload = () => {
|
|
394
|
+
console.log(`✓ Module loaded: ${this._src}`);
|
|
395
|
+
if (this._onLoad) this._onLoad();
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
script.onerror = () => {
|
|
399
|
+
const error = new Error(`Failed to load module: ${this._src}`);
|
|
400
|
+
ErrorHandler.captureError({
|
|
401
|
+
component: 'Import',
|
|
402
|
+
method: 'createModule',
|
|
403
|
+
message: error.message,
|
|
404
|
+
timestamp: new Date(),
|
|
405
|
+
context: { src: this._src, type: 'module', error: 'load_failed' }
|
|
406
|
+
});
|
|
407
|
+
if (this._onError) this._onError(error);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
return script;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Remove the element from the document
|
|
415
|
+
*/
|
|
416
|
+
remove(): this {
|
|
417
|
+
if (this._element && this._element.parentNode) {
|
|
418
|
+
this._element.parentNode.removeChild(this._element);
|
|
419
|
+
this._element = null;
|
|
420
|
+
}
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Factory function for creating imports
|
|
427
|
+
*/
|
|
428
|
+
export function importResource(url: string, options?: ImportOptions): Import {
|
|
429
|
+
return new Import(url, options);
|
|
430
|
+
}
|