@backtickjs/bundler 0.1.8 → 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 +25 -3
- package/dist/bundle/buildBundle.js.map +1 -1
- package/dist/bundle/expandServerComponents.d.ts +2 -1
- package/dist/bundle/expandServerComponents.js +23 -8
- package/dist/bundle/expandServerComponents.js.map +1 -1
- 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 +32 -5
- package/src/bundle/expandServerComponents.ts +35 -8
- 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/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>;
|
|
@@ -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
|
|
@@ -119,6 +119,23 @@ 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,
|
|
@@ -126,6 +143,9 @@ export async function buildBundle(value, packageVersions) {
|
|
|
126
143
|
// reached each script. Expansion has refused cycles, so it ends.
|
|
127
144
|
const render = (value, scope = rootScope()) => {
|
|
128
145
|
if (isClientScript(value)) {
|
|
146
|
+
if (value.once) {
|
|
147
|
+
return valueOf(value);
|
|
148
|
+
}
|
|
129
149
|
const target = scriptFor(value);
|
|
130
150
|
if (target.module.kind !== "function") {
|
|
131
151
|
return call(labelOf(target), exprCallArgs(value, scope));
|
|
@@ -203,7 +223,9 @@ export async function buildBundle(value, packageVersions) {
|
|
|
203
223
|
sourceName(key) +
|
|
204
224
|
"` shadows it: the binding is still there, but no longer reachable by " +
|
|
205
225
|
"name, and naming it anyway would mean emitting what the source " +
|
|
206
|
-
"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.");
|
|
207
229
|
};
|
|
208
230
|
// The arguments of a call of a script: a thunk per splice, then its
|
|
209
231
|
// captures.
|
|
@@ -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,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,OAAO,EACP,MAAM,GACP,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"buildBundle.js","sourceRoot":"","sources":["../../src/bundle/buildBundle.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type ClientScript } from "@backtickjs/core";
|
|
1
2
|
interface Ancestors {
|
|
2
3
|
readonly value: object;
|
|
3
4
|
readonly parent: Ancestors | null;
|
|
4
5
|
}
|
|
5
|
-
export declare function expandServerComponents(value: unknown, ancestors?: Ancestors | null): Promise<unknown>;
|
|
6
|
+
export declare function expandServerComponents(value: unknown, ancestors?: Ancestors | null, values?: Map<ClientScript, Promise<unknown>>): Promise<unknown>;
|
|
6
7
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isClientImport, isClientScript, isJsxElement } from "@backtickjs/core";
|
|
1
|
+
import { isClientImport, isClientScript, isJsxElement, } from "@backtickjs/core";
|
|
2
2
|
const CYCLE_ERROR = "Can't splice a value that contains itself: a bundle writes each value " +
|
|
3
3
|
"out in full, so a cycle never ends. Break the cycle before splicing it.";
|
|
4
4
|
const contains = (ancestors, value) => {
|
|
@@ -17,7 +17,11 @@ const contains = (ancestors, value) => {
|
|
|
17
17
|
// each place it stands: the component itself never leaves the host, and only
|
|
18
18
|
// the client code it bottoms out in reaches the bundle. A value holding no
|
|
19
19
|
// server component comes back as the same object.
|
|
20
|
-
|
|
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()) {
|
|
21
25
|
if (typeof value !== "object" || value === null) {
|
|
22
26
|
return value;
|
|
23
27
|
}
|
|
@@ -25,20 +29,31 @@ export async function expandServerComponents(value, ancestors = null) {
|
|
|
25
29
|
throw new Error(CYCLE_ERROR);
|
|
26
30
|
}
|
|
27
31
|
const inside = { value, parent: ancestors };
|
|
28
|
-
const all = (
|
|
32
|
+
const all = (entries) => Promise.all(entries.map((entry) => expandServerComponents(entry, inside, values)));
|
|
29
33
|
if (isJsxElement(value)) {
|
|
30
34
|
const { type } = value;
|
|
31
35
|
// A tag the client draws is left for rendering to refuse.
|
|
32
36
|
if (typeof type !== "function" || isClientImport(type)) {
|
|
33
37
|
return value;
|
|
34
38
|
}
|
|
35
|
-
return expandServerComponents(await type(value.props), inside);
|
|
39
|
+
return expandServerComponents(await type(value.props), inside, values);
|
|
36
40
|
}
|
|
37
41
|
if (isClientScript(value)) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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;
|
|
42
57
|
}
|
|
43
58
|
if (Array.isArray(value)) {
|
|
44
59
|
const elements = await all(value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expandServerComponents.js","sourceRoot":"","sources":["../../src/bundle/expandServerComponents.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
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",
|
|
@@ -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
|
|
@@ -177,6 +178,27 @@ 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,
|
|
@@ -184,6 +206,9 @@ export async function buildBundle(
|
|
|
184
206
|
// reached each script. Expansion has refused cycles, so it ends.
|
|
185
207
|
const render = (value: unknown, scope: Scope = rootScope()): string => {
|
|
186
208
|
if (isClientScript(value)) {
|
|
209
|
+
if (value.once) {
|
|
210
|
+
return valueOf(value);
|
|
211
|
+
}
|
|
187
212
|
const target = scriptFor(value);
|
|
188
213
|
if (target.module.kind !== "function") {
|
|
189
214
|
return call(labelOf(target), exprCallArgs(value, scope));
|
|
@@ -276,7 +301,9 @@ export async function buildBundle(
|
|
|
276
301
|
sourceName(key) +
|
|
277
302
|
"` shadows it: the binding is still there, but no longer reachable by " +
|
|
278
303
|
"name, and naming it anyway would mean emitting what the source " +
|
|
279
|
-
"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.",
|
|
280
307
|
);
|
|
281
308
|
};
|
|
282
309
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ClientScript,
|
|
3
|
+
isClientImport,
|
|
4
|
+
isClientScript,
|
|
5
|
+
isJsxElement,
|
|
6
|
+
} from "@backtickjs/core";
|
|
2
7
|
|
|
3
8
|
const CYCLE_ERROR =
|
|
4
9
|
"Can't splice a value that contains itself: a bundle writes each value " +
|
|
@@ -28,9 +33,14 @@ const contains = (ancestors: Ancestors | null, value: object): boolean => {
|
|
|
28
33
|
// each place it stands: the component itself never leaves the host, and only
|
|
29
34
|
// the client code it bottoms out in reaches the bundle. A value holding no
|
|
30
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.
|
|
31
40
|
export async function expandServerComponents(
|
|
32
41
|
value: unknown,
|
|
33
42
|
ancestors: Ancestors | null = null,
|
|
43
|
+
values = new Map<ClientScript, Promise<unknown>>(),
|
|
34
44
|
): Promise<unknown> {
|
|
35
45
|
if (typeof value !== "object" || value === null) {
|
|
36
46
|
return value;
|
|
@@ -39,8 +49,10 @@ export async function expandServerComponents(
|
|
|
39
49
|
throw new Error(CYCLE_ERROR);
|
|
40
50
|
}
|
|
41
51
|
const inside: Ancestors = { value, parent: ancestors };
|
|
42
|
-
const all = (
|
|
43
|
-
Promise.all(
|
|
52
|
+
const all = (entries: readonly unknown[]) =>
|
|
53
|
+
Promise.all(
|
|
54
|
+
entries.map((entry) => expandServerComponents(entry, inside, values)),
|
|
55
|
+
);
|
|
44
56
|
|
|
45
57
|
if (isJsxElement(value)) {
|
|
46
58
|
const { type } = value;
|
|
@@ -48,13 +60,28 @@ export async function expandServerComponents(
|
|
|
48
60
|
if (typeof type !== "function" || isClientImport(type)) {
|
|
49
61
|
return value;
|
|
50
62
|
}
|
|
51
|
-
return expandServerComponents(
|
|
63
|
+
return expandServerComponents(
|
|
64
|
+
await type(value.props as never),
|
|
65
|
+
inside,
|
|
66
|
+
values,
|
|
67
|
+
);
|
|
52
68
|
}
|
|
53
69
|
if (isClientScript(value)) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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;
|
|
58
85
|
}
|
|
59
86
|
if (Array.isArray(value)) {
|
|
60
87
|
const elements = await all(value);
|
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}`;
|