@markless/bundler 0.9.0 → 0.10.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.
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,59 @@
1
+ import { a as writeCompileStoreEntry, t as compileStoreEntry } from "./compile-store-DxF9Vj4_.js";
2
+ import { serialize } from "node:v8";
3
+ import { parentPort } from "node:worker_threads";
4
+ import { clearCompileCache, compileTsrxModule } from "@markless/compiler";
5
+ //#region src/build-worker.ts
6
+ let chunkTasks;
7
+ let chunkAst;
8
+ parentPort.on("message", async (job) => {
9
+ if (job.kind === "chunk") await runChunkJob(job);
10
+ else await runCompileJob(job);
11
+ });
12
+ async function runChunkJob({ id, task, fileName, code, context }) {
13
+ chunkTasks ??= import("./chunk-tasks-BexSGNjx.js");
14
+ chunkAst ??= import("./chunk-ast-C8zKZiam.js");
15
+ const [{ runChunkTaskInline }, { clearParsedChunkCode }] = await Promise.all([chunkTasks, chunkAst]);
16
+ try {
17
+ parentPort.postMessage({
18
+ id,
19
+ result: runChunkTaskInline(task, fileName, code, context)
20
+ });
21
+ } catch (error) {
22
+ parentPort.postMessage({
23
+ id,
24
+ error: error instanceof Error ? error.stack ?? error.message : String(error)
25
+ });
26
+ } finally {
27
+ clearParsedChunkCode();
28
+ }
29
+ }
30
+ async function runCompileJob({ id, input, key, store }) {
31
+ let bytes;
32
+ try {
33
+ bytes = serialize(await compileTsrxModule(input));
34
+ } catch {
35
+ parentPort.postMessage({
36
+ id,
37
+ failed: true
38
+ });
39
+ return;
40
+ } finally {
41
+ clearCompileCache();
42
+ }
43
+ const storePath = store ? compileStoreEntry(store.directory, store.fingerprint, input.source, key).path : void 0;
44
+ const copy = storePath === void 0 ? void 0 : new Uint8Array(bytes);
45
+ parentPort.postMessage({
46
+ id,
47
+ buffer: bytes.buffer,
48
+ offset: bytes.byteOffset,
49
+ length: bytes.byteLength
50
+ }, [bytes.buffer]);
51
+ if (copy && storePath) {
52
+ await writeCompileStoreEntry(storePath, copy);
53
+ parentPort.postMessage({
54
+ id,
55
+ written: true
56
+ });
57
+ }
58
+ }
59
+ //#endregion
@@ -0,0 +1 @@
1
+ export { t as PARSED_CHUNK_CACHE_MAX_CODE_LENGTH, n as PARSED_CHUNK_CACHE_MAX_ENTRIES, r as calleeOffsets, i as chunkDynamicImports, a as chunkLocalExportSpecifiers, o as clearChunkFactCaches, s as clearParsedChunkCode, c as knowsChunkDynamicImports, l as mayContainDynamicImport, u as parseChunkCode, d as prefetchChunkParses, f as readChunkDynamicImports, p as readChunkLocalExportSpecifiers, m as rememberChunkDynamicImports, h as rememberChunkLocalExportSpecifiers, g as renderedChunkCount, _ as spansAnyOffset, v as textOffsets } from "./chunk-ast-pN_P5wIo.js";
@@ -0,0 +1,204 @@
1
+ import { parse, parseSync } from "rolldown/experimental";
2
+ //#region src/build/chunk-ast.ts
3
+ const UNNAMED_CHUNK = "chunk.js";
4
+ const parsedByExtension = /* @__PURE__ */ new Map();
5
+ const PARSED_CHUNK_CACHE_MAX_CODE_LENGTH = 16777216;
6
+ const PARSED_CHUNK_CACHE_MAX_ENTRIES = 1024;
7
+ let cachedCodeLength = 0;
8
+ let cachedEntries = 0;
9
+ const codeByFile = /* @__PURE__ */ new Map();
10
+ function parsedFor(fileName) {
11
+ const extension = fileName.slice(fileName.lastIndexOf("."));
12
+ let parsed = parsedByExtension.get(extension);
13
+ if (!parsed) parsedByExtension.set(extension, parsed = /* @__PURE__ */ new Map());
14
+ return parsed;
15
+ }
16
+ const renderedChunkCounts = /* @__PURE__ */ new WeakMap();
17
+ function renderedChunkCount(chunks) {
18
+ let count = renderedChunkCounts.get(chunks);
19
+ if (count === void 0) renderedChunkCounts.set(chunks, count = Object.keys(chunks).length);
20
+ return count;
21
+ }
22
+ function forgetSuperseded(fileName, code) {
23
+ const previous = codeByFile.get(fileName);
24
+ if (previous === void 0 || previous === code) return;
25
+ if (parsedFor(fileName).delete(previous)) {
26
+ cachedCodeLength -= previous.length;
27
+ cachedEntries--;
28
+ }
29
+ codeByFile.delete(fileName);
30
+ }
31
+ function admits(fileName, code) {
32
+ if (fileName !== UNNAMED_CHUNK) forgetSuperseded(fileName, code);
33
+ return cachedEntries < 1024 && cachedCodeLength + code.length <= 16777216;
34
+ }
35
+ function remember(fileName, code, result) {
36
+ const parsed = parsedFor(fileName);
37
+ if (parsed.has(code) || cachedEntries >= 1024) return;
38
+ if (fileName !== UNNAMED_CHUNK) codeByFile.set(fileName, code);
39
+ parsed.set(code, result);
40
+ cachedCodeLength += code.length;
41
+ cachedEntries++;
42
+ }
43
+ function parseChunkCode(fileName, code) {
44
+ const cached = parsedFor(fileName).get(code);
45
+ if (cached) return cached;
46
+ const result = parseSync(fileName, code);
47
+ if (admits(fileName, code)) remember(fileName, code, result);
48
+ return result;
49
+ }
50
+ async function prefetchChunkParses(chunks) {
51
+ const pending = [];
52
+ let reserved = 0;
53
+ let reservedEntries = 0;
54
+ for (const { fileName, code } of chunks) {
55
+ if (parsedFor(fileName).has(code)) continue;
56
+ if (!admits(fileName, code) || cachedEntries + reservedEntries >= 1024 || cachedCodeLength + reserved + code.length > 16777216) continue;
57
+ reserved += code.length;
58
+ reservedEntries++;
59
+ pending.push(parse(fileName, code).then((result) => remember(fileName, code, result), () => {}));
60
+ }
61
+ await Promise.all(pending);
62
+ }
63
+ function clearParsedChunkCode() {
64
+ parsedByExtension.clear();
65
+ clearChunkFactCaches();
66
+ codeByFile.clear();
67
+ cachedCodeLength = 0;
68
+ cachedEntries = 0;
69
+ }
70
+ function mayContainDynamicImport(code) {
71
+ for (let index = code.indexOf("import"); index >= 0; index = code.indexOf("import", index + 1)) {
72
+ const next = nextTokenStart(code, index + 6);
73
+ if (next < 0 || code[next] === "(") return true;
74
+ }
75
+ return false;
76
+ }
77
+ function calleeOffsets(code, name) {
78
+ const offsets = [];
79
+ for (let index = code.indexOf(name); index >= 0; index = code.indexOf(name, index + 1)) {
80
+ const end = index + name.length;
81
+ if (isAsciiIdentifierPart(code.charCodeAt(index - 1)) || isAsciiIdentifierPart(code.charCodeAt(end))) continue;
82
+ let next = nextTokenStart(code, end);
83
+ if (next >= 0 && code.startsWith("?.", next)) next = nextTokenStart(code, next + 2);
84
+ if (next < 0 || code[next] === "(") offsets.push(index);
85
+ }
86
+ return offsets;
87
+ }
88
+ function nextTokenStart(code, from) {
89
+ let cursor = from;
90
+ for (;;) {
91
+ const char = code.charCodeAt(cursor);
92
+ if (char === 47 && code[cursor + 1] === "*") {
93
+ const end = code.indexOf("*/", cursor + 2);
94
+ if (end < 0) return -1;
95
+ cursor = end + 2;
96
+ } else if (char === 47 && code[cursor + 1] === "/") cursor = nextLineBreak(code, cursor + 2);
97
+ else if (isWhitespace(char)) cursor++;
98
+ else return cursor;
99
+ }
100
+ }
101
+ function nextLineBreak(code, from) {
102
+ for (let index = from; index < code.length; index++) if (isLineTerminator(code.charCodeAt(index))) return index;
103
+ return code.length;
104
+ }
105
+ function isAsciiIdentifierPart(char) {
106
+ return char >= 48 && char <= 57 || char >= 65 && char <= 90 || char >= 97 && char <= 122 || char === 36 || char === 95;
107
+ }
108
+ function isLineTerminator(char) {
109
+ return char === 10 || char === 13 || char === 8232 || char === 8233;
110
+ }
111
+ function isWhitespace(char) {
112
+ return char === 9 || char === 11 || char === 12 || char === 32 || char === 160 || char === 65279 || isLineTerminator(char) || char >= 5760 && /\s/.test(String.fromCharCode(char));
113
+ }
114
+ function textOffsets(code, text) {
115
+ const offsets = [];
116
+ for (let index = code.indexOf(text); index >= 0; index = code.indexOf(text, index + 1)) offsets.push(index);
117
+ return offsets;
118
+ }
119
+ function spansAnyOffset(offsets, node) {
120
+ const { start, end } = node;
121
+ if (typeof start !== "number" || typeof end !== "number") return true;
122
+ let low = 0;
123
+ let high = offsets.length;
124
+ while (low < high) {
125
+ const middle = low + high >>> 1;
126
+ if (offsets[middle] < start) low = middle + 1;
127
+ else high = middle;
128
+ }
129
+ return low < offsets.length && offsets[low] < end;
130
+ }
131
+ function chunkDynamicImports(fileName, code) {
132
+ const known = dynamicImportsByFile.get(fileName);
133
+ if (known?.code === code) return known.value;
134
+ return readChunkDynamicImports(fileName, code);
135
+ }
136
+ function clearChunkFactCaches() {
137
+ dynamicImportsByFile.clear();
138
+ localExportsByFile.clear();
139
+ }
140
+ const dynamicImportsByFile = /* @__PURE__ */ new Map();
141
+ function knowsChunkDynamicImports(fileName, code) {
142
+ return dynamicImportsByFile.get(fileName)?.code === code;
143
+ }
144
+ function rememberChunkDynamicImports(fileName, code, found) {
145
+ dynamicImportsByFile.set(fileName, {
146
+ code,
147
+ value: found
148
+ });
149
+ }
150
+ function readChunkDynamicImports(fileName, code) {
151
+ const parsed = parseChunkCode(fileName, code);
152
+ if (parsed.errors.length) return void 0;
153
+ const found = [];
154
+ for (const entry of parsed.module.dynamicImports) {
155
+ const request = entry.moduleRequest;
156
+ const text = code.slice(request.start, request.end);
157
+ const quote = text[0];
158
+ let specifier;
159
+ if (quote === "\"" || quote === "'" || quote === "`") {
160
+ if (text.length < 2 || text[text.length - 1] !== quote) return void 0;
161
+ const body = text.slice(1, -1);
162
+ if (body.includes("\\") || body.includes(quote)) return void 0;
163
+ specifier = quote === "`" && body.includes("${") ? void 0 : body;
164
+ }
165
+ const afterKeyword = nextTokenStart(code, entry.start + 6);
166
+ let cursor = nextTokenStart(code, request.end);
167
+ if (cursor >= 0 && code[cursor] === ",") cursor = nextTokenStart(code, cursor + 1);
168
+ found.push({
169
+ start: entry.start,
170
+ end: entry.end,
171
+ specifier,
172
+ plain: code[afterKeyword] === "(" && code[cursor] === ")"
173
+ });
174
+ }
175
+ return found;
176
+ }
177
+ function chunkLocalExportSpecifiers(fileName, code) {
178
+ const known = localExportsByFile.get(fileName);
179
+ if (known?.code === code) return known.value;
180
+ return readChunkLocalExportSpecifiers(fileName, code);
181
+ }
182
+ const localExportsByFile = /* @__PURE__ */ new Map();
183
+ function rememberChunkLocalExportSpecifiers(fileName, code, bindings) {
184
+ localExportsByFile.set(fileName, {
185
+ code,
186
+ value: bindings
187
+ });
188
+ }
189
+ function readChunkLocalExportSpecifiers(fileName, code) {
190
+ const parsed = parseChunkCode(fileName, code);
191
+ if (parsed.errors.length) return void 0;
192
+ const bindings = /* @__PURE__ */ new Map();
193
+ for (const statement of parsed.module.staticExports) {
194
+ const braced = code[nextTokenStart(code, statement.start + 6)] === "{";
195
+ for (const entry of statement.entries) {
196
+ if (!braced || entry.moduleRequest) continue;
197
+ if (entry.exportName.kind !== "Name" || entry.localName.kind !== "Name") return void 0;
198
+ bindings.set(entry.exportName.name, entry.localName.name);
199
+ }
200
+ }
201
+ return bindings;
202
+ }
203
+ //#endregion
204
+ export { spansAnyOffset as _, chunkLocalExportSpecifiers as a, knowsChunkDynamicImports as c, prefetchChunkParses as d, readChunkDynamicImports as f, renderedChunkCount as g, rememberChunkLocalExportSpecifiers as h, chunkDynamicImports as i, mayContainDynamicImport as l, rememberChunkDynamicImports as m, PARSED_CHUNK_CACHE_MAX_ENTRIES as n, clearChunkFactCaches as o, readChunkLocalExportSpecifiers as p, calleeOffsets as r, clearParsedChunkCode as s, PARSED_CHUNK_CACHE_MAX_CODE_LENGTH as t, parseChunkCode as u, textOffsets as v };
@@ -0,0 +1 @@
1
+ export { o as runChunkTaskInline } from "./lazy-module-facades-qZajWDD8.js";
@@ -0,0 +1,187 @@
1
+ import { deserialize, serialize } from "node:v8";
2
+ import { threadId } from "node:worker_threads";
3
+ import { createHash } from "node:crypto";
4
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
+ import { mkdir, readdir, rename, rm, stat, utimes, writeFile } from "node:fs/promises";
6
+ import { createRequire } from "node:module";
7
+ import { promisify } from "node:util";
8
+ import * as zlib from "node:zlib";
9
+ import { dirname, join } from "pathe";
10
+ /** Runtimes without zstd in `node:zlib` (older Node, some Bun releases) build without the cache. */
11
+ function persistentCompileCacheAvailable() {
12
+ return typeof zlib.zstdCompress === "function" && typeof zlib.zstdDecompressSync === "function";
13
+ }
14
+ function createPersistentCompileCache(input) {
15
+ const used = /* @__PURE__ */ new Set();
16
+ const writes = /* @__PURE__ */ new Set();
17
+ const strippedCode = persistentTextMemo(join(input.directory, "stripped-code.bin"), `${input.fingerprint}\0${stripperVersion()}`);
18
+ const pathOf = (source, key) => {
19
+ const entry = compileStoreEntry(input.directory, input.fingerprint, source, key);
20
+ used.add(entry.hash);
21
+ return entry.path;
22
+ };
23
+ return {
24
+ read(source, key) {
25
+ const path = pathOf(source, key);
26
+ let bytes;
27
+ try {
28
+ bytes = readFileSync(path);
29
+ } catch {
30
+ return;
31
+ }
32
+ try {
33
+ const result = deserialize(zlib.zstdDecompressSync(bytes));
34
+ const now = /* @__PURE__ */ new Date();
35
+ utimes(path, now, now).catch(() => {});
36
+ return result;
37
+ } catch {
38
+ return;
39
+ }
40
+ },
41
+ write(source, key, result) {
42
+ const path = pathOf(source, key);
43
+ let bytes;
44
+ try {
45
+ bytes = serialize(result);
46
+ } catch {
47
+ return;
48
+ }
49
+ const pending = writeCompileStoreEntry(path, bytes).finally(() => writes.delete(pending));
50
+ writes.add(pending);
51
+ },
52
+ track(write) {
53
+ const pending = write.finally(() => writes.delete(pending));
54
+ writes.add(pending);
55
+ },
56
+ strippedCode,
57
+ async flush() {
58
+ while (writes.size) await Promise.all(writes);
59
+ await strippedCode.save();
60
+ await trim(input.directory, input.maxBytes ?? 1073741824, used);
61
+ }
62
+ };
63
+ }
64
+ /** Where one compile result lives: a hash of the compiler's fingerprint and the whole compile input. */
65
+ function compileStoreEntry(directory, fingerprint, source, key) {
66
+ const hash = createHash("sha256").update(fingerprint).update("\0").update(key).update("\0").update(source).digest("hex");
67
+ return {
68
+ hash,
69
+ path: join(directory, hash.slice(0, 2), `${hash}.bin`)
70
+ };
71
+ }
72
+ /** Writes one serialized result; a failed write only costs a recompile next time. */
73
+ async function writeCompileStoreEntry(path, bytes) {
74
+ try {
75
+ const packed = await promisify(zlib.zstdCompress)(bytes);
76
+ await mkdir(dirname(path), { recursive: true });
77
+ const partial = `${path}.${process.pid}.${threadId}.tmp`;
78
+ await writeFile(partial, packed);
79
+ await rename(partial, path);
80
+ } catch {}
81
+ }
82
+ function persistentTextMemo(path, fingerprint) {
83
+ let stored;
84
+ const kept = /* @__PURE__ */ new Map();
85
+ let changed = false;
86
+ const hashOf = (key) => createHash("sha256").update(fingerprint).update("\0").update(key).digest("base64");
87
+ const load = () => {
88
+ if (stored) return stored;
89
+ try {
90
+ stored = new Map(deserialize(zlib.zstdDecompressSync(readFileSync(path))));
91
+ } catch {
92
+ stored = /* @__PURE__ */ new Map();
93
+ }
94
+ return stored;
95
+ };
96
+ return {
97
+ get(key) {
98
+ const hash = hashOf(key);
99
+ let value = kept.get(hash);
100
+ if (value === void 0) {
101
+ const loaded = load();
102
+ value = loaded.get(hash);
103
+ if (value !== void 0) {
104
+ loaded.delete(hash);
105
+ kept.set(hash, value);
106
+ }
107
+ }
108
+ return value;
109
+ },
110
+ set(key, value) {
111
+ kept.set(hashOf(key), value);
112
+ changed = true;
113
+ },
114
+ async save() {
115
+ if (!changed && load().size === 0) return;
116
+ const packed = await promisify(zlib.zstdCompress)(serialize([...kept]));
117
+ await mkdir(dirname(path), { recursive: true });
118
+ const partial = `${path}.${process.pid}.tmp`;
119
+ await writeFile(partial, packed);
120
+ await rename(partial, path);
121
+ }
122
+ };
123
+ }
124
+ async function trim(directory, maxBytes, used) {
125
+ const files = [];
126
+ let total = 0;
127
+ for (const shard of await readdir(directory).catch(() => [])) for (const name of await readdir(join(directory, shard)).catch(() => [])) {
128
+ const path = join(directory, shard, name);
129
+ const info = await stat(path).catch(() => void 0);
130
+ if (!info?.isFile()) continue;
131
+ total += info.size;
132
+ files.push({
133
+ path,
134
+ hash: name.split(".")[0],
135
+ bytes: info.size,
136
+ mtime: info.mtimeMs
137
+ });
138
+ }
139
+ if (total <= maxBytes) return;
140
+ files.sort((a, b) => a.mtime - b.mtime);
141
+ for (const file of files) {
142
+ if (total <= maxBytes) break;
143
+ if (used.has(file.hash)) continue;
144
+ await rm(file.path, { force: true });
145
+ total -= file.bytes;
146
+ }
147
+ }
148
+ /** A hash of every file the compiler's output depends on: its own package, the serializer it embeds, and its parsers. */
149
+ function compilerFingerprint() {
150
+ const hash = createHash("sha256");
151
+ const compiler = packageDirectory(createRequire(import.meta.url), "@markless/compiler");
152
+ const fromCompiler = createRequire(join(compiler, "package.json"));
153
+ const serializer = packageDirectory(fromCompiler, "@markless/serializer");
154
+ for (const directory of [compiler, serializer]) hashPackage(hash, directory);
155
+ for (const name of ["@tsrx/yuku", "yuku-css"]) try {
156
+ hash.update(readFileSync(join(packageDirectory(fromCompiler, name), "package.json")));
157
+ } catch {
158
+ hash.update(`${name}:missing`);
159
+ }
160
+ return hash.digest("hex");
161
+ }
162
+ function stripperVersion() {
163
+ try {
164
+ return readFileSync(join(packageDirectory(createRequire(import.meta.url), "rolldown"), "package.json"), "utf8");
165
+ } catch {
166
+ return "rolldown:missing";
167
+ }
168
+ }
169
+ function packageDirectory(require, name) {
170
+ let directory = dirname(require.resolve(name));
171
+ while (!existsSync(join(directory, "package.json")) && dirname(directory) !== directory) directory = dirname(directory);
172
+ return directory;
173
+ }
174
+ function hashPackage(hash, directory) {
175
+ hash.update(readFileSync(join(directory, "package.json")));
176
+ for (const sub of ["src", "dist"]) {
177
+ const root = join(directory, sub);
178
+ if (!existsSync(root)) continue;
179
+ for (const name of readdirSync(root, { recursive: true }).sort()) {
180
+ const path = join(root, name);
181
+ if (!statSync(path).isFile()) continue;
182
+ hash.update(name).update("\0").update(readFileSync(path));
183
+ }
184
+ }
185
+ }
186
+ //#endregion
187
+ export { writeCompileStoreEntry as a, persistentCompileCacheAvailable as i, compilerFingerprint as n, createPersistentCompileCache as r, compileStoreEntry as t };
@@ -1,7 +1,7 @@
1
- import "./types-5srcMsXU.js";
1
+ import "./types-B8YqEscg.js";
2
+ import "@markless/compiler";
2
3
  import "@markless/web/inline/resumer";
3
4
  import "@markless/serializer";
4
- import "@markless/compiler";
5
5
  //#region src/source-module.d.ts
6
6
  declare const MARKLESS_VIRTUAL_PREFIX = "virtual:markless:";
7
7
  declare function symbolVirtualModuleSourceFile(moduleId: string): string | null;