@codespark/framework 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/dist/html.d.ts +66 -0
- package/dist/html.js +442 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +87 -0
- package/dist/loaders.d.ts +87 -0
- package/dist/loaders.js +175 -0
- package/dist/markdown.d.ts +52 -0
- package/dist/markdown.js +75 -0
- package/dist/react.d.ts +59 -0
- package/dist/react.js +358 -0
- package/package.json +55 -0
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Framework as Framework$1 } from "@codespark/framework";
|
|
2
|
+
import MagicString from "magic-string";
|
|
3
|
+
|
|
4
|
+
//#region src/loaders/types.d.ts
|
|
5
|
+
declare enum LoaderType {
|
|
6
|
+
ESModule = "esmodule",
|
|
7
|
+
Style = "style",
|
|
8
|
+
Script = "script",
|
|
9
|
+
Asset = "asset",
|
|
10
|
+
}
|
|
11
|
+
interface BaseLoaderOutput<T extends LoaderType> {
|
|
12
|
+
type: T;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
interface ESModuleLoaderOutput extends BaseLoaderOutput<LoaderType.ESModule> {
|
|
16
|
+
dependencies: Record<string, string>;
|
|
17
|
+
externals: {
|
|
18
|
+
name: string;
|
|
19
|
+
imported: string[];
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
interface StyleLoaderOutput extends BaseLoaderOutput<LoaderType.Style> {
|
|
23
|
+
imports: string[];
|
|
24
|
+
href?: string;
|
|
25
|
+
attributes?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
interface ScriptLoaderOutput extends BaseLoaderOutput<LoaderType.Script> {
|
|
28
|
+
src?: string;
|
|
29
|
+
attributes?: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
type AssetLoaderOutput = BaseLoaderOutput<LoaderType.Asset>;
|
|
32
|
+
type LoaderOutput<T extends LoaderType> = Extract<ESModuleLoaderOutput | StyleLoaderOutput | ScriptLoaderOutput | AssetLoaderOutput, {
|
|
33
|
+
type: T;
|
|
34
|
+
}>;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/registry.d.ts
|
|
37
|
+
type Output<T extends LoaderType = LoaderType> = Omit<LoaderOutput<T>, 'type'> & {
|
|
38
|
+
path: string;
|
|
39
|
+
};
|
|
40
|
+
type Outputs = Map<LoaderType, Output[]>;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/html/index.d.ts
|
|
43
|
+
interface FrameworkConfig {
|
|
44
|
+
liteMode?: {
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
htmlEntry?: string;
|
|
47
|
+
scriptEntry?: string;
|
|
48
|
+
styleEntry?: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
declare class Framework extends Framework$1 {
|
|
52
|
+
private config?;
|
|
53
|
+
readonly name = "html";
|
|
54
|
+
readonly imports: {};
|
|
55
|
+
outputs: Outputs;
|
|
56
|
+
private blobUrlMap;
|
|
57
|
+
constructor(config?: FrameworkConfig | undefined);
|
|
58
|
+
analyze(entry: string, files: Record<string, string>): void;
|
|
59
|
+
compile(): string;
|
|
60
|
+
private wrapInLiteModeTemplate;
|
|
61
|
+
private transformModulesToBlob;
|
|
62
|
+
private transformCodeWithBlobUrls;
|
|
63
|
+
}
|
|
64
|
+
declare const html: Framework;
|
|
65
|
+
//#endregion
|
|
66
|
+
export { Framework, FrameworkConfig, html };
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
import { Framework as Framework$1 } from "@codespark/framework";
|
|
3
|
+
import MagicString from "magic-string";
|
|
4
|
+
import { render } from "dom-serializer";
|
|
5
|
+
import { DomUtils, parseDocument } from "htmlparser2";
|
|
6
|
+
import { availablePresets, transform } from "@babel/standalone";
|
|
7
|
+
|
|
8
|
+
//#region src/loaders/types.ts
|
|
9
|
+
let LoaderType = /* @__PURE__ */ function(LoaderType$1) {
|
|
10
|
+
LoaderType$1["ESModule"] = "esmodule";
|
|
11
|
+
LoaderType$1["Style"] = "style";
|
|
12
|
+
LoaderType$1["Script"] = "script";
|
|
13
|
+
LoaderType$1["Asset"] = "asset";
|
|
14
|
+
return LoaderType$1;
|
|
15
|
+
}({});
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/loaders/css-loader.ts
|
|
19
|
+
var CSSLoader = class {
|
|
20
|
+
name = "css-loader";
|
|
21
|
+
test = /\.css$/;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
transform(source, ctx) {
|
|
26
|
+
const { enabled = true, match } = this.config?.tailwind || {};
|
|
27
|
+
const isTailwind = match ? match.test(ctx.resourcePath) : ctx.resourcePath.endsWith(".tw.css");
|
|
28
|
+
const imports = [];
|
|
29
|
+
const content = source.replace(/@import\s+(?:url\()?['"]?([^'")]+)['"]?\)?\s*;?/g, (_, importPath) => {
|
|
30
|
+
const resolved = ctx.resolve(importPath);
|
|
31
|
+
if (resolved) imports.push(resolved);
|
|
32
|
+
return "";
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
type: LoaderType.Style,
|
|
36
|
+
content,
|
|
37
|
+
imports,
|
|
38
|
+
attributes: enabled && isTailwind ? { type: "text/tailwindcss" } : {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/loaders/es-loader.ts
|
|
45
|
+
const parseCode = (code) => parse(code, {
|
|
46
|
+
sourceType: "module",
|
|
47
|
+
plugins: ["jsx", "typescript"]
|
|
48
|
+
}).program.body;
|
|
49
|
+
const collectIdentifiers = (ast) => {
|
|
50
|
+
const ids = /* @__PURE__ */ new Set();
|
|
51
|
+
const walk = (node) => {
|
|
52
|
+
if (!node || typeof node !== "object") return;
|
|
53
|
+
if (node.type === "Identifier" || node.type === "JSXIdentifier") ids.add(node.name);
|
|
54
|
+
for (const k of Object.keys(node)) {
|
|
55
|
+
if (k === "loc" || k === "range") continue;
|
|
56
|
+
const val = node[k];
|
|
57
|
+
if (Array.isArray(val)) val.forEach(walk);
|
|
58
|
+
else if (val && typeof val === "object") walk(val);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
ast.forEach(walk);
|
|
62
|
+
return ids;
|
|
63
|
+
};
|
|
64
|
+
const analyzeImports = (ast) => {
|
|
65
|
+
const imports = ast.filter((node) => node.type === "ImportDeclaration");
|
|
66
|
+
const usedIds = collectIdentifiers(ast.filter((node) => node.type !== "ImportDeclaration"));
|
|
67
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
68
|
+
imports.forEach((imp) => {
|
|
69
|
+
if (imp.importKind === "type") return;
|
|
70
|
+
imp.specifiers.forEach((spec) => {
|
|
71
|
+
if (spec.type === "ImportSpecifier" && spec.importKind === "type") return;
|
|
72
|
+
importMap.set(spec.local.name, imp.source.value);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
const usedSources = /* @__PURE__ */ new Set();
|
|
76
|
+
usedIds.forEach((id) => {
|
|
77
|
+
if (importMap.has(id)) usedSources.add(importMap.get(id));
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
imports,
|
|
81
|
+
usedSources
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
const buildExternalDeps = (imports, usedSources) => {
|
|
85
|
+
const externals = /* @__PURE__ */ new Map();
|
|
86
|
+
imports.forEach((imp) => {
|
|
87
|
+
if (imp.importKind === "type") return;
|
|
88
|
+
const source = imp.source.value;
|
|
89
|
+
if (!usedSources.has(source) || source.startsWith(".") || source.startsWith("/")) return;
|
|
90
|
+
const namedImports = imp.specifiers.filter((spec) => spec.type === "ImportSpecifier" && spec.importKind !== "type").map((spec) => spec.imported.name);
|
|
91
|
+
const existing = externals.get(source);
|
|
92
|
+
if (existing) namedImports.forEach((name) => existing.imported.add(name));
|
|
93
|
+
else externals.set(source, {
|
|
94
|
+
name: source,
|
|
95
|
+
imported: new Set(namedImports)
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
return [...externals.values()].map((dep) => ({
|
|
99
|
+
...dep,
|
|
100
|
+
imported: [...dep.imported]
|
|
101
|
+
}));
|
|
102
|
+
};
|
|
103
|
+
var ESLoader = class {
|
|
104
|
+
name = "es-loader";
|
|
105
|
+
test = /\.(tsx?|jsx?)$/;
|
|
106
|
+
jsxPreset;
|
|
107
|
+
isTSX;
|
|
108
|
+
constructor(options) {
|
|
109
|
+
const { jsxPreset, isTSX = false } = options || {};
|
|
110
|
+
this.jsxPreset = jsxPreset;
|
|
111
|
+
this.isTSX = isTSX;
|
|
112
|
+
}
|
|
113
|
+
transform(source, ctx) {
|
|
114
|
+
const { imports, usedSources } = analyzeImports(parseCode(source));
|
|
115
|
+
const externals = buildExternalDeps(imports, usedSources);
|
|
116
|
+
const dependencies = {};
|
|
117
|
+
for (const imp of imports) {
|
|
118
|
+
if (imp.importKind === "type") continue;
|
|
119
|
+
const importPath = imp.source.value;
|
|
120
|
+
if (imp.specifiers.length === 0) {
|
|
121
|
+
const resolved$1 = ctx.resolve(importPath);
|
|
122
|
+
if (resolved$1) dependencies[importPath] = resolved$1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!usedSources.has(importPath)) continue;
|
|
126
|
+
const resolved = ctx.resolve(importPath);
|
|
127
|
+
if (resolved) dependencies[importPath] = resolved;
|
|
128
|
+
}
|
|
129
|
+
const { typescript } = availablePresets;
|
|
130
|
+
const defaultPresets = [typescript, {
|
|
131
|
+
isTSX: this.isTSX,
|
|
132
|
+
allExtensions: true
|
|
133
|
+
}];
|
|
134
|
+
const { code } = transform(source, {
|
|
135
|
+
filename: ctx.resourcePath,
|
|
136
|
+
presets: this.jsxPreset ? [this.jsxPreset, defaultPresets] : [defaultPresets]
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
type: LoaderType.ESModule,
|
|
140
|
+
content: code || "",
|
|
141
|
+
dependencies,
|
|
142
|
+
externals
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/html/analyze.ts
|
|
149
|
+
const LOADERS = {
|
|
150
|
+
es: new ESLoader(),
|
|
151
|
+
css: new CSSLoader()
|
|
152
|
+
};
|
|
153
|
+
function isExternalUrl(url) {
|
|
154
|
+
return url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//");
|
|
155
|
+
}
|
|
156
|
+
function getTextContent(element) {
|
|
157
|
+
return element.children.filter((child) => child.type === "text").map((child) => child.data).join("").trim();
|
|
158
|
+
}
|
|
159
|
+
function extractAttributes(attribs, excludeKeys) {
|
|
160
|
+
const filtered = Object.fromEntries(Object.entries(attribs).filter(([key]) => !excludeKeys.includes(key)));
|
|
161
|
+
return Object.keys(filtered).length > 0 ? filtered : void 0;
|
|
162
|
+
}
|
|
163
|
+
function createLoaderContext(files) {
|
|
164
|
+
return {
|
|
165
|
+
getSource: (path) => files[path],
|
|
166
|
+
resolve: (src) => files[src] !== void 0 ? src : null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function parseHTML(html$1) {
|
|
170
|
+
const doc = parseDocument(html$1);
|
|
171
|
+
const elements = [];
|
|
172
|
+
const scripts = DomUtils.getElementsByTagName("script", doc);
|
|
173
|
+
for (const script of scripts) {
|
|
174
|
+
const src = script.attribs.src;
|
|
175
|
+
elements.push({
|
|
176
|
+
type: "script",
|
|
177
|
+
isModule: script.attribs.type === "module",
|
|
178
|
+
src,
|
|
179
|
+
content: src ? void 0 : getTextContent(script),
|
|
180
|
+
attributes: extractAttributes(script.attribs, ["src", "type"])
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const styles = DomUtils.getElementsByTagName("style", doc);
|
|
184
|
+
for (const style of styles) elements.push({
|
|
185
|
+
type: "style",
|
|
186
|
+
content: getTextContent(style),
|
|
187
|
+
attributes: extractAttributes(style.attribs, [])
|
|
188
|
+
});
|
|
189
|
+
const links = DomUtils.getElementsByTagName("link", doc);
|
|
190
|
+
for (const link of links) {
|
|
191
|
+
if (link.attribs.rel !== "stylesheet" || !link.attribs.href) continue;
|
|
192
|
+
elements.push({
|
|
193
|
+
type: "link",
|
|
194
|
+
href: link.attribs.href,
|
|
195
|
+
attributes: extractAttributes(link.attribs, ["href", "rel"])
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const bodies = DomUtils.getElementsByTagName("body", doc);
|
|
199
|
+
const container = bodies.length > 0 ? bodies[0] : doc;
|
|
200
|
+
const tagsToRemove = [
|
|
201
|
+
...DomUtils.getElementsByTagName("script", container),
|
|
202
|
+
...DomUtils.getElementsByTagName("style", container),
|
|
203
|
+
...DomUtils.getElementsByTagName("link", container)
|
|
204
|
+
];
|
|
205
|
+
for (const tag of tagsToRemove) DomUtils.removeElement(tag);
|
|
206
|
+
return {
|
|
207
|
+
elements,
|
|
208
|
+
bodyContent: render(DomUtils.getChildren(container)).trim()
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function processESModule(path, files, outputs, visited = /* @__PURE__ */ new Set()) {
|
|
212
|
+
if (visited.has(path)) return;
|
|
213
|
+
visited.add(path);
|
|
214
|
+
const source = files[path];
|
|
215
|
+
if (source === void 0) return;
|
|
216
|
+
const ctx = createLoaderContext(files);
|
|
217
|
+
const output = LOADERS.es.transform(source, {
|
|
218
|
+
resourcePath: path,
|
|
219
|
+
...ctx
|
|
220
|
+
});
|
|
221
|
+
getOutputArray(outputs, LoaderType.ESModule).push({
|
|
222
|
+
path,
|
|
223
|
+
content: output.content,
|
|
224
|
+
dependencies: output.dependencies,
|
|
225
|
+
externals: output.externals
|
|
226
|
+
});
|
|
227
|
+
for (const depPath of Object.values(output.dependencies)) processESModule(depPath, files, outputs, visited);
|
|
228
|
+
}
|
|
229
|
+
function processStylesheet(path, files, outputs, visited = /* @__PURE__ */ new Set()) {
|
|
230
|
+
if (visited.has(path)) return;
|
|
231
|
+
visited.add(path);
|
|
232
|
+
const source = files[path];
|
|
233
|
+
if (source === void 0) return;
|
|
234
|
+
const ctx = createLoaderContext(files);
|
|
235
|
+
const output = LOADERS.css.transform(source, {
|
|
236
|
+
resourcePath: path,
|
|
237
|
+
...ctx
|
|
238
|
+
});
|
|
239
|
+
getOutputArray(outputs, LoaderType.Style).push({
|
|
240
|
+
path,
|
|
241
|
+
content: output.content,
|
|
242
|
+
imports: output.imports,
|
|
243
|
+
attributes: output.attributes
|
|
244
|
+
});
|
|
245
|
+
for (const depPath of output.imports) processStylesheet(depPath, files, outputs, visited);
|
|
246
|
+
}
|
|
247
|
+
function getOutputArray(outputs, type) {
|
|
248
|
+
return outputs.get(type);
|
|
249
|
+
}
|
|
250
|
+
function createOutputs() {
|
|
251
|
+
const outputs = /* @__PURE__ */ new Map();
|
|
252
|
+
outputs.set(LoaderType.ESModule, []);
|
|
253
|
+
outputs.set(LoaderType.Style, []);
|
|
254
|
+
outputs.set(LoaderType.Script, []);
|
|
255
|
+
outputs.set(LoaderType.Asset, []);
|
|
256
|
+
return outputs;
|
|
257
|
+
}
|
|
258
|
+
function processScriptElement(el, entry, files, outputs, visited = /* @__PURE__ */ new Set()) {
|
|
259
|
+
if (el.isModule) processModuleScript(el, entry, files, outputs, visited);
|
|
260
|
+
else processRegularScript(el, entry, files, outputs);
|
|
261
|
+
}
|
|
262
|
+
function processModuleScript(el, entry, files, outputs, visited = /* @__PURE__ */ new Set()) {
|
|
263
|
+
if (el.src) {
|
|
264
|
+
if (isExternalUrl(el.src)) getOutputArray(outputs, LoaderType.Script).push({
|
|
265
|
+
path: el.src,
|
|
266
|
+
content: "",
|
|
267
|
+
src: el.src,
|
|
268
|
+
attributes: {
|
|
269
|
+
...el.attributes,
|
|
270
|
+
type: "module"
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
else if (files[el.src] !== void 0) processESModule(el.src, files, outputs, visited);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!el.content) return;
|
|
277
|
+
const virtualPath = `${entry}#inline-module-${getOutputArray(outputs, LoaderType.ESModule).length}`;
|
|
278
|
+
const ctx = createLoaderContext(files);
|
|
279
|
+
const output = LOADERS.es.transform(el.content, {
|
|
280
|
+
resourcePath: virtualPath,
|
|
281
|
+
...ctx
|
|
282
|
+
});
|
|
283
|
+
getOutputArray(outputs, LoaderType.ESModule).push({
|
|
284
|
+
path: virtualPath,
|
|
285
|
+
content: output.content,
|
|
286
|
+
dependencies: output.dependencies,
|
|
287
|
+
externals: output.externals
|
|
288
|
+
});
|
|
289
|
+
for (const depPath of Object.values(output.dependencies)) processESModule(depPath, files, outputs, visited);
|
|
290
|
+
}
|
|
291
|
+
function processRegularScript(el, entry, files, outputs) {
|
|
292
|
+
if (el.src) {
|
|
293
|
+
if (isExternalUrl(el.src)) getOutputArray(outputs, LoaderType.Script).push({
|
|
294
|
+
path: el.src,
|
|
295
|
+
content: "",
|
|
296
|
+
src: el.src,
|
|
297
|
+
attributes: el.attributes
|
|
298
|
+
});
|
|
299
|
+
else if (files[el.src] !== void 0) getOutputArray(outputs, LoaderType.Script).push({
|
|
300
|
+
path: el.src,
|
|
301
|
+
content: files[el.src],
|
|
302
|
+
attributes: el.attributes
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!el.content) return;
|
|
307
|
+
const virtualPath = `${entry}#inline-script-${getOutputArray(outputs, LoaderType.Script).length}`;
|
|
308
|
+
getOutputArray(outputs, LoaderType.Script).push({
|
|
309
|
+
path: virtualPath,
|
|
310
|
+
content: el.content,
|
|
311
|
+
attributes: el.attributes
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function processStyleElement(el, entry, outputs) {
|
|
315
|
+
if (!el.content) return;
|
|
316
|
+
const virtualPath = `${entry}#inline-style-${getOutputArray(outputs, LoaderType.Style).length}`;
|
|
317
|
+
getOutputArray(outputs, LoaderType.Style).push({
|
|
318
|
+
path: virtualPath,
|
|
319
|
+
content: el.content,
|
|
320
|
+
imports: [],
|
|
321
|
+
attributes: el.attributes
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function processLinkElement(el, files, outputs, visited = /* @__PURE__ */ new Set()) {
|
|
325
|
+
if (!el.href) return;
|
|
326
|
+
if (isExternalUrl(el.href)) getOutputArray(outputs, LoaderType.Style).push({
|
|
327
|
+
path: el.href,
|
|
328
|
+
content: "",
|
|
329
|
+
imports: [],
|
|
330
|
+
href: el.href,
|
|
331
|
+
attributes: el.attributes
|
|
332
|
+
});
|
|
333
|
+
else if (files[el.href] !== void 0) processStylesheet(el.href, files, outputs, visited);
|
|
334
|
+
}
|
|
335
|
+
function analyze(entry, files) {
|
|
336
|
+
const outputs = createOutputs();
|
|
337
|
+
const html$1 = files[entry];
|
|
338
|
+
if (!html$1) return outputs;
|
|
339
|
+
const { elements, bodyContent } = parseHTML(html$1);
|
|
340
|
+
for (const el of elements) if (el.type === "script") processScriptElement(el, entry, files, outputs);
|
|
341
|
+
else if (el.type === "style") processStyleElement(el, entry, outputs);
|
|
342
|
+
else if (el.type === "link") processLinkElement(el, files, outputs);
|
|
343
|
+
if (bodyContent) getOutputArray(outputs, LoaderType.Asset).push({
|
|
344
|
+
path: entry,
|
|
345
|
+
content: bodyContent
|
|
346
|
+
});
|
|
347
|
+
return outputs;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/html/index.ts
|
|
352
|
+
function serializeAttributes(attrs) {
|
|
353
|
+
if (!attrs || Object.keys(attrs).length === 0) return "";
|
|
354
|
+
return Object.entries(attrs).map(([key, value]) => value === "" ? ` ${key}` : ` ${key}="${value}"`).join("");
|
|
355
|
+
}
|
|
356
|
+
var Framework = class extends Framework$1 {
|
|
357
|
+
name = "html";
|
|
358
|
+
imports = {};
|
|
359
|
+
outputs = /* @__PURE__ */ new Map();
|
|
360
|
+
blobUrlMap = /* @__PURE__ */ new Map();
|
|
361
|
+
constructor(config) {
|
|
362
|
+
super();
|
|
363
|
+
this.config = config;
|
|
364
|
+
}
|
|
365
|
+
analyze(entry, files) {
|
|
366
|
+
const { enabled, htmlEntry = entry } = this.config?.liteMode ?? {};
|
|
367
|
+
if (enabled) this.outputs = analyze(entry, {
|
|
368
|
+
...files,
|
|
369
|
+
[htmlEntry]: this.wrapInLiteModeTemplate(files[htmlEntry] ?? "")
|
|
370
|
+
});
|
|
371
|
+
else this.outputs = analyze(entry, files);
|
|
372
|
+
}
|
|
373
|
+
compile() {
|
|
374
|
+
const builder = this.createBuilder();
|
|
375
|
+
const assets = this.getOutput(LoaderType.Asset);
|
|
376
|
+
const styles = this.getOutput(LoaderType.Style);
|
|
377
|
+
const scripts = this.getOutput(LoaderType.Script);
|
|
378
|
+
const modules = this.getOutput(LoaderType.ESModule);
|
|
379
|
+
let htmlContent = assets.map((a) => a.content).join("");
|
|
380
|
+
for (const style of styles) if (style.href) htmlContent += `<link${serializeAttributes(style.attributes)} rel="stylesheet" href="${style.href}">`;
|
|
381
|
+
else htmlContent += `<style${serializeAttributes(style.attributes)}>${style.content}</style>`;
|
|
382
|
+
for (const script of scripts) if (script.src) htmlContent += `<script${serializeAttributes(script.attributes)} src="${script.src}"><\/script>`;
|
|
383
|
+
else htmlContent += `<script${serializeAttributes(script.attributes)}>${script.content}<\/script>`;
|
|
384
|
+
if (modules.length > 0) {
|
|
385
|
+
const entryCode = this.transformModulesToBlob([...modules].reverse());
|
|
386
|
+
if (entryCode) htmlContent += `<script type="module">${entryCode}<\/script>`;
|
|
387
|
+
}
|
|
388
|
+
this.outputs.set(LoaderType.Style, []);
|
|
389
|
+
this.outputs.set(LoaderType.Script, []);
|
|
390
|
+
return builder.setHTML(JSON.stringify(htmlContent), {
|
|
391
|
+
target: "body",
|
|
392
|
+
activateScripts: scripts.length > 0 || modules.length > 0
|
|
393
|
+
}).done();
|
|
394
|
+
}
|
|
395
|
+
wrapInLiteModeTemplate(htmlFragment) {
|
|
396
|
+
const { scriptEntry = "./index.js", styleEntry = "./index.css" } = this.config?.liteMode ?? {};
|
|
397
|
+
return `<!DOCTYPE html>
|
|
398
|
+
<html>
|
|
399
|
+
<head>
|
|
400
|
+
<link rel="stylesheet" href="${styleEntry}">
|
|
401
|
+
<script type="module" src="${scriptEntry}"><\/script>
|
|
402
|
+
</head>
|
|
403
|
+
<body>
|
|
404
|
+
${htmlFragment}
|
|
405
|
+
</body>
|
|
406
|
+
</html>`;
|
|
407
|
+
}
|
|
408
|
+
transformModulesToBlob(modules) {
|
|
409
|
+
let entryCode = "";
|
|
410
|
+
for (let index = 0; index < modules.length; index++) {
|
|
411
|
+
const mod = modules[index];
|
|
412
|
+
const isEntry = index === modules.length - 1;
|
|
413
|
+
const code = this.transformCodeWithBlobUrls(mod);
|
|
414
|
+
if (isEntry) entryCode = code;
|
|
415
|
+
else {
|
|
416
|
+
const blob = new Blob([code], { type: "application/javascript" });
|
|
417
|
+
this.blobUrlMap.set(mod.path, URL.createObjectURL(blob));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return entryCode;
|
|
421
|
+
}
|
|
422
|
+
transformCodeWithBlobUrls(mod) {
|
|
423
|
+
const s = new MagicString(mod.content);
|
|
424
|
+
const ast = parse(mod.content, {
|
|
425
|
+
sourceType: "module",
|
|
426
|
+
plugins: ["jsx", "typescript"]
|
|
427
|
+
}).program.body;
|
|
428
|
+
for (const node of ast) {
|
|
429
|
+
if (node.type !== "ImportDeclaration") continue;
|
|
430
|
+
const resolved = mod.dependencies[node.source.value];
|
|
431
|
+
if (!resolved) continue;
|
|
432
|
+
const blobUrl = this.blobUrlMap.get(resolved);
|
|
433
|
+
if (blobUrl) s.update(node.source.start + 1, node.source.end - 1, blobUrl);
|
|
434
|
+
else s.remove(node.start, node.end);
|
|
435
|
+
}
|
|
436
|
+
return s.toString();
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
const html = new Framework();
|
|
440
|
+
|
|
441
|
+
//#endregion
|
|
442
|
+
export { Framework, html };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import MagicString from "magic-string";
|
|
2
|
+
|
|
3
|
+
//#region src/builder.d.ts
|
|
4
|
+
declare class RuntimeBuilder {
|
|
5
|
+
private s;
|
|
6
|
+
root: string;
|
|
7
|
+
constructor(code?: string | MagicString);
|
|
8
|
+
toString(): string;
|
|
9
|
+
update(start: number, end: number, content: string): this;
|
|
10
|
+
remove(start: number, end: number): this;
|
|
11
|
+
setHTML(content: string, options?: {
|
|
12
|
+
target?: 'root' | 'body';
|
|
13
|
+
activateScripts?: boolean;
|
|
14
|
+
}): this;
|
|
15
|
+
append(code: string): this;
|
|
16
|
+
prepend(code: string): this;
|
|
17
|
+
done(): string;
|
|
18
|
+
async(code: string): this;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/loaders/types.d.ts
|
|
22
|
+
declare enum LoaderType {
|
|
23
|
+
ESModule = "esmodule",
|
|
24
|
+
Style = "style",
|
|
25
|
+
Script = "script",
|
|
26
|
+
Asset = "asset",
|
|
27
|
+
}
|
|
28
|
+
interface BaseLoaderOutput<T extends LoaderType> {
|
|
29
|
+
type: T;
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
interface ESModuleLoaderOutput extends BaseLoaderOutput<LoaderType.ESModule> {
|
|
33
|
+
dependencies: Record<string, string>;
|
|
34
|
+
externals: {
|
|
35
|
+
name: string;
|
|
36
|
+
imported: string[];
|
|
37
|
+
}[];
|
|
38
|
+
}
|
|
39
|
+
interface StyleLoaderOutput extends BaseLoaderOutput<LoaderType.Style> {
|
|
40
|
+
imports: string[];
|
|
41
|
+
href?: string;
|
|
42
|
+
attributes?: Record<string, string>;
|
|
43
|
+
}
|
|
44
|
+
interface ScriptLoaderOutput extends BaseLoaderOutput<LoaderType.Script> {
|
|
45
|
+
src?: string;
|
|
46
|
+
attributes?: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
type AssetLoaderOutput = BaseLoaderOutput<LoaderType.Asset>;
|
|
49
|
+
type LoaderOutput<T extends LoaderType> = Extract<ESModuleLoaderOutput | StyleLoaderOutput | ScriptLoaderOutput | AssetLoaderOutput, {
|
|
50
|
+
type: T;
|
|
51
|
+
}>;
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/registry.d.ts
|
|
54
|
+
type Output<T extends LoaderType = LoaderType> = Omit<LoaderOutput<T>, 'type'> & {
|
|
55
|
+
path: string;
|
|
56
|
+
};
|
|
57
|
+
type Outputs = Map<LoaderType, Output[]>;
|
|
58
|
+
declare abstract class Framework {
|
|
59
|
+
abstract readonly name: string;
|
|
60
|
+
abstract readonly imports: Record<string, string>;
|
|
61
|
+
abstract outputs: Outputs;
|
|
62
|
+
abstract analyze(entry: string, files: Record<string, string>): void;
|
|
63
|
+
abstract compile(): string;
|
|
64
|
+
protected createBuilder(init?: string): RuntimeBuilder;
|
|
65
|
+
getOutput<T extends LoaderType>(type: T): Output<T>[];
|
|
66
|
+
}
|
|
67
|
+
declare class FrameworkRegistry {
|
|
68
|
+
private frameworks;
|
|
69
|
+
register(framework: Framework, name?: string): void;
|
|
70
|
+
get(name: string): Framework | undefined;
|
|
71
|
+
list(): string[];
|
|
72
|
+
}
|
|
73
|
+
declare const registry: FrameworkRegistry;
|
|
74
|
+
declare function registerFramework(framework: Framework, name?: string): void;
|
|
75
|
+
//#endregion
|
|
76
|
+
export { Framework, type Output, type Outputs, registerFramework, registry };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import MagicString from "magic-string";
|
|
2
|
+
|
|
3
|
+
//#region src/builder.ts
|
|
4
|
+
var RuntimeBuilder = class {
|
|
5
|
+
s;
|
|
6
|
+
root = `document.getElementById('root')`;
|
|
7
|
+
constructor(code = "") {
|
|
8
|
+
this.s = typeof code === "string" ? new MagicString(code) : code;
|
|
9
|
+
}
|
|
10
|
+
toString() {
|
|
11
|
+
return this.s.toString();
|
|
12
|
+
}
|
|
13
|
+
update(start, end, content) {
|
|
14
|
+
this.s.update(start, end, content);
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
remove(start, end) {
|
|
18
|
+
this.s.remove(start, end);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
setHTML(content, options) {
|
|
22
|
+
const container = options?.target === "body" ? "document.body" : `document.getElementById('root')`;
|
|
23
|
+
this.s.append(`${container}.innerHTML = ${content};`);
|
|
24
|
+
if (options?.activateScripts) this.s.append(`
|
|
25
|
+
${container}.querySelectorAll('script').forEach(old => {
|
|
26
|
+
const el = document.createElement('script');
|
|
27
|
+
[...old.attributes].forEach(a => el.setAttribute(a.name, a.value));
|
|
28
|
+
el.textContent = old.textContent;
|
|
29
|
+
old.replaceWith(el);
|
|
30
|
+
});`);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
append(code) {
|
|
34
|
+
this.s.append(code);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
prepend(code) {
|
|
38
|
+
this.s.prepend(code);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
done() {
|
|
42
|
+
this.s.append("window.__render_complete__?.();\nwindow.__next__?.();");
|
|
43
|
+
return this.s.toString();
|
|
44
|
+
}
|
|
45
|
+
async(code) {
|
|
46
|
+
this.s.append(`
|
|
47
|
+
;(async () => {
|
|
48
|
+
try {
|
|
49
|
+
${code}
|
|
50
|
+
window.__render_complete__?.();
|
|
51
|
+
} finally {
|
|
52
|
+
window.__next__?.();
|
|
53
|
+
}
|
|
54
|
+
})();`);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/registry.ts
|
|
61
|
+
var Framework = class {
|
|
62
|
+
createBuilder(init) {
|
|
63
|
+
return new RuntimeBuilder(init);
|
|
64
|
+
}
|
|
65
|
+
getOutput(type) {
|
|
66
|
+
return this.outputs.get(type) ?? [];
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var FrameworkRegistry = class {
|
|
70
|
+
frameworks = /* @__PURE__ */ new Map();
|
|
71
|
+
register(framework, name) {
|
|
72
|
+
this.frameworks.set(name ?? framework.name, framework);
|
|
73
|
+
}
|
|
74
|
+
get(name) {
|
|
75
|
+
return this.frameworks.get(name);
|
|
76
|
+
}
|
|
77
|
+
list() {
|
|
78
|
+
return Array.from(this.frameworks.keys());
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const registry = new FrameworkRegistry();
|
|
82
|
+
function registerFramework(framework, name) {
|
|
83
|
+
registry.register(framework, name);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
export { Framework, registerFramework, registry };
|