solid-tag-runtime 0.0.11 → 0.0.13
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/ARCHITECTURE.md +167 -38
- package/README.md +76 -885
- package/docs/README.md +23 -0
- package/docs/api/compile-cache.md +103 -0
- package/docs/api/html.md +83 -0
- package/docs/api/runtime.md +87 -0
- package/docs/compile-cache.md +271 -0
- package/docs/getting-started.md +105 -0
- package/docs/html-runtime.md +112 -0
- package/docs/lifecycle-events.md +77 -0
- package/docs/modules.md +93 -0
- package/docs/rendering.md +65 -0
- package/docs/solid-render.md +111 -0
- package/examples/basic.js +41 -0
- package/examples/compile-cache.js +35 -0
- package/examples/main.tsx +318 -0
- package/examples/render.html +79 -0
- package/index.d.ts +183 -27
- package/package.json +5 -3
- package/src/compile-cache.js +340 -0
- package/src/compiler.js +86 -38
- package/src/html.js +191 -19
- package/src/index.js +6 -0
- package/src/runtime.js +656 -74
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-tag-runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Runtime module system for JSX modules compiled with solid-tag and executed through @solidjs/html",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -33,11 +33,13 @@
|
|
|
33
33
|
"index.d.ts",
|
|
34
34
|
"html.d.ts",
|
|
35
35
|
"README.md",
|
|
36
|
-
"ARCHITECTURE.md"
|
|
36
|
+
"ARCHITECTURE.md",
|
|
37
|
+
"docs",
|
|
38
|
+
"examples"
|
|
37
39
|
],
|
|
38
40
|
"sideEffects": false,
|
|
39
41
|
"dependencies": {
|
|
40
|
-
"solid-tag": "
|
|
42
|
+
"solid-tag": "0.0.1"
|
|
41
43
|
},
|
|
42
44
|
"scripts": {
|
|
43
45
|
"test": "node ./test/run.mjs",
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
const DEFAULT_LOCAL_STORAGE_PREFIX = "solid-tag-runtime:compile-cache:";
|
|
2
|
+
const DEFAULT_IDB_DATABASE = "solid-tag-runtime-compile-cache";
|
|
3
|
+
const DEFAULT_IDB_STORE = "artifacts";
|
|
4
|
+
|
|
5
|
+
export function createMemoryCompileCache(options = {}) {
|
|
6
|
+
const records = options.records instanceof Map ? options.records : new Map();
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
kind: "memory",
|
|
10
|
+
async get(key) {
|
|
11
|
+
return records.get(String(key));
|
|
12
|
+
},
|
|
13
|
+
async set(key, value) {
|
|
14
|
+
records.set(String(key), cloneValue(value));
|
|
15
|
+
},
|
|
16
|
+
async delete(key) {
|
|
17
|
+
return records.delete(String(key));
|
|
18
|
+
},
|
|
19
|
+
async clear(options = {}) {
|
|
20
|
+
const prefix = options.prefix == null ? null : String(options.prefix);
|
|
21
|
+
if (prefix == null) {
|
|
22
|
+
const count = records.size;
|
|
23
|
+
records.clear();
|
|
24
|
+
return count;
|
|
25
|
+
}
|
|
26
|
+
let count = 0;
|
|
27
|
+
for (const key of [...records.keys()]) {
|
|
28
|
+
if (key.startsWith(prefix)) {
|
|
29
|
+
records.delete(key);
|
|
30
|
+
count += 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return count;
|
|
34
|
+
},
|
|
35
|
+
async entries(options = {}) {
|
|
36
|
+
const prefix = options.prefix == null ? null : String(options.prefix);
|
|
37
|
+
const result = [];
|
|
38
|
+
for (const [key, value] of records) {
|
|
39
|
+
if (prefix == null || key.startsWith(prefix)) result.push([key, cloneValue(value)]);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createLocalStorageCompileCache(options = {}) {
|
|
47
|
+
const prefix = String(options.prefix ?? DEFAULT_LOCAL_STORAGE_PREFIX);
|
|
48
|
+
const configuredStorage = options.storage;
|
|
49
|
+
|
|
50
|
+
function assertStorage() {
|
|
51
|
+
let storage = configuredStorage;
|
|
52
|
+
if (!storage) {
|
|
53
|
+
try { storage = globalThis.localStorage; } catch (error) { throw error; }
|
|
54
|
+
}
|
|
55
|
+
if (!storage || typeof storage.getItem !== "function") {
|
|
56
|
+
throw new Error("localStorage is unavailable in this environment.");
|
|
57
|
+
}
|
|
58
|
+
return storage;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const physical = key => `${prefix}${String(key)}`;
|
|
62
|
+
const logical = key => String(key).slice(prefix.length);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
kind: "localstorage",
|
|
66
|
+
async get(key) {
|
|
67
|
+
const raw = assertStorage().getItem(physical(key));
|
|
68
|
+
return raw == null ? undefined : JSON.parse(raw);
|
|
69
|
+
},
|
|
70
|
+
async set(key, value) {
|
|
71
|
+
assertStorage().setItem(physical(key), JSON.stringify(value));
|
|
72
|
+
},
|
|
73
|
+
async delete(key) {
|
|
74
|
+
const target = physical(key);
|
|
75
|
+
const store = assertStorage();
|
|
76
|
+
const existed = store.getItem(target) != null;
|
|
77
|
+
store.removeItem(target);
|
|
78
|
+
return existed;
|
|
79
|
+
},
|
|
80
|
+
async clear(options = {}) {
|
|
81
|
+
const store = assertStorage();
|
|
82
|
+
const logicalPrefix = options.prefix == null ? "" : String(options.prefix);
|
|
83
|
+
const targetPrefix = physical(logicalPrefix);
|
|
84
|
+
const keys = [];
|
|
85
|
+
for (let index = 0; index < store.length; index += 1) {
|
|
86
|
+
const key = store.key(index);
|
|
87
|
+
if (key != null && key.startsWith(targetPrefix)) keys.push(key);
|
|
88
|
+
}
|
|
89
|
+
for (const key of keys) store.removeItem(key);
|
|
90
|
+
return keys.length;
|
|
91
|
+
},
|
|
92
|
+
async entries(options = {}) {
|
|
93
|
+
const store = assertStorage();
|
|
94
|
+
const logicalPrefix = options.prefix == null ? "" : String(options.prefix);
|
|
95
|
+
const targetPrefix = physical(logicalPrefix);
|
|
96
|
+
const result = [];
|
|
97
|
+
for (let index = 0; index < store.length; index += 1) {
|
|
98
|
+
const key = store.key(index);
|
|
99
|
+
if (key == null || !key.startsWith(targetPrefix)) continue;
|
|
100
|
+
const raw = store.getItem(key);
|
|
101
|
+
if (raw == null) continue;
|
|
102
|
+
result.push([logical(key), JSON.parse(raw)]);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createIndexedDBCompileCache(options = {}) {
|
|
110
|
+
const database = String(options.database ?? DEFAULT_IDB_DATABASE);
|
|
111
|
+
const storeName = String(options.storeName ?? DEFAULT_IDB_STORE);
|
|
112
|
+
const databaseVersion = Number(options.databaseVersion ?? 1);
|
|
113
|
+
const indexedDBFactory = options.indexedDB ?? globalThis.indexedDB;
|
|
114
|
+
let dbPromise;
|
|
115
|
+
|
|
116
|
+
function openDatabase() {
|
|
117
|
+
if (dbPromise) return dbPromise;
|
|
118
|
+
if (!indexedDBFactory || typeof indexedDBFactory.open !== "function") {
|
|
119
|
+
return Promise.reject(new Error("IndexedDB is unavailable in this environment."));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
dbPromise = new Promise((resolve, reject) => {
|
|
123
|
+
const request = indexedDBFactory.open(database, databaseVersion);
|
|
124
|
+
request.onupgradeneeded = () => {
|
|
125
|
+
const db = request.result;
|
|
126
|
+
if (!db.objectStoreNames.contains(storeName)) db.createObjectStore(storeName);
|
|
127
|
+
};
|
|
128
|
+
request.onsuccess = () => resolve(request.result);
|
|
129
|
+
request.onerror = () => {
|
|
130
|
+
dbPromise = undefined;
|
|
131
|
+
reject(request.error ?? new Error(`Failed to open IndexedDB database ${database}.`));
|
|
132
|
+
};
|
|
133
|
+
request.onblocked = () => {
|
|
134
|
+
dbPromise = undefined;
|
|
135
|
+
reject(new Error(`Opening IndexedDB database ${database} was blocked.`));
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return dbPromise;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
kind: "indexeddb",
|
|
146
|
+
async get(key) {
|
|
147
|
+
const db = await openDatabase();
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
const tx = db.transaction(storeName, "readonly");
|
|
150
|
+
const request = tx.objectStore(storeName).get(String(key));
|
|
151
|
+
request.onsuccess = () => resolve(request.result);
|
|
152
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB cache read failed."));
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
async set(key, value) {
|
|
156
|
+
const db = await openDatabase();
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
159
|
+
tx.objectStore(storeName).put(value, String(key));
|
|
160
|
+
tx.oncomplete = () => resolve();
|
|
161
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB cache write failed."));
|
|
162
|
+
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB cache write was aborted."));
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
async delete(key) {
|
|
166
|
+
const db = await openDatabase();
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
169
|
+
const store = tx.objectStore(storeName);
|
|
170
|
+
const lookup = store.getKey(String(key));
|
|
171
|
+
let existed = false;
|
|
172
|
+
lookup.onsuccess = () => {
|
|
173
|
+
existed = lookup.result !== undefined;
|
|
174
|
+
store.delete(String(key));
|
|
175
|
+
};
|
|
176
|
+
lookup.onerror = () => reject(lookup.error ?? new Error("IndexedDB cache delete lookup failed."));
|
|
177
|
+
tx.oncomplete = () => resolve(existed);
|
|
178
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB cache delete failed."));
|
|
179
|
+
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB cache delete was aborted."));
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
async clear(options = {}) {
|
|
183
|
+
const prefix = options.prefix == null ? null : String(options.prefix);
|
|
184
|
+
const db = await openDatabase();
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
const tx = db.transaction(storeName, "readwrite");
|
|
187
|
+
const store = tx.objectStore(storeName);
|
|
188
|
+
if (prefix == null || prefix === "") {
|
|
189
|
+
const countRequest = store.count();
|
|
190
|
+
let count = 0;
|
|
191
|
+
countRequest.onsuccess = () => { count = countRequest.result; };
|
|
192
|
+
store.clear();
|
|
193
|
+
tx.oncomplete = () => resolve(count);
|
|
194
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB cache clear failed."));
|
|
195
|
+
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB cache clear was aborted."));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let count = 0;
|
|
200
|
+
const request = store.openCursor();
|
|
201
|
+
request.onsuccess = () => {
|
|
202
|
+
const cursor = request.result;
|
|
203
|
+
if (!cursor) return;
|
|
204
|
+
if (String(cursor.key).startsWith(prefix)) {
|
|
205
|
+
cursor.delete();
|
|
206
|
+
count += 1;
|
|
207
|
+
}
|
|
208
|
+
cursor.continue();
|
|
209
|
+
};
|
|
210
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB cache clear cursor failed."));
|
|
211
|
+
tx.oncomplete = () => resolve(count);
|
|
212
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB cache clear failed."));
|
|
213
|
+
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB cache clear was aborted."));
|
|
214
|
+
});
|
|
215
|
+
},
|
|
216
|
+
async entries(options = {}) {
|
|
217
|
+
const prefix = options.prefix == null ? null : String(options.prefix);
|
|
218
|
+
const db = await openDatabase();
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
const tx = db.transaction(storeName, "readonly");
|
|
221
|
+
const request = tx.objectStore(storeName).openCursor();
|
|
222
|
+
const result = [];
|
|
223
|
+
request.onsuccess = () => {
|
|
224
|
+
const cursor = request.result;
|
|
225
|
+
if (!cursor) return;
|
|
226
|
+
const key = String(cursor.key);
|
|
227
|
+
if (prefix == null || key.startsWith(prefix)) result.push([key, cursor.value]);
|
|
228
|
+
cursor.continue();
|
|
229
|
+
};
|
|
230
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB cache entries cursor failed."));
|
|
231
|
+
tx.oncomplete = () => resolve(result);
|
|
232
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB cache entries transaction failed."));
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function sha256(value) {
|
|
239
|
+
const bytes = new TextEncoder().encode(String(value));
|
|
240
|
+
const subtle = globalThis.crypto?.subtle;
|
|
241
|
+
if (subtle) {
|
|
242
|
+
const digest = await subtle.digest("SHA-256", bytes);
|
|
243
|
+
return toHex(new Uint8Array(digest));
|
|
244
|
+
}
|
|
245
|
+
return sha256Fallback(bytes);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function stableSerialize(value) {
|
|
249
|
+
return JSON.stringify(normalize(value));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function approximateByteSize(value) {
|
|
253
|
+
try {
|
|
254
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
255
|
+
} catch {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function normalize(value) {
|
|
261
|
+
if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
262
|
+
if (Array.isArray(value)) return value.map(normalize);
|
|
263
|
+
if (typeof value === "object") {
|
|
264
|
+
const result = {};
|
|
265
|
+
for (const key of Object.keys(value).sort()) {
|
|
266
|
+
const item = value[key];
|
|
267
|
+
if (item === undefined) continue;
|
|
268
|
+
result[key] = normalize(item);
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
return String(value);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function toHex(bytes) {
|
|
276
|
+
return [...bytes].map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function sha256Fallback(bytes) {
|
|
280
|
+
const K = [
|
|
281
|
+
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
|
282
|
+
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
|
283
|
+
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
|
284
|
+
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
|
285
|
+
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
|
286
|
+
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
|
287
|
+
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
|
288
|
+
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2,
|
|
289
|
+
];
|
|
290
|
+
const H = [0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19];
|
|
291
|
+
const bitLength = bytes.length * 8;
|
|
292
|
+
const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;
|
|
293
|
+
const data = new Uint8Array(paddedLength);
|
|
294
|
+
data.set(bytes);
|
|
295
|
+
data[bytes.length] = 0x80;
|
|
296
|
+
const view = new DataView(data.buffer);
|
|
297
|
+
const high = Math.floor(bitLength / 0x100000000);
|
|
298
|
+
const low = bitLength >>> 0;
|
|
299
|
+
view.setUint32(paddedLength - 8, high, false);
|
|
300
|
+
view.setUint32(paddedLength - 4, low, false);
|
|
301
|
+
const w = new Uint32Array(64);
|
|
302
|
+
|
|
303
|
+
for (let offset = 0; offset < paddedLength; offset += 64) {
|
|
304
|
+
for (let i = 0; i < 16; i += 1) w[i] = view.getUint32(offset + i * 4, false);
|
|
305
|
+
for (let i = 16; i < 64; i += 1) {
|
|
306
|
+
const a = w[i - 15];
|
|
307
|
+
const b = w[i - 2];
|
|
308
|
+
const s0 = rotr(a, 7) ^ rotr(a, 18) ^ (a >>> 3);
|
|
309
|
+
const s1 = rotr(b, 17) ^ rotr(b, 19) ^ (b >>> 10);
|
|
310
|
+
w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
|
|
311
|
+
}
|
|
312
|
+
let [a,b,c,d,e,f,g,h] = H;
|
|
313
|
+
for (let i = 0; i < 64; i += 1) {
|
|
314
|
+
const S1 = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25);
|
|
315
|
+
const ch = (e & f) ^ (~e & g);
|
|
316
|
+
const t1 = (h + S1 + ch + K[i] + w[i]) >>> 0;
|
|
317
|
+
const S0 = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22);
|
|
318
|
+
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
319
|
+
const t2 = (S0 + maj) >>> 0;
|
|
320
|
+
h=g; g=f; f=e; e=(d+t1)>>>0; d=c; c=b; b=a; a=(t1+t2)>>>0;
|
|
321
|
+
}
|
|
322
|
+
H[0]=(H[0]+a)>>>0; H[1]=(H[1]+b)>>>0; H[2]=(H[2]+c)>>>0; H[3]=(H[3]+d)>>>0;
|
|
323
|
+
H[4]=(H[4]+e)>>>0; H[5]=(H[5]+f)>>>0; H[6]=(H[6]+g)>>>0; H[7]=(H[7]+h)>>>0;
|
|
324
|
+
}
|
|
325
|
+
const output = new Uint8Array(32);
|
|
326
|
+
const out = new DataView(output.buffer);
|
|
327
|
+
H.forEach((value, index) => out.setUint32(index * 4, value, false));
|
|
328
|
+
return toHex(output);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function rotr(value, bits) {
|
|
332
|
+
return (value >>> bits) | (value << (32 - bits));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function cloneValue(value) {
|
|
336
|
+
if (typeof structuredClone === "function") {
|
|
337
|
+
try { return structuredClone(value); } catch {}
|
|
338
|
+
}
|
|
339
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
340
|
+
}
|
package/src/compiler.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { parseJSX, transformModule } from "solid-tag";
|
|
2
|
+
import { stableSerialize } from "./compile-cache.js";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_COMPILER_ABI = "solid-tag-runtime:solid-tag@0.0.1:compiler:v1";
|
|
2
5
|
|
|
3
6
|
export function createSolidTagCompiler(options = {}) {
|
|
4
7
|
const {
|
|
@@ -6,45 +9,11 @@ export function createSolidTagCompiler(options = {}) {
|
|
|
6
9
|
transformOptions = {},
|
|
7
10
|
} = options;
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const ast = parseJSX(source);
|
|
12
|
-
const imports = [];
|
|
13
|
-
let needsHtmlRuntime = false;
|
|
14
|
-
walk(ast, node => {
|
|
15
|
-
if (node.type === "JSXElement" || node.type === "JSXFragment") needsHtmlRuntime = true;
|
|
16
|
-
if (
|
|
17
|
-
(node.type === "ImportDeclaration" ||
|
|
18
|
-
node.type === "ExportNamedDeclaration" ||
|
|
19
|
-
node.type === "ExportAllDeclaration") &&
|
|
20
|
-
node.source &&
|
|
21
|
-
typeof node.source.value === "string"
|
|
22
|
-
) {
|
|
23
|
-
imports.push({
|
|
24
|
-
specifier: node.source.value,
|
|
25
|
-
start: node.source.start,
|
|
26
|
-
end: node.source.end,
|
|
27
|
-
kind: node.type,
|
|
28
|
-
});
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
node.type === "ImportExpression" &&
|
|
34
|
-
node.source &&
|
|
35
|
-
(node.source.type === "Literal" || node.source.type === "StringLiteral") &&
|
|
36
|
-
typeof node.source.value === "string"
|
|
37
|
-
) {
|
|
38
|
-
imports.push({
|
|
39
|
-
specifier: node.source.value,
|
|
40
|
-
start: node.source.start,
|
|
41
|
-
end: node.source.end,
|
|
42
|
-
kind: "ImportExpression",
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
});
|
|
12
|
+
const compiler = {
|
|
13
|
+
fingerprint: `${DEFAULT_COMPILER_ABI}:${stableSerialize({ tag, transformOptions })}`,
|
|
46
14
|
|
|
47
|
-
|
|
15
|
+
analyze(source) {
|
|
16
|
+
return analyzeSource(source);
|
|
48
17
|
},
|
|
49
18
|
|
|
50
19
|
transform(source, context = {}) {
|
|
@@ -52,10 +21,89 @@ export function createSolidTagCompiler(options = {}) {
|
|
|
52
21
|
...transformOptions,
|
|
53
22
|
tag,
|
|
54
23
|
injectHtmlImport: true,
|
|
24
|
+
// Keep this unresolved in the persistent artifact. Runtime-specific
|
|
25
|
+
// host/native resolution happens later in the linker.
|
|
55
26
|
htmlImportSource: context.htmlImportSource ?? "@solidjs/html",
|
|
56
27
|
});
|
|
57
28
|
},
|
|
29
|
+
|
|
30
|
+
compile(source, context = {}) {
|
|
31
|
+
let code;
|
|
32
|
+
let diagnostics = [];
|
|
33
|
+
let changed = false;
|
|
34
|
+
let sourceMap;
|
|
35
|
+
|
|
36
|
+
if (context.format === "js") {
|
|
37
|
+
code = source;
|
|
38
|
+
} else {
|
|
39
|
+
const transformed = compiler.transform(source, {
|
|
40
|
+
...context,
|
|
41
|
+
htmlImportSource: "@solidjs/html",
|
|
42
|
+
});
|
|
43
|
+
code = transformed.code;
|
|
44
|
+
diagnostics = [...(transformed.diagnostics ?? [])];
|
|
45
|
+
changed = Boolean(transformed.changed);
|
|
46
|
+
sourceMap = transformed.sourceMap;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const analysis = analyzeSource(code);
|
|
50
|
+
return {
|
|
51
|
+
schema: 1,
|
|
52
|
+
code,
|
|
53
|
+
imports: analysis.imports,
|
|
54
|
+
diagnostics,
|
|
55
|
+
changed,
|
|
56
|
+
...(sourceMap == null ? {} : { sourceMap }),
|
|
57
|
+
};
|
|
58
|
+
},
|
|
58
59
|
};
|
|
60
|
+
|
|
61
|
+
return compiler;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function analyzeSource(source) {
|
|
65
|
+
const ast = parseJSX(source);
|
|
66
|
+
const imports = [];
|
|
67
|
+
let needsHtmlRuntime = false;
|
|
68
|
+
walk(ast, node => {
|
|
69
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") needsHtmlRuntime = true;
|
|
70
|
+
if (
|
|
71
|
+
(node.type === "ImportDeclaration" ||
|
|
72
|
+
node.type === "ExportNamedDeclaration" ||
|
|
73
|
+
node.type === "ExportAllDeclaration") &&
|
|
74
|
+
node.source &&
|
|
75
|
+
typeof node.source.value === "string"
|
|
76
|
+
) {
|
|
77
|
+
imports.push({
|
|
78
|
+
specifier: node.source.value,
|
|
79
|
+
start: node.source.start,
|
|
80
|
+
end: node.source.end,
|
|
81
|
+
kind: normalizeImportKind(node.type),
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
node.type === "ImportExpression" &&
|
|
88
|
+
node.source &&
|
|
89
|
+
(node.source.type === "Literal" || node.source.type === "StringLiteral") &&
|
|
90
|
+
typeof node.source.value === "string"
|
|
91
|
+
) {
|
|
92
|
+
imports.push({
|
|
93
|
+
specifier: node.source.value,
|
|
94
|
+
start: node.source.start,
|
|
95
|
+
end: node.source.end,
|
|
96
|
+
kind: "dynamic",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return { imports, needsHtmlRuntime };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeImportKind(type) {
|
|
105
|
+
if (type === "ExportNamedDeclaration" || type === "ExportAllDeclaration") return "export";
|
|
106
|
+
return "static";
|
|
59
107
|
}
|
|
60
108
|
|
|
61
109
|
function walk(node, visit) {
|