@backtickjs/core 0.2.1 → 0.2.3
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 +24 -20
- package/dist/modulesOf.d.ts +8 -11
- package/dist/modulesOf.js +9 -15
- package/dist/modulesOf.js.map +1 -1
- package/dist/node.d.ts +5 -10
- package/dist/node.js +5 -10
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/src/modulesOf.ts +9 -17
- package/src/node.ts +5 -10
package/README.md
CHANGED
|
@@ -58,36 +58,40 @@ import { greet } from "my-native-module" with { backtick: "client" };
|
|
|
58
58
|
|
|
59
59
|
The package must be installed where the server's file finds it, for its types and its version. The client must provide it, and the server must list it in the bundler's `modules` at that version or a later one of the same major. The compiler writes each name as what `createImport({ name, from, version })` makes, which a hand-written import can call too.
|
|
60
60
|
|
|
61
|
+
To draw for clients on two majors of a package, install both, one under an alias: `"acme-ui-2": "npm:acme-ui@2"`. An import from either is of `acme-ui`, the module clients provide, typed against its own version; `supports`, in a server component, picks the one the client can run:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { Button } from "acme-ui" with { backtick: "client" };
|
|
65
|
+
import { Button as Button2 } from "acme-ui-2" with { backtick: "client" };
|
|
66
|
+
|
|
67
|
+
function Save() {
|
|
68
|
+
return supports("acme-ui", "^2.0.0")
|
|
69
|
+
? cs`<$Button2 tone="primary">Save</$Button2>`
|
|
70
|
+
: cs`<$Button variant="primary">Save</$Button>`;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
61
74
|
## Every build in users' hands
|
|
62
75
|
|
|
63
76
|
A store build carries the modules it was built with, at their versions, for as long as it's installed, while your server moves on. Each build sends the modules it carries, and the server bundles its screens for exactly those: there's nothing to record before a build ships, and any version of your server can draw for any build.
|
|
64
77
|
|
|
65
78
|
1. **The app's module map** offers what screens may require, each from a package in `package.json`'s dependencies. A platform that differs gets its own file, as Metro picks files: `client/moduleMap.web.ts` for the web.
|
|
66
79
|
|
|
67
|
-
2. **The app's
|
|
80
|
+
2. **The app's build records its installed packages.** `installedPackages`, from `@backtickjs/core/node`, reads the installed version of each of the app's dependencies from `node_modules`, and the build embeds them in the app. In an Expo app, the config plugin from [`@backtickjs/expo`](https://github.com/backtickjs/backtick/tree/main/packages/expo) does it:
|
|
68
81
|
|
|
69
|
-
```
|
|
70
|
-
// app.
|
|
71
|
-
|
|
72
|
-
import type { ConfigContext, ExpoConfig } from "expo/config";
|
|
73
|
-
|
|
74
|
-
export default ({ config, projectRoot }: ConfigContext): ExpoConfig => ({
|
|
75
|
-
...(config as ExpoConfig),
|
|
76
|
-
extra: {
|
|
77
|
-
...config.extra,
|
|
78
|
-
backtick: { packages: installedPackages(projectRoot) },
|
|
79
|
-
},
|
|
80
|
-
});
|
|
82
|
+
```json
|
|
83
|
+
// app.json
|
|
84
|
+
{ "expo": { "plugins": ["@backtickjs/expo"] } }
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
3. **The app sends its modules** however it reaches its server: a header, a GraphQL variable. `modulesOf` gives each module in the map its package's version, and throws as the app starts
|
|
87
|
+
3. **The app sends its modules** however it reaches its server: a header, a GraphQL variable. `modulesOf` gives each module in the map its package's version, and throws as the app starts for a module from a package that isn't a dependency:
|
|
84
88
|
|
|
85
89
|
```ts
|
|
86
90
|
import { modulesOf } from "@backtickjs/core";
|
|
91
|
+
import { getBacktickExtra } from "@backtickjs/expo";
|
|
87
92
|
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
);
|
|
93
|
+
const { installedPackages } = getBacktickExtra(Constants.expoConfig);
|
|
94
|
+
const modules = JSON.stringify(modulesOf(moduleMap, installedPackages));
|
|
91
95
|
fetch(url, { headers: { "x-backtick-modules": modules } });
|
|
92
96
|
```
|
|
93
97
|
|
|
@@ -98,7 +102,7 @@ A store build carries the modules it was built with, at their versions, for as l
|
|
|
98
102
|
const bundle = await bundler.build({ input, modules });
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
The bundler refuses modules that aren't `{ "specifier": "version" }`, and a screen that imports a module the build doesn't carry, or at a version it can't run. To draw differently for builds that lack a module, or to ask old ones to update, check with `supports` from [`@backtickjs/bundler`](https://github.com/backtickjs/backtick/tree/main/packages/bundler): `supports(
|
|
105
|
+
The bundler refuses modules that aren't `{ "specifier": "version" }`, and a screen that imports a module the build doesn't carry, or at a version it can't run. To draw differently for builds that lack a module, or to ask old ones to update, check with `supports` from [`@backtickjs/bundler`](https://github.com/backtickjs/backtick/tree/main/packages/bundler), in a server component: `supports("react-native", ">=0.86")`.
|
|
102
106
|
|
|
103
107
|
In development, a reload keeps the app config the app was opened with, and its versions with it: after upgrading a package, close the app and open it again.
|
|
104
108
|
|
|
@@ -116,8 +120,8 @@ A test draws a screen with a module map of its own, stand-ins included; its modu
|
|
|
116
120
|
- `Bundle<T>`: a bundle's code, branded with what it runs to. `bundler.build(…).generate(…)` writes one.
|
|
117
121
|
- `execRoot(bundle, moduleMap)`, from `@backtickjs/core/cjs`: in a React Native app, runs the CommonJS bundle it starts from to its value, as a promise, with `require` answered from `moduleMap`, a `ModuleMap`: the modules the app provides, by specifier.
|
|
118
122
|
- `execRoot(bundle)`, from `@backtickjs/core/es`: in a web page, imports the ES bundle it starts from and resolves to its value, the page's import map answering its imports. Each format has its own entry, so a React Native app's bundler never reads the ES one's `import()`.
|
|
119
|
-
- `modulesOf(moduleMap,
|
|
120
|
-
- `installedPackages(projectRoot)`, from `@backtickjs/core/node`: the installed version of each of the app's dependencies, read from `node_modules
|
|
123
|
+
- `modulesOf(moduleMap, installedPackages)`: the modules `moduleMap`, a `ModuleMap`, offers, each at its package's version in `installedPackages`, for the app to send its server.
|
|
124
|
+
- `installedPackages(projectRoot)`, from `@backtickjs/core/node`: the installed version of each of the app's dependencies, read from `node_modules` as Metro finds packages by default. A `metro.config.js` that aliases or redirects packages needs its own versions. Node only.
|
|
121
125
|
- `exec`: in a script, `$exec(bundle)` runs another bundle as the script's own was run, with the same modules, to a promise of its value.
|
|
122
126
|
- `isClientExec`, `ClientExec`: its check and type.
|
|
123
127
|
- `SplicesAs<T>`: `T` as a splice checks it, member by member. Used by the bundler to check its input.
|
package/dist/modulesOf.d.ts
CHANGED
|
@@ -15,16 +15,13 @@
|
|
|
15
15
|
*/
|
|
16
16
|
export type ModuleMap = Readonly<Record<string, unknown>>;
|
|
17
17
|
/**
|
|
18
|
-
* The modules `moduleMap` offers, each at the version in `
|
|
19
|
-
* package it comes from: what the app sends however it reaches its
|
|
20
|
-
* header, a GraphQL variable, for the server to bundle screens it
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* The modules `moduleMap` offers, each at the version in `installedPackages`
|
|
19
|
+
* of the package it comes from: what the app sends however it reaches its
|
|
20
|
+
* server, a header, a GraphQL variable, for the server to bundle screens it
|
|
21
|
+
* can run. The app's build records them, with `installedPackages` from
|
|
22
|
+
* `@backtickjs/core/node`. Throws as the app starts for a module from a
|
|
23
|
+
* package that isn't a dependency.
|
|
24
24
|
*
|
|
25
|
-
* const modules = modulesOf(
|
|
26
|
-
* moduleMap,
|
|
27
|
-
* Constants.expoConfig?.extra?.backtick?.packages,
|
|
28
|
-
* );
|
|
25
|
+
* const modules = modulesOf(moduleMap, installedPackages);
|
|
29
26
|
*/
|
|
30
|
-
export declare function modulesOf(moduleMap: ModuleMap,
|
|
27
|
+
export declare function modulesOf(moduleMap: ModuleMap, installedPackages: Readonly<Record<string, string>>): Record<string, string>;
|
package/dist/modulesOf.js
CHANGED
|
@@ -4,24 +4,18 @@ function packageOf(specifier) {
|
|
|
4
4
|
return segments.slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* The modules `moduleMap` offers, each at the version in `
|
|
8
|
-
* package it comes from: what the app sends however it reaches its
|
|
9
|
-
* header, a GraphQL variable, for the server to bundle screens it
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* The modules `moduleMap` offers, each at the version in `installedPackages`
|
|
8
|
+
* of the package it comes from: what the app sends however it reaches its
|
|
9
|
+
* server, a header, a GraphQL variable, for the server to bundle screens it
|
|
10
|
+
* can run. The app's build records them, with `installedPackages` from
|
|
11
|
+
* `@backtickjs/core/node`. Throws as the app starts for a module from a
|
|
12
|
+
* package that isn't a dependency.
|
|
13
13
|
*
|
|
14
|
-
* const modules = modulesOf(
|
|
15
|
-
* moduleMap,
|
|
16
|
-
* Constants.expoConfig?.extra?.backtick?.packages,
|
|
17
|
-
* );
|
|
14
|
+
* const modules = modulesOf(moduleMap, installedPackages);
|
|
18
15
|
*/
|
|
19
|
-
export function modulesOf(moduleMap,
|
|
20
|
-
if (packages == null) {
|
|
21
|
-
throw new Error("No packages: does app.config.ts put installedPackages(projectRoot) in the config's extra?");
|
|
22
|
-
}
|
|
16
|
+
export function modulesOf(moduleMap, installedPackages) {
|
|
23
17
|
return Object.fromEntries(Object.keys(moduleMap).map((specifier) => {
|
|
24
|
-
const version =
|
|
18
|
+
const version = installedPackages[packageOf(specifier)];
|
|
25
19
|
if (version === undefined) {
|
|
26
20
|
throw new Error(`The module map offers ${JSON.stringify(specifier)}, but ${packageOf(specifier)} isn't in package.json's dependencies.`);
|
|
27
21
|
}
|
package/dist/modulesOf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modulesOf.js","sourceRoot":"","sources":["../src/modulesOf.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,SAAS,SAAS,CAAC,SAAiB;IAClC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"modulesOf.js","sourceRoot":"","sources":["../src/modulesOf.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,SAAS,SAAS,CAAC,SAAiB;IAClC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CACvB,SAAoB,EACpB,iBAAmD;IAEnD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;QACvC,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;QACxD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,yBAAyB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,SAAS,SAAS,CAAC,SAAS,CAAC,wCAAwC,CACxH,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
package/dist/node.d.ts
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The installed version of each of the app's dependencies, at `projectRoot`:
|
|
3
|
-
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* extra: {
|
|
9
|
-
* ...config.extra,
|
|
10
|
-
* backtick: { packages: installedPackages(projectRoot) },
|
|
11
|
-
* },
|
|
12
|
-
* });
|
|
3
|
+
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
4
|
+
* the app's build calls it, and embeds what it answers in the app.
|
|
5
|
+
* Packages are found as Metro finds them by default, in `node_modules` here
|
|
6
|
+
* or above: a `metro.config.js` that aliases or redirects them needs its own
|
|
7
|
+
* versions.
|
|
13
8
|
*/
|
|
14
9
|
export declare function installedPackages(projectRoot: string): Record<string, string>;
|
package/dist/node.js
CHANGED
|
@@ -14,16 +14,11 @@ function installed(projectRoot, name) {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* The installed version of each of the app's dependencies, at `projectRoot`:
|
|
17
|
-
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* extra: {
|
|
23
|
-
* ...config.extra,
|
|
24
|
-
* backtick: { packages: installedPackages(projectRoot) },
|
|
25
|
-
* },
|
|
26
|
-
* });
|
|
17
|
+
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
18
|
+
* the app's build calls it, and embeds what it answers in the app.
|
|
19
|
+
* Packages are found as Metro finds them by default, in `node_modules` here
|
|
20
|
+
* or above: a `metro.config.js` that aliases or redirects them needs its own
|
|
21
|
+
* versions.
|
|
27
22
|
*/
|
|
28
23
|
export function installedPackages(projectRoot) {
|
|
29
24
|
const { dependencies = {} } = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,2EAA2E;AAC3E,2EAA2E;AAC3E,SAAS,SAAS,CAAC,WAAmB,EAAE,IAAY;IAClD,KAAK,IAAI,SAAS,GAAG,WAAW,GAAI,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACnE,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,GAAG,WAAW,KAAK,IAAI,kBAAkB,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,2EAA2E;AAC3E,2EAA2E;AAC3E,SAAS,SAAS,CAAC,WAAmB,EAAE,IAAY;IAClD,KAAK,IAAI,SAAS,GAAG,WAAW,GAAI,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;QACnE,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,GAAG,WAAW,KAAK,IAAI,kBAAkB,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAmB;IACnD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,KAAK,CACtC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CACX,CAAC;IAC/C,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI;QAEF,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAG9D,CAAC,OAAO;KACV,CAAC,CACH,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
package/src/modulesOf.ts
CHANGED
|
@@ -22,30 +22,22 @@ function packageOf(specifier: string): string {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* The modules `moduleMap` offers, each at the version in `
|
|
26
|
-
* package it comes from: what the app sends however it reaches its
|
|
27
|
-
* header, a GraphQL variable, for the server to bundle screens it
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* The modules `moduleMap` offers, each at the version in `installedPackages`
|
|
26
|
+
* of the package it comes from: what the app sends however it reaches its
|
|
27
|
+
* server, a header, a GraphQL variable, for the server to bundle screens it
|
|
28
|
+
* can run. The app's build records them, with `installedPackages` from
|
|
29
|
+
* `@backtickjs/core/node`. Throws as the app starts for a module from a
|
|
30
|
+
* package that isn't a dependency.
|
|
31
31
|
*
|
|
32
|
-
* const modules = modulesOf(
|
|
33
|
-
* moduleMap,
|
|
34
|
-
* Constants.expoConfig?.extra?.backtick?.packages,
|
|
35
|
-
* );
|
|
32
|
+
* const modules = modulesOf(moduleMap, installedPackages);
|
|
36
33
|
*/
|
|
37
34
|
export function modulesOf(
|
|
38
35
|
moduleMap: ModuleMap,
|
|
39
|
-
|
|
36
|
+
installedPackages: Readonly<Record<string, string>>,
|
|
40
37
|
): Record<string, string> {
|
|
41
|
-
if (packages == null) {
|
|
42
|
-
throw new Error(
|
|
43
|
-
"No packages: does app.config.ts put installedPackages(projectRoot) in the config's extra?",
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
38
|
return Object.fromEntries(
|
|
47
39
|
Object.keys(moduleMap).map((specifier) => {
|
|
48
|
-
const version =
|
|
40
|
+
const version = installedPackages[packageOf(specifier)];
|
|
49
41
|
if (version === undefined) {
|
|
50
42
|
throw new Error(
|
|
51
43
|
`The module map offers ${JSON.stringify(specifier)}, but ${packageOf(specifier)} isn't in package.json's dependencies.`,
|
package/src/node.ts
CHANGED
|
@@ -15,16 +15,11 @@ function installed(projectRoot: string, name: string): string {
|
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* The installed version of each of the app's dependencies, at `projectRoot`:
|
|
18
|
-
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* extra: {
|
|
24
|
-
* ...config.extra,
|
|
25
|
-
* backtick: { packages: installedPackages(projectRoot) },
|
|
26
|
-
* },
|
|
27
|
-
* });
|
|
18
|
+
* what a build made from it now carries there, for `modulesOf`. Node only:
|
|
19
|
+
* the app's build calls it, and embeds what it answers in the app.
|
|
20
|
+
* Packages are found as Metro finds them by default, in `node_modules` here
|
|
21
|
+
* or above: a `metro.config.js` that aliases or redirects them needs its own
|
|
22
|
+
* versions.
|
|
28
23
|
*/
|
|
29
24
|
export function installedPackages(projectRoot: string): Record<string, string> {
|
|
30
25
|
const { dependencies = {} } = JSON.parse(
|