Skip to content

Commit

Permalink
chore(viteroll): use simple app format (#78)
Browse files Browse the repository at this point in the history
hi-ogawa authored Nov 30, 2024
1 parent 1d651c8 commit 9eb8b68
Showing 4 changed files with 294 additions and 644 deletions.
2 changes: 1 addition & 1 deletion viteroll/package.json
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
"@playwright/test": "^1.48.2",
"@types/node": "^22.8.5",
"preact": "^10.24.3",
"rolldown": "file:./rolldown-0.14.0.tgz",
"rolldown": "link:../../../others/rolldown/packages/rolldown",
"typescript": "^5.6.3",
"vite": "6.0.0-beta.8"
},
585 changes: 3 additions & 582 deletions viteroll/pnpm-lock.yaml

Large diffs are not rendered by default.

237 changes: 237 additions & 0 deletions viteroll/viteroll-runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// based on
// https://github.com/rolldown/rolldown/blob/a29240168290e45b36fdc1a6d5c375281fb8dc3e/crates/rolldown/src/runtime/runtime-without-comments.js#L69
// https://github.com/hi-ogawa/rolldown/blob/27d203a74d8dd95aed256bde29232d535bd294f4/crates/rolldown/src/runtime/runtime-app.js

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res) =>
function () {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])((fn = 0))), res;
};
var __esmMin = (fn, res) => () => (fn && (res = fn((fn = 0))), res);
var __commonJS = (cb, mod) =>
function () {
return (
mod ||
(0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod),
mod.exports
);
};
var __commonJSMin = (cb, mod) => () => (
mod || cb((mod = { exports: {} }).exports, mod), mod.exports
);
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if ((from && typeof from === "object") || typeof from === "function")
for (
var keys = __getOwnPropNames(from), i = 0, n = keys.length, key;
i < n;
i++
) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
});
}
return to;
};
var __reExport = (target, mod, secondTarget) => (
__copyProps(target, mod, "default"),
secondTarget && __copyProps(secondTarget, mod, "default")
);
var __toESM = (mod, isNodeMode, target) => (
(target = mod != null ? __create(__getProtoOf(mod)) : {}),
__copyProps(
isNodeMode || !mod || !mod.__esModule
? __defProp(target, "default", { value: mod, enumerable: true })
: target,
mod,
)
);
var __toCommonJS = (mod) =>
__copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __toBinaryNode = (base64) => new Uint8Array(Buffer.from(base64, "base64"));
var __toBinary = /* @__PURE__ */ (() => {
var table = new Uint8Array(128);
for (var i = 0; i < 64; i++)
table[i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i * 4 - 205] = i;
return (base64) => {
var n = base64.length,
bytes = new Uint8Array(
(((n - (base64[n - 1] == "=") - (base64[n - 2] == "=")) * 3) / 4) | 0,
);
for (var i = 0, j = 0; i < n; ) {
var c0 = table[base64.charCodeAt(i++)],
c1 = table[base64.charCodeAt(i++)];
var c2 = table[base64.charCodeAt(i++)],
c3 = table[base64.charCodeAt(i++)];
bytes[j++] = (c0 << 2) | (c1 >> 4);
bytes[j++] = (c1 << 4) | (c2 >> 2);
bytes[j++] = (c2 << 6) | c3;
}
return bytes;
};
})();

var rolldown_runtime = (self.rolldown_runtime = {
patching: false,
patchedModuleFactoryMap: {},
executeModuleStack: [],
moduleCache: {},
moduleFactoryMap: {},
define: function (id, factory) {
if (self.patching) {
this.patchedModuleFactoryMap[id] = factory;
} else {
this.moduleFactoryMap[id] = factory;
}
},
require: function (id) {
const parent =
this.executeModuleStack.length >= 1
? this.executeModuleStack[this.executeModuleStack.length - 1]
: null;
if (this.moduleCache[id]) {
var module = this.moduleCache[id];
if (parent && module.parents.indexOf(parent) === -1) {
module.parents.push(parent);
}
return module.exports;
}
var factory = this.moduleFactoryMap[id];
if (!factory) {
throw new Error("Module not found: " + id);
}
var module = (this.moduleCache[id] = {
exports: {},
parents: parent ? [parent] : [],
hot: {
selfAccept: false,
acceptCallbacks: [],
accept: function (callback) {
this.selfAccept = true;
if (callback && typeof callback === "function") {
this.acceptCallbacks.push({
deps: [id],
callback,
});
}
},
},
});
this.executeModuleStack.push(id);
factory(this.require.bind(this), module, module.exports);
this.executeModuleStack.pop();
return module.exports;
},
patch: function (updateModuleIds, callback) {
self.patching = true;

callback();

var boundaries = [];
var invalidModuleIds = [];
var acceptCallbacks = [];

for (var i = 0; i < updateModuleIds.length; i++) {
foundBoundariesAndInvalidModuleIds(
updateModuleIds[i],
boundaries,
invalidModuleIds,
acceptCallbacks,
);
}

for (var i = 0; i < invalidModuleIds.length; i++) {
var id = invalidModuleIds[i];
delete this.moduleCache[id];
}

for (var id in this.patchedModuleFactoryMap) {
this.moduleFactoryMap[id] = this.patchedModuleFactoryMap[id];
}
this.patchedModuleFactoryMap = {};

for (var i = 0; i < boundaries.length; i++) {
this.require(boundaries[i]);
}

for (var i = 0; i < acceptCallbacks.length; i++) {
var item = acceptCallbacks[i];
item.callback.apply(
null,
item.deps.map((dep) => this.moduleCache[dep].exports),
);
}

self.patching = false;

function foundBoundariesAndInvalidModuleIds(
updateModuleId,
boundaries,
invalidModuleIds,
acceptCallbacks,
) {
var queue = [{ moduleId: updateModuleId, chain: [updateModuleId] }];
var visited = {};

while (queue.length > 0) {
var item = queue.pop();
var moduleId = item.moduleId;
var chain = item.chain;

if (visited[moduleId]) {
continue;
}

var module = rolldown_runtime.moduleCache[moduleId];
if (!module) {
continue;
}

if (module.hot.selfAccept) {
if (boundaries.indexOf(moduleId) === -1) {
boundaries.push(moduleId);

for (var i = 0; i < module.hot.acceptCallbacks.length; i++) {
var item = module.hot.acceptCallbacks[i];
acceptCallbacks.push(item);
}
}
for (var i = 0; i < chain.length; i++) {
if (invalidModuleIds.indexOf(chain[i]) === -1) {
invalidModuleIds.push(chain[i]);
}
}
continue;
}

boundaries.push(moduleId);
invalidModuleIds.push(moduleId);
if (module.parents.length === 0) {
globalThis.window?.location.reload();
break;
}

for (var i = 0; i < module.parents.length; i++) {
var parent = module.parents[i];
queue.push({
moduleId: parent,
chain: chain.concat([parent]),
});
}

visited[moduleId] = true;
}
}
},
});
114 changes: 53 additions & 61 deletions viteroll/viteroll.ts
Original file line number Diff line number Diff line change
@@ -147,7 +147,7 @@ hot.on("rolldown:hmr", (data) => {
});
window.__rolldown_hot = hot;
`;
return `(() => {/*** @vite/client for rolldown ***/\n${code}}\n)()`;
return `\n;(() => {/*** @vite/client for rolldown ***/\n${code}}\n)();`;
}

export class RolldownEnvironment extends DevEnvironment {
@@ -244,24 +244,6 @@ export class RolldownEnvironment extends DevEnvironment {
rolldownExperimental.aliasPlugin({
entries: this.config.resolve.alias,
}),
{
name: "viteroll:extract-hmr-chunk",
renderChunk: (_code, chunk) => {
// cf. https://github.com/web-infra-dev/rspack/blob/5a967f7a10ec51171a304a1ce8d741bd09fa8ed5/crates/rspack_plugin_hmr/src/lib.rs#L60
// TODO: assume single chunk for now
this.newModules = {};
const modules: Record<string, string | null> = {};
for (const [id, mod] of Object.entries(chunk.modules)) {
const current = mod.code;
const last = this.lastModules?.[id];
if (current !== last) {
this.newModules[id] = current;
}
modules[id] = current;
}
this.lastModules = modules;
},
},
...(plugins as any),
],
};
@@ -287,6 +269,21 @@ export class RolldownEnvironment extends DevEnvironment {
// `generate` should work but we use `write` so it's easier to see output and debug
this.result = await this.instance.write(this.outputOptions);

// extract hmr chunk
// cf. https://github.com/web-infra-dev/rspack/blob/5a967f7a10ec51171a304a1ce8d741bd09fa8ed5/crates/rspack_plugin_hmr/src/lib.rs#L60
const chunk = this.result.output[0];
this.newModules = {};
const modules: Record<string, string | null> = {};
for (const [id, mod] of Object.entries(chunk.modules)) {
const current = mod.code;
const last = this.lastModules?.[id];
if (current !== last) {
this.newModules[id] = current;
}
modules[id] = current;
}
this.lastModules = modules;

this.buildTimestamp = Date.now();
console.timeEnd(`[rolldown:${this.name}:build]`);
}
@@ -459,50 +456,45 @@ function viterollEntryPlugin(
};
},
},
renderChunk(code) {
// patch rolldown_runtime to workaround a few things
if (code.includes("//#region rolldown:runtime")) {
const output = new MagicString(code);
// replace hard-coded WebSocket setup with custom one
output.replace(
/const socket =.*?\n};/s,
environment.name === "client" ? getRolldownClientCode(config) : "",
renderChunk(code, chunk) {
// silly but we can do `render_app` on our own for now
// https://github.com/rolldown/rolldown/blob/a29240168290e45b36fdc1a6d5c375281fb8dc3e/crates/rolldown/src/ecmascript/format/app.rs#L28-L55
const output = new MagicString(code);

// extract isolated module between #region and #endregion
const matches = code.matchAll(/^\/\/#region (.*)$/gm);
for (const match of matches) {
const stableId = match[1]!;
const start = match.index!;
const end = code.indexOf("//#endregion", match.index);
output.appendLeft(
start,
`rolldown_runtime.define(${JSON.stringify(stableId)},function(require, module, exports){\n\n`,
);
// trigger full rebuild on non-accepting entry invalidation
output
.replace(
"this.executeModuleStack.length > 1",
"this.executeModuleStack.length >= 1",
)
.replace("parents: [parent],", "parents: parent ? [parent] : [],")
.replace(
"if (module.parents.indexOf(parent) === -1) {",
"if (parent && module.parents.indexOf(parent) === -1) {",
)
.replace("if (item.deps.includes(updateModuleId)) {", "if (true) {")
.replace(
"var module = rolldown_runtime.moduleCache[moduleId];",
"var module = rolldown_runtime.moduleCache[moduleId]; if (!module) { continue; }",
)
.replace(
"for (var i = 0; i < module.parents.length; i++) {",
`
boundaries.push(moduleId);
invalidModuleIds.push(moduleId);
if (module.parents.filter(Boolean).length === 0) {
globalThis.window?.location.reload();
break;
}
for (var i = 0; i < module.parents.length; i++) {`,
);
if (viterollOptions.reactRefresh) {
output.prepend(getReactRefreshRuntimeCode());
}
return {
code: output.toString(),
map: output.generateMap({ hires: "boundary" }),
};
output.appendRight(end, `\n\n});\n`);
}
assert(chunk.facadeModuleId);
const stableId = path.relative(config.root, chunk.facadeModuleId);
output.append(
`\nrolldown_runtime.require(${JSON.stringify(stableId)});\n`,
);

// inject runtime
const runtimeCode = fs.readFileSync(
path.join(import.meta.dirname, "viteroll-runtime.js"),
"utf-8",
);
output.prepend(runtimeCode);
if (environment.name === "client") {
output.prepend(getRolldownClientCode(config));
}
if (viterollOptions.reactRefresh) {
output.prepend(getReactRefreshRuntimeCode());
}
return {
code: output.toString(),
map: output.generateMap({ hires: "boundary" }),
};
},
generateBundle(_options, bundle) {
for (const key in bundle) {

0 comments on commit 9eb8b68

Please sign in to comment.