@backtickjs/bundler 0.1.7 → 0.1.9
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 +17 -7
- package/dist/bundle/buildBundle.d.ts +2 -1
- package/dist/bundle/buildBundle.js +43 -49
- package/dist/bundle/buildBundle.js.map +1 -1
- package/dist/bundle/expandServerComponents.d.ts +7 -0
- package/dist/bundle/expandServerComponents.js +77 -0
- package/dist/bundle/expandServerComponents.js.map +1 -0
- package/dist/bundler.d.ts +4 -3
- package/dist/bundler.js +3 -3
- package/dist/bundler.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modules.d.ts +11 -0
- package/dist/modules.js +11 -0
- package/dist/modules.js.map +1 -0
- package/dist/print/code.d.ts +4 -3
- package/dist/print/code.js +9 -16
- package/dist/print/code.js.map +1 -1
- package/package.json +3 -3
- package/src/bundle/buildBundle.ts +53 -63
- package/src/bundle/expandServerComponents.ts +106 -0
- package/src/bundler.ts +8 -7
- package/src/index.ts +1 -0
- package/src/modules.ts +21 -0
- package/src/print/code.ts +12 -22
- package/dist/bundle/expandJsxElement.d.ts +0 -2
- package/dist/bundle/expandJsxElement.js +0 -8
- package/dist/bundle/expandJsxElement.js.map +0 -1
- package/src/bundle/expandJsxElement.ts +0 -12
package/README.md
CHANGED
|
@@ -16,10 +16,14 @@ For React Native, write CommonJS and serve it; the app fetches it and runs it wi
|
|
|
16
16
|
import { bundler } from "@backtickjs/bundler";
|
|
17
17
|
import { Home } from "./Home.js";
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const modules = {
|
|
20
|
+
react: "19.2.3",
|
|
21
|
+
"react/jsx-runtime": "19.2.3",
|
|
22
|
+
"react-native": "0.86.3",
|
|
23
|
+
};
|
|
20
24
|
|
|
21
25
|
// In a request handler:
|
|
22
|
-
const bundle = await bundler.build({ input: <Home />,
|
|
26
|
+
const bundle = await bundler.build({ input: <Home />, modules });
|
|
23
27
|
const { code } = bundle.generate({ format: "cjs" });
|
|
24
28
|
response.setHeader("content-type", "text/javascript");
|
|
25
29
|
response.end(code);
|
|
@@ -36,14 +40,20 @@ import { Home } from "./Home.js";
|
|
|
36
40
|
const element = <Home />;
|
|
37
41
|
const bundle = await bundler.build({
|
|
38
42
|
input: cs`$createRoot(document.getElementById("app")!).render($element)`,
|
|
39
|
-
|
|
43
|
+
modules: {
|
|
44
|
+
react: "19.2.3",
|
|
45
|
+
"react/jsx-runtime": "19.2.3",
|
|
46
|
+
"react-dom/client": "19.2.3",
|
|
47
|
+
},
|
|
40
48
|
});
|
|
41
49
|
const { code } = bundle.generate({ format: "es", sourcemap: "inline" });
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
### `
|
|
52
|
+
### `modules`
|
|
45
53
|
|
|
46
|
-
The
|
|
54
|
+
The modules the client provides, by specifier, each at the exact version of the package it comes from: `"react-dom/client": "19.2.3"`. The server decides which to bundle for: one app version, or one per build with [`@backtickjs/builds`](https://github.com/backtickjs/backtick/tree/main/packages/builds). Those modules are left out of the bundle, which imports (`es`) or requires (`cjs`) them. `build` rejects a script that imports from any other module, or whose import needs a range that doesn't include the version given: an adapter's (`^19.2.3` for `@backtickjs/react`) or one written with `createImport`.
|
|
55
|
+
|
|
56
|
+
`supports(modules, specifier, range)` makes the same check, for a server choosing what to draw for a client: `supports(modules, "expo-haptics", "^15")`.
|
|
47
57
|
|
|
48
58
|
### What crosses into a script
|
|
49
59
|
|
|
@@ -60,7 +70,7 @@ Spliced values are written into the bundle as data, never as code. What can't cr
|
|
|
60
70
|
|
|
61
71
|
## API
|
|
62
72
|
|
|
63
|
-
- `bundler.build({ input,
|
|
73
|
+
- `bundler.build({ input, modules })`: runs the server components in `input` and returns a `Bundle`. Async.
|
|
64
74
|
- `Bundle.generate({ format, sourcemap })`: writes the bundle; returns `{ code, map }`, where `map` is `null` without a source map.
|
|
65
75
|
- Types: `BuildOptions`, `OutputOptions`, `OutputChunk`, `Bundle`.
|
|
66
76
|
|
|
@@ -68,7 +78,7 @@ Spliced values are written into the bundle as data, never as code. What can't cr
|
|
|
68
78
|
|
|
69
79
|
- [`@backtickjs/core`](https://github.com/backtickjs/backtick/tree/main/packages/core): the `cs` tag
|
|
70
80
|
- [`@backtickjs/react-native-client`](https://github.com/backtickjs/backtick/tree/main/packages/react-native-client): runs a `cjs` bundle in a React Native app
|
|
71
|
-
- [`@backtickjs/react`](https://github.com/backtickjs/backtick/tree/main/
|
|
81
|
+
- [`@backtickjs/react`](https://github.com/backtickjs/backtick/tree/main/adapters/react), [`@backtickjs/react-native`](https://github.com/backtickjs/backtick/tree/main/adapters/react-native), [`@backtickjs/solid-js`](https://github.com/backtickjs/backtick/tree/main/adapters/solid-js): framework adapters
|
|
72
82
|
- [How it works](https://backtickjs.com/docs/how-it-works)
|
|
73
83
|
- [Errors](https://backtickjs.com/docs/errors)
|
|
74
84
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ClientModule } from "@backtickjs/core";
|
|
2
2
|
import type { Names } from "../print/code.js";
|
|
3
|
+
import type { Modules } from "../modules.js";
|
|
3
4
|
/**
|
|
4
5
|
* A bundle as it is built, before it is printed: each script's module it
|
|
5
6
|
* declares under its label, in the order rendering first reached it, and the
|
|
@@ -11,4 +12,4 @@ export interface BundleTree {
|
|
|
11
12
|
readonly root: string;
|
|
12
13
|
readonly names: Names;
|
|
13
14
|
}
|
|
14
|
-
export declare function buildBundle(value: unknown,
|
|
15
|
+
export declare function buildBundle(value: unknown, provided: Modules): Promise<BundleTree>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isClientImport, isClientScript, isJsxElement, } from "@backtickjs/core";
|
|
2
|
-
import {
|
|
2
|
+
import { expandServerComponents } from "./expandServerComponents.js";
|
|
3
3
|
import { bindingsOf, capturesOf } from "./params.js";
|
|
4
4
|
import { sourceName } from "./bindingKey.js";
|
|
5
5
|
import { array, arrow, call, createNames, imported, literal, object, } from "../print/code.js";
|
|
@@ -25,8 +25,8 @@ const rootScope = () => ({ bindings: new Set(), read: new Set() });
|
|
|
25
25
|
// name, a capture is threaded under that one name the whole way down — an
|
|
26
26
|
// intermediate script that binds a same-looking variable has a different
|
|
27
27
|
// unique name, so there is nothing to disambiguate and nothing to rename.
|
|
28
|
-
export async function buildBundle(value,
|
|
29
|
-
const names = createNames(
|
|
28
|
+
export async function buildBundle(value, provided) {
|
|
29
|
+
const names = createNames(provided);
|
|
30
30
|
// Each script's number, in the order rendering first reaches it. Two
|
|
31
31
|
// scripts written at one source location are one declaration, so the first
|
|
32
32
|
// script with an id stands for all of them, and a reference to a shared
|
|
@@ -100,9 +100,9 @@ export async function buildBundle(value, packageVersions) {
|
|
|
100
100
|
// `write` renders a closure's code in a scope that records what it reads:
|
|
101
101
|
// a constant's label if it read nothing of `scope` beyond `own`, its
|
|
102
102
|
// parameters, or else the code, written where it's used.
|
|
103
|
-
const closure =
|
|
103
|
+
const closure = (scope, own, write, name) => {
|
|
104
104
|
const read = new Set();
|
|
105
|
-
const code =
|
|
105
|
+
const code = write({
|
|
106
106
|
bindings: new Set([...scope.bindings, ...own]),
|
|
107
107
|
read,
|
|
108
108
|
});
|
|
@@ -119,42 +119,41 @@ export async function buildBundle(value, packageVersions) {
|
|
|
119
119
|
}
|
|
120
120
|
return label;
|
|
121
121
|
};
|
|
122
|
+
// A `once` script is one value: its code run at the top of the bundle,
|
|
123
|
+
// where nothing encloses it, and every read is that constant. One per
|
|
124
|
+
// script, which is one per call of `once`, not per code: two runs of a host
|
|
125
|
+
// function that each make a context are two contexts, as two calls of
|
|
126
|
+
// `createContext` are. Running code is observable, so unlike a function it
|
|
127
|
+
// is never shared by code.
|
|
128
|
+
const values = new Map();
|
|
129
|
+
const valueOf = (script) => {
|
|
130
|
+
let label = values.get(script);
|
|
131
|
+
if (label === undefined) {
|
|
132
|
+
const code = call(labelOf(scriptFor(script)), exprCallArgs(script, rootScope()));
|
|
133
|
+
label = `$value${constants.length}`;
|
|
134
|
+
values.set(script, label);
|
|
135
|
+
constants.push([label, code]);
|
|
136
|
+
}
|
|
137
|
+
return label;
|
|
138
|
+
};
|
|
122
139
|
// Writes a value as the code it becomes: composition as data, which is what
|
|
123
140
|
// a bundle is. A script reference is a call naming which script and
|
|
124
141
|
// what to hand it; everything else is its literal form. Rendered in order,
|
|
125
142
|
// one value after the other, so the table follows the order rendering first
|
|
126
|
-
// reached each script.
|
|
127
|
-
|
|
128
|
-
// itself, which written out would never end. A value used in two places
|
|
129
|
-
// isn't one: it's left before it's met again.
|
|
130
|
-
const path = new Set();
|
|
131
|
-
const render = async (value, scope = rootScope()) => {
|
|
132
|
-
if (typeof value !== "object" || value === null) {
|
|
133
|
-
return renderValue(value, scope);
|
|
134
|
-
}
|
|
135
|
-
if (path.has(value)) {
|
|
136
|
-
throw new Error("Can't splice a value that contains itself: a bundle writes each " +
|
|
137
|
-
"value out in full, so a cycle never ends. Break the cycle before " +
|
|
138
|
-
"splicing it.");
|
|
139
|
-
}
|
|
140
|
-
path.add(value);
|
|
141
|
-
try {
|
|
142
|
-
return await renderValue(value, scope);
|
|
143
|
-
}
|
|
144
|
-
finally {
|
|
145
|
-
path.delete(value);
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const renderValue = async (value, scope) => {
|
|
143
|
+
// reached each script. Expansion has refused cycles, so it ends.
|
|
144
|
+
const render = (value, scope = rootScope()) => {
|
|
149
145
|
if (isClientScript(value)) {
|
|
146
|
+
if (value.once) {
|
|
147
|
+
return valueOf(value);
|
|
148
|
+
}
|
|
150
149
|
const target = scriptFor(value);
|
|
151
150
|
if (target.module.kind !== "function") {
|
|
152
|
-
return call(labelOf(target),
|
|
151
|
+
return call(labelOf(target), exprCallArgs(value, scope));
|
|
153
152
|
}
|
|
154
|
-
return closure(scope, [],
|
|
153
|
+
return closure(scope, [], (inner) => call(labelOf(target), exprCallArgs(value, inner)), "$function");
|
|
155
154
|
}
|
|
156
155
|
if (isJsxElement(value)) {
|
|
157
|
-
return renderJsx(value
|
|
156
|
+
return renderJsx(value);
|
|
158
157
|
}
|
|
159
158
|
if (isClientImport(value)) {
|
|
160
159
|
return imported(names, value);
|
|
@@ -170,7 +169,7 @@ export async function buildBundle(value, packageVersions) {
|
|
|
170
169
|
if (Array.isArray(value)) {
|
|
171
170
|
const elements = [];
|
|
172
171
|
for (const element of value) {
|
|
173
|
-
elements.push(
|
|
172
|
+
elements.push(render(element, scope));
|
|
174
173
|
}
|
|
175
174
|
return array(elements);
|
|
176
175
|
}
|
|
@@ -205,7 +204,7 @@ export async function buildBundle(value, packageVersions) {
|
|
|
205
204
|
}
|
|
206
205
|
const entries = [];
|
|
207
206
|
for (const [key, entry] of Object.entries(value)) {
|
|
208
|
-
entries.push([key,
|
|
207
|
+
entries.push([key, render(entry, scope)]);
|
|
209
208
|
}
|
|
210
209
|
return object(entries);
|
|
211
210
|
};
|
|
@@ -224,11 +223,13 @@ export async function buildBundle(value, packageVersions) {
|
|
|
224
223
|
sourceName(key) +
|
|
225
224
|
"` shadows it: the binding is still there, but no longer reachable by " +
|
|
226
225
|
"name, and naming it anyway would mean emitting what the source " +
|
|
227
|
-
"couldn't say."
|
|
226
|
+
"couldn't say. A `once` script is made at the top of the bundle, " +
|
|
227
|
+
"outside every script, so a fragment spliced into one can't reach " +
|
|
228
|
+
"its binding either.");
|
|
228
229
|
};
|
|
229
230
|
// The arguments of a call of a script: a thunk per splice, then its
|
|
230
231
|
// captures.
|
|
231
|
-
const exprCallArgs =
|
|
232
|
+
const exprCallArgs = (ref, scope) => {
|
|
232
233
|
const target = scriptFor(ref);
|
|
233
234
|
const parts = [];
|
|
234
235
|
// The splices lead the parameters, one `arg` each.
|
|
@@ -238,25 +239,17 @@ export async function buildBundle(value, packageVersions) {
|
|
|
238
239
|
// nested inside it.
|
|
239
240
|
const passed = [...passKeys(target, index), ...capturesOf(target)];
|
|
240
241
|
// A thunk taking them, the splice rendered inside it.
|
|
241
|
-
parts.push(
|
|
242
|
+
parts.push(closure(scope, passed, (inner) => arrow(passed.map(displayName), render(arg, inner)), "$thunk"));
|
|
242
243
|
}
|
|
243
244
|
for (const key of capturesOf(target)) {
|
|
244
245
|
parts.push(capExpr(key, scope));
|
|
245
246
|
}
|
|
246
247
|
return parts;
|
|
247
248
|
};
|
|
248
|
-
// A tag
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
// A prop that is `undefined` is left out, as JSX and TypeScript's optional
|
|
253
|
-
// props read it: a component forwarding an optional prop it wasn't given
|
|
254
|
-
// writes nothing.
|
|
255
|
-
const renderJsx = async (jsx, scope) => {
|
|
249
|
+
// A tag left after expansion, which ran every server component's: one the
|
|
250
|
+
// client draws.
|
|
251
|
+
const renderJsx = (jsx) => {
|
|
256
252
|
const type = jsx.type;
|
|
257
|
-
if (typeof type === "function" && !isClientImport(type)) {
|
|
258
|
-
return render(await expandJsxElement(jsx, type), scope);
|
|
259
|
-
}
|
|
260
253
|
// An element and a client component (an import, or a script answering
|
|
261
254
|
// one) are client code, so on the host they can't be tags: they are tags
|
|
262
255
|
// in a script.
|
|
@@ -268,8 +261,9 @@ export async function buildBundle(value, packageVersions) {
|
|
|
268
261
|
throw new Error(`${tag} is a client component, so it can't be a tag on the host. ` +
|
|
269
262
|
"Use it as a tag in a script.");
|
|
270
263
|
};
|
|
271
|
-
//
|
|
272
|
-
|
|
264
|
+
// Every server component first, so siblings run at once; then the drawing,
|
|
265
|
+
// in order. Nothing encloses the root, so nothing it holds can capture.
|
|
266
|
+
const root = render(await expandServerComponents(value));
|
|
273
267
|
// In table order, which is the order rendering first reached each script.
|
|
274
268
|
const modules = [...numbers.keys()].map((script) => [labelOf(script), script.module]);
|
|
275
269
|
return { modules, constants, root, names };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildBundle.js","sourceRoot":"","sources":["../../src/bundle/buildBundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,cAAc,EACd,cAAc,EACd,YAAY,GAEb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"buildBundle.js","sourceRoot":"","sources":["../../src/bundle/buildBundle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ClientScript } from "@backtickjs/core";
|
|
2
|
+
interface Ancestors {
|
|
3
|
+
readonly value: object;
|
|
4
|
+
readonly parent: Ancestors | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function expandServerComponents(value: unknown, ancestors?: Ancestors | null, values?: Map<ClientScript, Promise<unknown>>): Promise<unknown>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { isClientImport, isClientScript, isJsxElement, } from "@backtickjs/core";
|
|
2
|
+
const CYCLE_ERROR = "Can't splice a value that contains itself: a bundle writes each value " +
|
|
3
|
+
"out in full, so a cycle never ends. Break the cycle before splicing it.";
|
|
4
|
+
const contains = (ancestors, value) => {
|
|
5
|
+
for (let at = ancestors; at !== null; at = at.parent) {
|
|
6
|
+
if (at.value === value) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
};
|
|
12
|
+
// Runs every server component in a value and answers with the value as drawn:
|
|
13
|
+
// each element of one replaced by what its component drew, expanded in turn.
|
|
14
|
+
// Siblings expand at once, so two components awaiting data wait together.
|
|
15
|
+
//
|
|
16
|
+
// A component runs each time its element is drawn, as React renders an element
|
|
17
|
+
// each place it stands: the component itself never leaves the host, and only
|
|
18
|
+
// the client code it bottoms out in reaches the bundle. A value holding no
|
|
19
|
+
// server component comes back as the same object.
|
|
20
|
+
//
|
|
21
|
+
// A `once` script is one value, so what it splices is expanded once, however
|
|
22
|
+
// many places read it: a server component inside it runs once. `values` holds
|
|
23
|
+
// each one's expansion for the call.
|
|
24
|
+
export async function expandServerComponents(value, ancestors = null, values = new Map()) {
|
|
25
|
+
if (typeof value !== "object" || value === null) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
if (contains(ancestors, value)) {
|
|
29
|
+
throw new Error(CYCLE_ERROR);
|
|
30
|
+
}
|
|
31
|
+
const inside = { value, parent: ancestors };
|
|
32
|
+
const all = (entries) => Promise.all(entries.map((entry) => expandServerComponents(entry, inside, values)));
|
|
33
|
+
if (isJsxElement(value)) {
|
|
34
|
+
const { type } = value;
|
|
35
|
+
// A tag the client draws is left for rendering to refuse.
|
|
36
|
+
if (typeof type !== "function" || isClientImport(type)) {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
return expandServerComponents(await type(value.props), inside, values);
|
|
40
|
+
}
|
|
41
|
+
if (isClientScript(value)) {
|
|
42
|
+
const expanded = async () => {
|
|
43
|
+
const args = await all(value.args);
|
|
44
|
+
return args.every((arg, index) => arg === value.args[index])
|
|
45
|
+
? value
|
|
46
|
+
: { ...value, args };
|
|
47
|
+
};
|
|
48
|
+
if (!value.once) {
|
|
49
|
+
return expanded();
|
|
50
|
+
}
|
|
51
|
+
let made = values.get(value);
|
|
52
|
+
if (made === undefined) {
|
|
53
|
+
made = expanded();
|
|
54
|
+
values.set(value, made);
|
|
55
|
+
}
|
|
56
|
+
return made;
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
const elements = await all(value);
|
|
60
|
+
return elements.every((element, index) => element === value[index])
|
|
61
|
+
? value
|
|
62
|
+
: elements;
|
|
63
|
+
}
|
|
64
|
+
// Only plain objects cross; rendering refuses anything else.
|
|
65
|
+
const prototype = Object.getPrototypeOf(value);
|
|
66
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
const entries = Object.entries(value);
|
|
70
|
+
const expanded = await all(entries.map(([, entry]) => entry));
|
|
71
|
+
if (expanded.every((entry, index) => entry === entries[index][1])) {
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
// Defined, not assigned, so a `__proto__` key stays a key.
|
|
75
|
+
return Object.fromEntries(entries.map(([key], index) => [key, expanded[index]]));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=expandServerComponents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expandServerComponents.js","sourceRoot":"","sources":["../../src/bundle/expandServerComponents.ts"],"names":[],"mappings":"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"}
|
package/dist/bundler.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Spliceable, SplicesAs } from "@backtickjs/core";
|
|
2
|
+
import type { Modules } from "./modules.js";
|
|
2
3
|
export interface BuildOptions<T = Spliceable> {
|
|
3
4
|
readonly input: T;
|
|
4
|
-
readonly
|
|
5
|
+
readonly modules: Modules;
|
|
5
6
|
}
|
|
6
7
|
export interface OutputOptions {
|
|
7
8
|
readonly format: "es" | "cjs";
|
|
@@ -20,7 +21,7 @@ export interface Bundle {
|
|
|
20
21
|
* What runs your components and hands back what they drew, shaped as Rollup
|
|
21
22
|
* is: a build, then output from it.
|
|
22
23
|
*
|
|
23
|
-
* const bundle = await bundler.build({ input: <Home />,
|
|
24
|
+
* const bundle = await bundler.build({ input: <Home />, modules });
|
|
24
25
|
* const { code, map } = bundle.generate({ format: "es", sourcemap: "hidden" });
|
|
25
26
|
*
|
|
26
27
|
* The module's default export is the value (CommonJS: `module.exports`). A
|
|
@@ -28,5 +29,5 @@ export interface Bundle {
|
|
|
28
29
|
* give what comes back.
|
|
29
30
|
*/
|
|
30
31
|
export declare const bundler: {
|
|
31
|
-
build<T extends [T] extends [SplicesAs<T>] ? unknown : Spliceable>({ input,
|
|
32
|
+
build<T extends [T] extends [SplicesAs<T>] ? unknown : Spliceable>({ input, modules, }: BuildOptions<T>): Promise<Bundle>;
|
|
32
33
|
};
|
package/dist/bundler.js
CHANGED
|
@@ -4,7 +4,7 @@ import { printBundle } from "./print/printBundle.js";
|
|
|
4
4
|
* What runs your components and hands back what they drew, shaped as Rollup
|
|
5
5
|
* is: a build, then output from it.
|
|
6
6
|
*
|
|
7
|
-
* const bundle = await bundler.build({ input: <Home />,
|
|
7
|
+
* const bundle = await bundler.build({ input: <Home />, modules });
|
|
8
8
|
* const { code, map } = bundle.generate({ format: "es", sourcemap: "hidden" });
|
|
9
9
|
*
|
|
10
10
|
* The module's default export is the value (CommonJS: `module.exports`). A
|
|
@@ -12,10 +12,10 @@ import { printBundle } from "./print/printBundle.js";
|
|
|
12
12
|
* give what comes back.
|
|
13
13
|
*/
|
|
14
14
|
export const bundler = {
|
|
15
|
-
async build({ input,
|
|
15
|
+
async build({ input, modules, }) {
|
|
16
16
|
// Scripts come compiled for their framework when their host was built, so
|
|
17
17
|
// the bundle is only put together: nothing is compiled or parsed here.
|
|
18
|
-
const tree = await buildBundle(input,
|
|
18
|
+
const tree = await buildBundle(input, modules);
|
|
19
19
|
return {
|
|
20
20
|
generate: ({ format, sourcemap = false }) => {
|
|
21
21
|
const { code, map } = printBundle(tree, format);
|
package/dist/bundler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundler.js","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"bundler.js","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAiCrD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,KAAK,CAAC,KAAK,CAA8D,EACvE,KAAK,EACL,OAAO,GACS;QAChB,0EAA0E;QAC1E,uEAAuE;QACvE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO;YACL,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,EAAE,EAAE;gBAC1C,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAChD,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;oBACxB,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;gBAC7B,CAAC;gBACD,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;oBAC3B,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;gBACvB,CAAC;gBACD,MAAM,GAAG,GAAG,uCAAuC,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7E,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,0BAA0B,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC;YAC/D,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,OAAO,GAGR,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,OAAO,GAGR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAgB,QAAQ,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The modules a client provides, by specifier, each at the exact version of
|
|
3
|
+
* the package it comes from: `{ "react/jsx-runtime": "19.2.3" }`.
|
|
4
|
+
*/
|
|
5
|
+
export type Modules = Readonly<Record<string, string>>;
|
|
6
|
+
/**
|
|
7
|
+
* Whether a client with `modules` provides `specifier` at a version in
|
|
8
|
+
* `range`: the check a bundle's every import passes, so a server choosing
|
|
9
|
+
* what to draw for a client can ask it first.
|
|
10
|
+
*/
|
|
11
|
+
export declare function supports(modules: Modules, specifier: string, range: string): boolean;
|
package/dist/modules.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a client with `modules` provides `specifier` at a version in
|
|
4
|
+
* `range`: the check a bundle's every import passes, so a server choosing
|
|
5
|
+
* what to draw for a client can ask it first.
|
|
6
|
+
*/
|
|
7
|
+
export function supports(modules, specifier, range) {
|
|
8
|
+
const provided = modules[specifier];
|
|
9
|
+
return provided !== undefined && semver.satisfies(provided, range);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=modules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAQ5B;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,OAAgB,EAChB,SAAiB,EACjB,KAAa;IAEb,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,OAAO,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC"}
|
package/dist/print/code.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ClientImport } from "@backtickjs/core";
|
|
2
|
+
import { type Modules } from "../modules.js";
|
|
2
3
|
/**
|
|
3
4
|
* What a bundle imports, keyed by specifier and export, each bound once; and
|
|
4
|
-
* the
|
|
5
|
+
* the modules it may import them from, which the client provides, by version.
|
|
5
6
|
*/
|
|
6
7
|
export interface Names {
|
|
7
8
|
readonly imports: Map<string, {
|
|
@@ -9,9 +10,9 @@ export interface Names {
|
|
|
9
10
|
name: string;
|
|
10
11
|
local: string;
|
|
11
12
|
}>;
|
|
12
|
-
readonly
|
|
13
|
+
readonly modules: Modules;
|
|
13
14
|
}
|
|
14
|
-
export declare function createNames(
|
|
15
|
+
export declare function createNames(modules: Modules): Names;
|
|
15
16
|
export declare function string(value: string): string;
|
|
16
17
|
export declare function literal(value: string | number | bigint | boolean | null | undefined): string;
|
|
17
18
|
export declare function call(callee: string, args: readonly string[]): string;
|
package/dist/print/code.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { supports } from "../modules.js";
|
|
2
2
|
// What a bundle is written as, and the one place its conventions live: how a
|
|
3
3
|
// name is written, how a literal escapes, and how an element is written as JSX
|
|
4
4
|
// for the framework's compiler. Everything is code, as a string.
|
|
5
5
|
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
6
|
-
export function createNames(
|
|
7
|
-
return { imports: new Map(),
|
|
6
|
+
export function createNames(modules) {
|
|
7
|
+
return { imports: new Map(), modules };
|
|
8
8
|
}
|
|
9
9
|
// A string, as a literal: `<` escaped, so no `</script>` or `<!--` appears
|
|
10
10
|
// when the bundle is inlined in a page, and the line and paragraph separators,
|
|
@@ -50,26 +50,19 @@ function member(key) {
|
|
|
50
50
|
}
|
|
51
51
|
return IDENTIFIER.test(key) ? key : string(key);
|
|
52
52
|
}
|
|
53
|
-
// The package a module is in: its specifier's first segment, or first two
|
|
54
|
-
// when scoped (`solid-js/web` → `solid-js`, `@scope/pkg/x` → `@scope/pkg`).
|
|
55
|
-
function packageOf(from) {
|
|
56
|
-
const segments = from.split("/");
|
|
57
|
-
return segments.slice(0, from.startsWith("@") ? 2 : 1).join("/");
|
|
58
|
-
}
|
|
59
53
|
/** An export of a module the client provides, imported by the bundle. */
|
|
60
54
|
export function imported(names, { from, name, version }) {
|
|
61
55
|
// Where a script's import is written: a plugin's own (a framework's compile
|
|
62
56
|
// step) are the plugin's, trusted as it is.
|
|
63
|
-
const
|
|
64
|
-
const provided = names.packageVersions[pkg];
|
|
57
|
+
const provided = names.modules[from];
|
|
65
58
|
if (provided === undefined) {
|
|
66
|
-
const
|
|
59
|
+
const listed = Object.entries(names.modules).map(([specifier, at]) => `${specifier}@${at}`);
|
|
67
60
|
throw new Error(`Can't import \`${name}\` from "${from}": the client provides ` +
|
|
68
|
-
`${
|
|
61
|
+
`${listed.join(", ") || "no modules"}.`);
|
|
69
62
|
}
|
|
70
|
-
if (!
|
|
71
|
-
throw new Error(`Can't import \`${name}\` from "${from}": it needs ${
|
|
72
|
-
`
|
|
63
|
+
if (!supports(names.modules, from, version)) {
|
|
64
|
+
throw new Error(`Can't import \`${name}\` from "${from}": it needs ${version}, and ` +
|
|
65
|
+
`the client provides ${from}@${provided}.`);
|
|
73
66
|
}
|
|
74
67
|
const key = `${from}\0${name}`;
|
|
75
68
|
let entry = names.imports.get(key);
|
package/dist/print/code.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/print/code.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/print/code.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtickjs/bundler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Runs Backtick server components and bundles a screen per request: one JavaScript file for the client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"backtick",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"@backtickjs/core": "^0.1.9",
|
|
43
|
+
"semver": "^7.8.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/semver": "^7.8.0",
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type JsxElement,
|
|
8
8
|
} from "@backtickjs/core";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { expandServerComponents } from "./expandServerComponents.js";
|
|
11
11
|
import { bindingsOf, capturesOf } from "./params.js";
|
|
12
12
|
import { sourceName } from "./bindingKey.js";
|
|
13
13
|
import {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
object,
|
|
21
21
|
} from "../print/code.js";
|
|
22
22
|
import type { Names } from "../print/code.js";
|
|
23
|
+
import type { Modules } from "../modules.js";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* A bundle as it is built, before it is printed: each script's module it
|
|
@@ -28,8 +29,8 @@ import type { Names } from "../print/code.js";
|
|
|
28
29
|
*/
|
|
29
30
|
export interface BundleTree {
|
|
30
31
|
readonly modules: readonly (readonly [string, ClientModule])[];
|
|
31
|
-
// The functions the bundle makes once, each under its
|
|
32
|
-
// they're finished: after what they read.
|
|
32
|
+
// The functions and `once` values the bundle makes once, each under its
|
|
33
|
+
// label, in the order they're finished: after what they read.
|
|
33
34
|
readonly constants: readonly (readonly [string, string])[];
|
|
34
35
|
readonly root: string;
|
|
35
36
|
readonly names: Names;
|
|
@@ -69,9 +70,9 @@ const rootScope = (): Scope => ({ bindings: new Set(), read: new Set() });
|
|
|
69
70
|
// unique name, so there is nothing to disambiguate and nothing to rename.
|
|
70
71
|
export async function buildBundle(
|
|
71
72
|
value: unknown,
|
|
72
|
-
|
|
73
|
+
provided: Modules,
|
|
73
74
|
): Promise<BundleTree> {
|
|
74
|
-
const names = createNames(
|
|
75
|
+
const names = createNames(provided);
|
|
75
76
|
// Each script's number, in the order rendering first reaches it. Two
|
|
76
77
|
// scripts written at one source location are one declaration, so the first
|
|
77
78
|
// script with an id stands for all of them, and a reference to a shared
|
|
@@ -150,14 +151,14 @@ export async function buildBundle(
|
|
|
150
151
|
// `write` renders a closure's code in a scope that records what it reads:
|
|
151
152
|
// a constant's label if it read nothing of `scope` beyond `own`, its
|
|
152
153
|
// parameters, or else the code, written where it's used.
|
|
153
|
-
const closure =
|
|
154
|
+
const closure = (
|
|
154
155
|
scope: Scope,
|
|
155
156
|
own: readonly string[],
|
|
156
|
-
write: (inner: Scope) =>
|
|
157
|
+
write: (inner: Scope) => string,
|
|
157
158
|
name: string,
|
|
158
|
-
):
|
|
159
|
+
): string => {
|
|
159
160
|
const read = new Set<string>();
|
|
160
|
-
const code =
|
|
161
|
+
const code = write({
|
|
161
162
|
bindings: new Set([...scope.bindings, ...own]),
|
|
162
163
|
read,
|
|
163
164
|
});
|
|
@@ -177,52 +178,50 @@ export async function buildBundle(
|
|
|
177
178
|
return label;
|
|
178
179
|
};
|
|
179
180
|
|
|
181
|
+
// A `once` script is one value: its code run at the top of the bundle,
|
|
182
|
+
// where nothing encloses it, and every read is that constant. One per
|
|
183
|
+
// script, which is one per call of `once`, not per code: two runs of a host
|
|
184
|
+
// function that each make a context are two contexts, as two calls of
|
|
185
|
+
// `createContext` are. Running code is observable, so unlike a function it
|
|
186
|
+
// is never shared by code.
|
|
187
|
+
const values = new Map<ClientScript, string>();
|
|
188
|
+
const valueOf = (script: ClientScript): string => {
|
|
189
|
+
let label = values.get(script);
|
|
190
|
+
if (label === undefined) {
|
|
191
|
+
const code = call(
|
|
192
|
+
labelOf(scriptFor(script)),
|
|
193
|
+
exprCallArgs(script, rootScope()),
|
|
194
|
+
);
|
|
195
|
+
label = `$value${constants.length}`;
|
|
196
|
+
values.set(script, label);
|
|
197
|
+
constants.push([label, code]);
|
|
198
|
+
}
|
|
199
|
+
return label;
|
|
200
|
+
};
|
|
201
|
+
|
|
180
202
|
// Writes a value as the code it becomes: composition as data, which is what
|
|
181
203
|
// a bundle is. A script reference is a call naming which script and
|
|
182
204
|
// what to hand it; everything else is its literal form. Rendered in order,
|
|
183
205
|
// one value after the other, so the table follows the order rendering first
|
|
184
|
-
// reached each script.
|
|
185
|
-
|
|
186
|
-
// itself, which written out would never end. A value used in two places
|
|
187
|
-
// isn't one: it's left before it's met again.
|
|
188
|
-
const path = new Set<object>();
|
|
189
|
-
const render = async (
|
|
190
|
-
value: unknown,
|
|
191
|
-
scope: Scope = rootScope(),
|
|
192
|
-
): Promise<string> => {
|
|
193
|
-
if (typeof value !== "object" || value === null) {
|
|
194
|
-
return renderValue(value, scope);
|
|
195
|
-
}
|
|
196
|
-
if (path.has(value)) {
|
|
197
|
-
throw new Error(
|
|
198
|
-
"Can't splice a value that contains itself: a bundle writes each " +
|
|
199
|
-
"value out in full, so a cycle never ends. Break the cycle before " +
|
|
200
|
-
"splicing it.",
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
path.add(value);
|
|
204
|
-
try {
|
|
205
|
-
return await renderValue(value, scope);
|
|
206
|
-
} finally {
|
|
207
|
-
path.delete(value);
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
const renderValue = async (value: unknown, scope: Scope): Promise<string> => {
|
|
206
|
+
// reached each script. Expansion has refused cycles, so it ends.
|
|
207
|
+
const render = (value: unknown, scope: Scope = rootScope()): string => {
|
|
211
208
|
if (isClientScript(value)) {
|
|
209
|
+
if (value.once) {
|
|
210
|
+
return valueOf(value);
|
|
211
|
+
}
|
|
212
212
|
const target = scriptFor(value);
|
|
213
213
|
if (target.module.kind !== "function") {
|
|
214
|
-
return call(labelOf(target),
|
|
214
|
+
return call(labelOf(target), exprCallArgs(value, scope));
|
|
215
215
|
}
|
|
216
216
|
return closure(
|
|
217
217
|
scope,
|
|
218
218
|
[],
|
|
219
|
-
|
|
220
|
-
call(labelOf(target), await exprCallArgs(value, inner)),
|
|
219
|
+
(inner) => call(labelOf(target), exprCallArgs(value, inner)),
|
|
221
220
|
"$function",
|
|
222
221
|
);
|
|
223
222
|
}
|
|
224
223
|
if (isJsxElement(value)) {
|
|
225
|
-
return renderJsx(value
|
|
224
|
+
return renderJsx(value);
|
|
226
225
|
}
|
|
227
226
|
if (isClientImport(value)) {
|
|
228
227
|
return imported(names, value);
|
|
@@ -240,7 +239,7 @@ export async function buildBundle(
|
|
|
240
239
|
if (Array.isArray(value)) {
|
|
241
240
|
const elements: string[] = [];
|
|
242
241
|
for (const element of value) {
|
|
243
|
-
elements.push(
|
|
242
|
+
elements.push(render(element, scope));
|
|
244
243
|
}
|
|
245
244
|
return array(elements);
|
|
246
245
|
}
|
|
@@ -281,7 +280,7 @@ export async function buildBundle(
|
|
|
281
280
|
}
|
|
282
281
|
const entries: [string, string][] = [];
|
|
283
282
|
for (const [key, entry] of Object.entries(value)) {
|
|
284
|
-
entries.push([key,
|
|
283
|
+
entries.push([key, render(entry, scope)]);
|
|
285
284
|
}
|
|
286
285
|
return object(entries);
|
|
287
286
|
};
|
|
@@ -302,16 +301,15 @@ export async function buildBundle(
|
|
|
302
301
|
sourceName(key) +
|
|
303
302
|
"` shadows it: the binding is still there, but no longer reachable by " +
|
|
304
303
|
"name, and naming it anyway would mean emitting what the source " +
|
|
305
|
-
"couldn't say."
|
|
304
|
+
"couldn't say. A `once` script is made at the top of the bundle, " +
|
|
305
|
+
"outside every script, so a fragment spliced into one can't reach " +
|
|
306
|
+
"its binding either.",
|
|
306
307
|
);
|
|
307
308
|
};
|
|
308
309
|
|
|
309
310
|
// The arguments of a call of a script: a thunk per splice, then its
|
|
310
311
|
// captures.
|
|
311
|
-
const exprCallArgs =
|
|
312
|
-
ref: ClientScript,
|
|
313
|
-
scope: Scope,
|
|
314
|
-
): Promise<string[]> => {
|
|
312
|
+
const exprCallArgs = (ref: ClientScript, scope: Scope): string[] => {
|
|
315
313
|
const target = scriptFor(ref);
|
|
316
314
|
const parts: string[] = [];
|
|
317
315
|
// The splices lead the parameters, one `arg` each.
|
|
@@ -322,11 +320,10 @@ export async function buildBundle(
|
|
|
322
320
|
const passed = [...passKeys(target, index), ...capturesOf(target)];
|
|
323
321
|
// A thunk taking them, the splice rendered inside it.
|
|
324
322
|
parts.push(
|
|
325
|
-
|
|
323
|
+
closure(
|
|
326
324
|
scope,
|
|
327
325
|
passed,
|
|
328
|
-
|
|
329
|
-
arrow(passed.map(displayName), await render(arg, inner)),
|
|
326
|
+
(inner) => arrow(passed.map(displayName), render(arg, inner)),
|
|
330
327
|
"$thunk",
|
|
331
328
|
),
|
|
332
329
|
);
|
|
@@ -337,18 +334,10 @@ export async function buildBundle(
|
|
|
337
334
|
return parts;
|
|
338
335
|
};
|
|
339
336
|
|
|
340
|
-
// A tag
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
//
|
|
344
|
-
// A prop that is `undefined` is left out, as JSX and TypeScript's optional
|
|
345
|
-
// props read it: a component forwarding an optional prop it wasn't given
|
|
346
|
-
// writes nothing.
|
|
347
|
-
const renderJsx = async (jsx: JsxElement, scope: Scope): Promise<string> => {
|
|
337
|
+
// A tag left after expansion, which ran every server component's: one the
|
|
338
|
+
// client draws.
|
|
339
|
+
const renderJsx = (jsx: JsxElement): never => {
|
|
348
340
|
const type = jsx.type;
|
|
349
|
-
if (typeof type === "function" && !isClientImport(type)) {
|
|
350
|
-
return render(await expandJsxElement(jsx, type), scope);
|
|
351
|
-
}
|
|
352
341
|
// An element and a client component (an import, or a script answering
|
|
353
342
|
// one) are client code, so on the host they can't be tags: they are tags
|
|
354
343
|
// in a script.
|
|
@@ -365,8 +354,9 @@ export async function buildBundle(
|
|
|
365
354
|
);
|
|
366
355
|
};
|
|
367
356
|
|
|
368
|
-
//
|
|
369
|
-
|
|
357
|
+
// Every server component first, so siblings run at once; then the drawing,
|
|
358
|
+
// in order. Nothing encloses the root, so nothing it holds can capture.
|
|
359
|
+
const root = render(await expandServerComponents(value));
|
|
370
360
|
// In table order, which is the order rendering first reached each script.
|
|
371
361
|
const modules = [...numbers.keys()].map(
|
|
372
362
|
(script) => [labelOf(script), script.module] as const,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ClientScript,
|
|
3
|
+
isClientImport,
|
|
4
|
+
isClientScript,
|
|
5
|
+
isJsxElement,
|
|
6
|
+
} from "@backtickjs/core";
|
|
7
|
+
|
|
8
|
+
const CYCLE_ERROR =
|
|
9
|
+
"Can't splice a value that contains itself: a bundle writes each value " +
|
|
10
|
+
"out in full, so a cycle never ends. Break the cycle before splicing it.";
|
|
11
|
+
|
|
12
|
+
// The objects a value is inside of, innermost first. Each branch has its own,
|
|
13
|
+
// so siblings expanding at once never see each other's.
|
|
14
|
+
interface Ancestors {
|
|
15
|
+
readonly value: object;
|
|
16
|
+
readonly parent: Ancestors | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const contains = (ancestors: Ancestors | null, value: object): boolean => {
|
|
20
|
+
for (let at = ancestors; at !== null; at = at.parent) {
|
|
21
|
+
if (at.value === value) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Runs every server component in a value and answers with the value as drawn:
|
|
29
|
+
// each element of one replaced by what its component drew, expanded in turn.
|
|
30
|
+
// Siblings expand at once, so two components awaiting data wait together.
|
|
31
|
+
//
|
|
32
|
+
// A component runs each time its element is drawn, as React renders an element
|
|
33
|
+
// each place it stands: the component itself never leaves the host, and only
|
|
34
|
+
// the client code it bottoms out in reaches the bundle. A value holding no
|
|
35
|
+
// server component comes back as the same object.
|
|
36
|
+
//
|
|
37
|
+
// A `once` script is one value, so what it splices is expanded once, however
|
|
38
|
+
// many places read it: a server component inside it runs once. `values` holds
|
|
39
|
+
// each one's expansion for the call.
|
|
40
|
+
export async function expandServerComponents(
|
|
41
|
+
value: unknown,
|
|
42
|
+
ancestors: Ancestors | null = null,
|
|
43
|
+
values = new Map<ClientScript, Promise<unknown>>(),
|
|
44
|
+
): Promise<unknown> {
|
|
45
|
+
if (typeof value !== "object" || value === null) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
if (contains(ancestors, value)) {
|
|
49
|
+
throw new Error(CYCLE_ERROR);
|
|
50
|
+
}
|
|
51
|
+
const inside: Ancestors = { value, parent: ancestors };
|
|
52
|
+
const all = (entries: readonly unknown[]) =>
|
|
53
|
+
Promise.all(
|
|
54
|
+
entries.map((entry) => expandServerComponents(entry, inside, values)),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (isJsxElement(value)) {
|
|
58
|
+
const { type } = value;
|
|
59
|
+
// A tag the client draws is left for rendering to refuse.
|
|
60
|
+
if (typeof type !== "function" || isClientImport(type)) {
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
return expandServerComponents(
|
|
64
|
+
await type(value.props as never),
|
|
65
|
+
inside,
|
|
66
|
+
values,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (isClientScript(value)) {
|
|
70
|
+
const expanded = async (): Promise<unknown> => {
|
|
71
|
+
const args = await all(value.args);
|
|
72
|
+
return args.every((arg, index) => arg === value.args[index])
|
|
73
|
+
? value
|
|
74
|
+
: { ...value, args };
|
|
75
|
+
};
|
|
76
|
+
if (!value.once) {
|
|
77
|
+
return expanded();
|
|
78
|
+
}
|
|
79
|
+
let made = values.get(value);
|
|
80
|
+
if (made === undefined) {
|
|
81
|
+
made = expanded();
|
|
82
|
+
values.set(value, made);
|
|
83
|
+
}
|
|
84
|
+
return made;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
const elements = await all(value);
|
|
88
|
+
return elements.every((element, index) => element === value[index])
|
|
89
|
+
? value
|
|
90
|
+
: elements;
|
|
91
|
+
}
|
|
92
|
+
// Only plain objects cross; rendering refuses anything else.
|
|
93
|
+
const prototype = Object.getPrototypeOf(value);
|
|
94
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
const entries = Object.entries(value);
|
|
98
|
+
const expanded = await all(entries.map(([, entry]) => entry));
|
|
99
|
+
if (expanded.every((entry, index) => entry === entries[index]![1])) {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
// Defined, not assigned, so a `__proto__` key stays a key.
|
|
103
|
+
return Object.fromEntries(
|
|
104
|
+
entries.map(([key], index) => [key, expanded[index]]),
|
|
105
|
+
);
|
|
106
|
+
}
|
package/src/bundler.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { Spliceable, SplicesAs } from "@backtickjs/core";
|
|
2
2
|
import { buildBundle } from "./bundle/buildBundle.js";
|
|
3
|
+
import type { Modules } from "./modules.js";
|
|
3
4
|
import { printBundle } from "./print/printBundle.js";
|
|
4
5
|
|
|
5
6
|
export interface BuildOptions<T = Spliceable> {
|
|
6
7
|
// What to bundle: what a splice could carry, checked as a splice checks it.
|
|
7
8
|
readonly input: T;
|
|
8
|
-
// The
|
|
9
|
-
// the bundle, which requires them. A script's
|
|
10
|
-
// needing a version it doesn't have, throws.
|
|
11
|
-
readonly
|
|
9
|
+
// The modules the client provides, by specifier, each at its package's
|
|
10
|
+
// exact version: left out of the bundle, which requires them. A script's
|
|
11
|
+
// import from any other, or needing a version it doesn't have, throws.
|
|
12
|
+
readonly modules: Modules;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export interface OutputOptions {
|
|
@@ -37,7 +38,7 @@ export interface Bundle {
|
|
|
37
38
|
* What runs your components and hands back what they drew, shaped as Rollup
|
|
38
39
|
* is: a build, then output from it.
|
|
39
40
|
*
|
|
40
|
-
* const bundle = await bundler.build({ input: <Home />,
|
|
41
|
+
* const bundle = await bundler.build({ input: <Home />, modules });
|
|
41
42
|
* const { code, map } = bundle.generate({ format: "es", sourcemap: "hidden" });
|
|
42
43
|
*
|
|
43
44
|
* The module's default export is the value (CommonJS: `module.exports`). A
|
|
@@ -47,11 +48,11 @@ export interface Bundle {
|
|
|
47
48
|
export const bundler = {
|
|
48
49
|
async build<T extends [T] extends [SplicesAs<T>] ? unknown : Spliceable>({
|
|
49
50
|
input,
|
|
50
|
-
|
|
51
|
+
modules,
|
|
51
52
|
}: BuildOptions<T>): Promise<Bundle> {
|
|
52
53
|
// Scripts come compiled for their framework when their host was built, so
|
|
53
54
|
// the bundle is only put together: nothing is compiled or parsed here.
|
|
54
|
-
const tree = await buildBundle(input,
|
|
55
|
+
const tree = await buildBundle(input, modules);
|
|
55
56
|
return {
|
|
56
57
|
generate: ({ format, sourcemap = false }) => {
|
|
57
58
|
const { code, map } = printBundle(tree, format);
|
package/src/index.ts
CHANGED
package/src/modules.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The modules a client provides, by specifier, each at the exact version of
|
|
5
|
+
* the package it comes from: `{ "react/jsx-runtime": "19.2.3" }`.
|
|
6
|
+
*/
|
|
7
|
+
export type Modules = Readonly<Record<string, string>>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Whether a client with `modules` provides `specifier` at a version in
|
|
11
|
+
* `range`: the check a bundle's every import passes, so a server choosing
|
|
12
|
+
* what to draw for a client can ask it first.
|
|
13
|
+
*/
|
|
14
|
+
export function supports(
|
|
15
|
+
modules: Modules,
|
|
16
|
+
specifier: string,
|
|
17
|
+
range: string,
|
|
18
|
+
): boolean {
|
|
19
|
+
const provided = modules[specifier];
|
|
20
|
+
return provided !== undefined && semver.satisfies(provided, range);
|
|
21
|
+
}
|
package/src/print/code.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ClientImport } from "@backtickjs/core";
|
|
2
|
-
import
|
|
2
|
+
import { type Modules, supports } from "../modules.js";
|
|
3
3
|
|
|
4
4
|
// What a bundle is written as, and the one place its conventions live: how a
|
|
5
5
|
// name is written, how a literal escapes, and how an element is written as JSX
|
|
@@ -9,17 +9,15 @@ const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* What a bundle imports, keyed by specifier and export, each bound once; and
|
|
12
|
-
* the
|
|
12
|
+
* the modules it may import them from, which the client provides, by version.
|
|
13
13
|
*/
|
|
14
14
|
export interface Names {
|
|
15
15
|
readonly imports: Map<string, { from: string; name: string; local: string }>;
|
|
16
|
-
readonly
|
|
16
|
+
readonly modules: Modules;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function createNames(
|
|
20
|
-
|
|
21
|
-
): Names {
|
|
22
|
-
return { imports: new Map(), packageVersions };
|
|
19
|
+
export function createNames(modules: Modules): Names {
|
|
20
|
+
return { imports: new Map(), modules };
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
// A string, as a literal: `<` escaped, so no `</script>` or `<!--` appears
|
|
@@ -80,13 +78,6 @@ function member(key: string): string {
|
|
|
80
78
|
return IDENTIFIER.test(key) ? key : string(key);
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
// The package a module is in: its specifier's first segment, or first two
|
|
84
|
-
// when scoped (`solid-js/web` → `solid-js`, `@scope/pkg/x` → `@scope/pkg`).
|
|
85
|
-
function packageOf(from: string): string {
|
|
86
|
-
const segments = from.split("/");
|
|
87
|
-
return segments.slice(0, from.startsWith("@") ? 2 : 1).join("/");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
81
|
/** An export of a module the client provides, imported by the bundle. */
|
|
91
82
|
export function imported(
|
|
92
83
|
names: Names,
|
|
@@ -94,21 +85,20 @@ export function imported(
|
|
|
94
85
|
): string {
|
|
95
86
|
// Where a script's import is written: a plugin's own (a framework's compile
|
|
96
87
|
// step) are the plugin's, trusted as it is.
|
|
97
|
-
const
|
|
98
|
-
const provided = names.packageVersions[pkg];
|
|
88
|
+
const provided = names.modules[from];
|
|
99
89
|
if (provided === undefined) {
|
|
100
|
-
const
|
|
101
|
-
([
|
|
90
|
+
const listed = Object.entries(names.modules).map(
|
|
91
|
+
([specifier, at]) => `${specifier}@${at}`,
|
|
102
92
|
);
|
|
103
93
|
throw new Error(
|
|
104
94
|
`Can't import \`${name}\` from "${from}": the client provides ` +
|
|
105
|
-
`${
|
|
95
|
+
`${listed.join(", ") || "no modules"}.`,
|
|
106
96
|
);
|
|
107
97
|
}
|
|
108
|
-
if (!
|
|
98
|
+
if (!supports(names.modules, from, version)) {
|
|
109
99
|
throw new Error(
|
|
110
|
-
`Can't import \`${name}\` from "${from}": it needs ${
|
|
111
|
-
`
|
|
100
|
+
`Can't import \`${name}\` from "${from}": it needs ${version}, and ` +
|
|
101
|
+
`the client provides ${from}@${provided}.`,
|
|
112
102
|
);
|
|
113
103
|
}
|
|
114
104
|
const key = `${from}\0${name}`;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Runs a host component and answers with what it drew, each time an element
|
|
2
|
-
// is drawn, as React renders an element each place it stands. The component
|
|
3
|
-
// itself never leaves the host: a server component expands away here, and
|
|
4
|
-
// only the client component it bottoms out in reaches the bundle.
|
|
5
|
-
export async function expandJsxElement(jsx, component) {
|
|
6
|
-
return component(jsx.props);
|
|
7
|
-
}
|
|
8
|
-
//# sourceMappingURL=expandJsxElement.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"expandJsxElement.js","sourceRoot":"","sources":["../../src/bundle/expandJsxElement.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,4EAA4E;AAC5E,0EAA0E;AAC1E,kEAAkE;AAClE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAe,EACf,SAAoC;IAEpC,OAAO,SAAS,CAAC,GAAG,CAAC,KAAc,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { JsxElement } from "@backtickjs/core";
|
|
2
|
-
|
|
3
|
-
// Runs a host component and answers with what it drew, each time an element
|
|
4
|
-
// is drawn, as React renders an element each place it stands. The component
|
|
5
|
-
// itself never leaves the host: a server component expands away here, and
|
|
6
|
-
// only the client component it bottoms out in reaches the bundle.
|
|
7
|
-
export async function expandJsxElement(
|
|
8
|
-
jsx: JsxElement,
|
|
9
|
-
component: (props: never) => unknown,
|
|
10
|
-
): Promise<unknown> {
|
|
11
|
-
return component(jsx.props as never);
|
|
12
|
-
}
|