create-backtick-app 0.2.0 → 0.2.1
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 +2 -5
- package/dist/index.js +8 -1
- package/package.json +1 -1
- package/templates/react/package.json +6 -6
- package/templates/react-native/AGENTS.md +1 -1
- package/templates/react-native/app.config.ts +12 -0
- package/templates/react-native/app.json +1 -2
- package/templates/react-native/client/App.tsx +8 -7
- package/templates/react-native/client/moduleMap.ts +3 -3
- package/templates/react-native/package.json +6 -10
- package/templates/react-native/server/index.tsx +1 -1
- package/templates/react-native/server/screen.tsx +3 -11
- package/templates/react-native/server/test/drawScreen.ts +7 -5
- package/templates/react-native/tsconfig.json +2 -2
- package/templates/solid-js/package.json +7 -7
- package/templates/solid-js/server/html.ts +6 -6
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ yarn create backtick-app
|
|
|
17
17
|
bun create backtick-app
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
It asks for a name, and nothing else: it copies the React Native template into that directory, installs with the package manager you ran it with (npm, pnpm, Yarn or Bun), and prints the commands to start. The server runs on Node, or on Bun when you ran it with Bun (`bun create`). Given the name on the command line, it asks nothing. Requires Node 22.15 or later.
|
|
20
|
+
It asks for a name, and nothing else: it copies the React Native template into that directory, installs with the package manager you ran it with (npm, pnpm, Yarn or Bun), and prints the commands to start. The server runs on Node, or on Bun when you ran it with Bun (`bun create`) or passed `--runtime bun`; tests run on Node either way. Given the name on the command line, it asks nothing. Requires Node 22.15 or later.
|
|
21
21
|
|
|
22
22
|
## Options
|
|
23
23
|
|
|
@@ -52,10 +52,7 @@ Every template has:
|
|
|
52
52
|
|
|
53
53
|
An Expo app (SDK 57) with the Backtick server beside it in `server/`. `client/App.tsx` fetches the screen at `/` and runs it with `execRoot` from [`@backtickjs/core/cjs`](https://github.com/backtickjs/backtick/tree/main/packages/core), offering screens what `client/moduleMap.ts` lists.
|
|
54
54
|
|
|
55
|
-
Each build of the app sends
|
|
56
|
-
|
|
57
|
-
- The server draws only registered builds, one file each in `builds/`. `npm start` registers this code's builds as Expo starts; `npm run register-build` does it without Expo, as before a build for the stores. Commit what they write.
|
|
58
|
-
- On EAS, `eas-build-post-install` fails a preview or production build that isn't registered.
|
|
55
|
+
Each build of the app sends the modules it carries: those `client/moduleMap.ts` offers, at the versions `app.config.ts` records. The server bundles each screen for those modules ([more](https://github.com/backtickjs/backtick/tree/main/packages/core#every-build-in-users-hands)), so a build in users' hands keeps working as the app moves on, with nothing to record before it ships.
|
|
59
56
|
|
|
60
57
|
```sh
|
|
61
58
|
npm start # then scan the QR code with your phone, in Expo Go
|
package/dist/index.js
CHANGED
|
@@ -142,8 +142,8 @@ rewriteJson(path.join(target, "package.json"), (json) => {
|
|
|
142
142
|
json.name = slug;
|
|
143
143
|
// The templates ship Node's loader and scripts; another runtime gets its own.
|
|
144
144
|
const loader = RUNTIMES[runtimeName].loader;
|
|
145
|
+
const version = json.dependencies["@backtickjs/node-plugin"];
|
|
145
146
|
if (!(loader in json.dependencies)) {
|
|
146
|
-
const version = json.dependencies["@backtickjs/node-plugin"];
|
|
147
147
|
delete json.dependencies["@backtickjs/node-plugin"];
|
|
148
148
|
json.dependencies = Object.fromEntries(Object.entries({ ...json.dependencies, [loader]: version }).sort());
|
|
149
149
|
}
|
|
@@ -159,6 +159,13 @@ rewriteJson(path.join(target, "package.json"), (json) => {
|
|
|
159
159
|
for (const [name, command] of Object.entries(events)) {
|
|
160
160
|
events[name] = bun(command);
|
|
161
161
|
}
|
|
162
|
+
// What still runs on Node, as the tests do, keeps Node's loader.
|
|
163
|
+
if (Object.values(json.scripts).some((command) => command.includes("@backtickjs/node-plugin"))) {
|
|
164
|
+
json.devDependencies = Object.fromEntries(Object.entries({
|
|
165
|
+
...json.devDependencies,
|
|
166
|
+
"@backtickjs/node-plugin": version,
|
|
167
|
+
}).sort());
|
|
168
|
+
}
|
|
162
169
|
}
|
|
163
170
|
});
|
|
164
171
|
if (existsSync(path.join(target, "app.json"))) {
|
package/package.json
CHANGED
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"format:check": "prettier --check --ignore-unknown ."
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@backtickjs/bundler": "^0.2.
|
|
15
|
-
"@backtickjs/core": "^0.2.
|
|
16
|
-
"@backtickjs/node-plugin": "^0.2.
|
|
17
|
-
"@backtickjs/jsx-react": "^0.2.
|
|
14
|
+
"@backtickjs/bundler": "^0.2.1",
|
|
15
|
+
"@backtickjs/core": "^0.2.1",
|
|
16
|
+
"@backtickjs/node-plugin": "^0.2.1",
|
|
17
|
+
"@backtickjs/jsx-react": "^0.2.1",
|
|
18
18
|
"@hono/node-server": "^2.1.4",
|
|
19
19
|
"hono": "^4.13.13",
|
|
20
20
|
"react": "19.2.3",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"typescript": "^6.0.3"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@backtickjs/prettier-plugin": "^0.2.
|
|
26
|
-
"@backtickjs/tsc": "^0.2.
|
|
25
|
+
"@backtickjs/prettier-plugin": "^0.2.1",
|
|
26
|
+
"@backtickjs/tsc": "^0.2.1",
|
|
27
27
|
"@types/node": "^25.9.1",
|
|
28
28
|
"@types/react": "19.2.18",
|
|
29
29
|
"@types/react-dom": "19.2.7",
|
|
@@ -6,4 +6,4 @@ The server's output is in the terminal `npm start` runs in.
|
|
|
6
6
|
|
|
7
7
|
Saving a file under `server/` restarts the server, and Expo reloads the app once it's back: what the server keeps in memory starts over.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
An upgraded package reaches the server once the app is closed and opened again: a reload keeps the versions it was opened with.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { installedPackages } from "@backtickjs/core/node";
|
|
2
|
+
import type { ConfigContext, ExpoConfig } from "expo/config";
|
|
3
|
+
|
|
4
|
+
// app.json, with the installed version of each of the app's dependencies,
|
|
5
|
+
// which the app reads to tell its server the modules it carries.
|
|
6
|
+
export default ({ config, projectRoot }: ConfigContext): ExpoConfig => ({
|
|
7
|
+
...(config as ExpoConfig),
|
|
8
|
+
extra: {
|
|
9
|
+
...config.extra,
|
|
10
|
+
backtick: { packages: installedPackages(projectRoot) },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Bundle } from "@backtickjs/core";
|
|
1
|
+
import { type Bundle, modulesOf } from "@backtickjs/core";
|
|
3
2
|
import { execRoot } from "@backtickjs/core/cjs";
|
|
4
3
|
import Constants from "expo-constants";
|
|
5
4
|
import { StatusBar } from "expo-status-bar";
|
|
6
5
|
import { type ReactNode, Suspense, use, useState } from "react";
|
|
7
|
-
import { ActivityIndicator,
|
|
6
|
+
import { ActivityIndicator, Text } from "react-native";
|
|
8
7
|
import {
|
|
9
8
|
ErrorBoundary,
|
|
10
9
|
type FallbackProps,
|
|
@@ -16,15 +15,17 @@ import { moduleMap } from "./moduleMap";
|
|
|
16
15
|
const host = Constants.expoConfig?.hostUri?.split(":")[0] ?? "localhost";
|
|
17
16
|
const server = `http://${host}:3000`;
|
|
18
17
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
const
|
|
18
|
+
// The modules this build carries, for the server to bundle screens it can
|
|
19
|
+
// run: app.config.ts adds its packages' versions.
|
|
20
|
+
const modules = JSON.stringify(
|
|
21
|
+
modulesOf(moduleMap, Constants.expoConfig?.extra?.backtick?.packages),
|
|
22
|
+
);
|
|
22
23
|
|
|
23
24
|
// The screen at `path` on your server: its bundle, fetched and run with the
|
|
24
25
|
// app's modules. Your fetch to shape: headers, auth, caching.
|
|
25
26
|
async function fetchScreen(path: string): Promise<ReactNode> {
|
|
26
27
|
const response = await fetch(`${server}${path}`, {
|
|
27
|
-
headers: { "x-
|
|
28
|
+
headers: { "x-backtick-modules": modules },
|
|
28
29
|
});
|
|
29
30
|
if (!response.ok) {
|
|
30
31
|
throw new Error(`${path} answered ${response.status}.`);
|
|
@@ -3,9 +3,9 @@ import * as JSXRuntime from "react/jsx-runtime";
|
|
|
3
3
|
import * as ReactNative from "react-native";
|
|
4
4
|
|
|
5
5
|
// What a screen may require, as your app imports it: the packages it's built
|
|
6
|
-
// with. Add one when a screen uses another package
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// with. Add one when a screen uses another package, from package.json's
|
|
7
|
+
// dependencies: the app sends your server what it offers, at the versions
|
|
8
|
+
// it's built with. A platform that differs gets its own file, as
|
|
9
9
|
// Metro picks files: moduleMap.web.ts, moduleMap.ios.ts.
|
|
10
10
|
export const moduleMap = {
|
|
11
11
|
react: React,
|
|
@@ -12,17 +12,14 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit && backtick-tsc -p server --noEmit",
|
|
13
13
|
"test": "node --import ./server/test/setup.mjs --import @backtickjs/node-plugin --test \"server/**/*.test.tsx\"",
|
|
14
14
|
"reset-project": "node scripts/reset-project.mjs",
|
|
15
|
-
"register-build": "backtick-builds register",
|
|
16
|
-
"eas-build-post-install": "backtick-builds check",
|
|
17
15
|
"format": "prettier --write --ignore-unknown .",
|
|
18
16
|
"format:check": "prettier --check --ignore-unknown ."
|
|
19
17
|
},
|
|
20
18
|
"dependencies": {
|
|
21
|
-
"@backtickjs/
|
|
22
|
-
"@backtickjs/
|
|
23
|
-
"@backtickjs/
|
|
24
|
-
"@backtickjs/
|
|
25
|
-
"@backtickjs/jsx-react": "^0.2.0",
|
|
19
|
+
"@backtickjs/bundler": "^0.2.1",
|
|
20
|
+
"@backtickjs/core": "^0.2.1",
|
|
21
|
+
"@backtickjs/node-plugin": "^0.2.1",
|
|
22
|
+
"@backtickjs/jsx-react": "^0.2.1",
|
|
26
23
|
"@hono/node-server": "^2.1.4",
|
|
27
24
|
"expo": "~57.0.26",
|
|
28
25
|
"expo-constants": "~57.0.20",
|
|
@@ -36,8 +33,8 @@
|
|
|
36
33
|
"typescript": "^6.0.3"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
39
|
-
"@backtickjs/prettier-plugin": "^0.2.
|
|
40
|
-
"@backtickjs/tsc": "^0.2.
|
|
36
|
+
"@backtickjs/prettier-plugin": "^0.2.1",
|
|
37
|
+
"@backtickjs/tsc": "^0.2.1",
|
|
41
38
|
"@testing-library/react": "^16.3.3",
|
|
42
39
|
"@testing-library/user-event": "^14.6.7",
|
|
43
40
|
"@types/node": "^25.9.1",
|
|
@@ -59,7 +56,6 @@
|
|
|
59
56
|
}
|
|
60
57
|
},
|
|
61
58
|
"backtick": {
|
|
62
|
-
"clientRoot": "client",
|
|
63
59
|
"plugins": [
|
|
64
60
|
"@backtickjs/jsx-react/plugin"
|
|
65
61
|
]
|
|
@@ -7,7 +7,7 @@ import { screen } from "./screen.js";
|
|
|
7
7
|
const app = new Hono();
|
|
8
8
|
|
|
9
9
|
// `npm run web` serves the app from another origin.
|
|
10
|
-
app.use(cors({ origin: "*", allowHeaders: ["x-
|
|
10
|
+
app.use(cors({ origin: "*", allowHeaders: ["x-backtick-modules"] }));
|
|
11
11
|
|
|
12
12
|
// Your app's screens: add a route, and the app can fetch it. A path can
|
|
13
13
|
// take parameters, `/stories/:id`, read with `context.req.param("id")`.
|
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { createBuilds } from "@backtickjs/builds";
|
|
3
1
|
import { bundler } from "@backtickjs/bundler";
|
|
4
2
|
import type { JSX } from "@backtickjs/jsx-react/jsx-runtime";
|
|
5
3
|
import type { Context } from "hono";
|
|
6
4
|
|
|
7
|
-
// Your app's builds: each sends its id, and a screen is bundled for the
|
|
8
|
-
// modules that build carries, from client/moduleMap.ts. A build in users' hands
|
|
9
|
-
// keeps working as your app moves on, once registered: see builds/.
|
|
10
|
-
const builds = createBuilds({
|
|
11
|
-
projectRoot: fileURLToPath(new URL("..", import.meta.url)),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
5
|
/**
|
|
15
6
|
* `element` as the response to a request from your app, bundled for the
|
|
16
|
-
* modules the requesting build carries.
|
|
7
|
+
* modules the requesting build carries, as client/moduleMap.ts offers them
|
|
8
|
+
* at its packages' versions.
|
|
17
9
|
*/
|
|
18
10
|
export async function screen(context: Context, element: JSX.Element) {
|
|
19
|
-
const modules =
|
|
11
|
+
const modules = JSON.parse(context.req.header("x-backtick-modules") ?? "{}");
|
|
20
12
|
const bundle = await bundler.build({ input: element, modules });
|
|
21
13
|
const { code } = bundle.generate({ format: "cjs" });
|
|
22
14
|
return context.body(code, 200, { "content-type": "text/javascript" });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
-
import { createBuilds } from "@backtickjs/builds";
|
|
3
2
|
import { bundler } from "@backtickjs/bundler";
|
|
4
|
-
import type
|
|
3
|
+
import { modulesOf, type Spliceable } from "@backtickjs/core";
|
|
4
|
+
import { installedPackages } from "@backtickjs/core/node";
|
|
5
5
|
import { execRoot } from "@backtickjs/core/cjs";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import * as JSXRuntime from "react/jsx-runtime";
|
|
@@ -10,14 +10,16 @@ import * as ReactNativeWeb from "react-native-web";
|
|
|
10
10
|
// What the app's web build provides, as client/moduleMap.ts says, with React
|
|
11
11
|
// Native's web build standing in for React Native. Add a stand-in here when
|
|
12
12
|
// your module map offers another package.
|
|
13
|
-
const modules = createBuilds({
|
|
14
|
-
projectRoot: fileURLToPath(new URL("../..", import.meta.url)),
|
|
15
|
-
}).installed("web");
|
|
16
13
|
const moduleMap = {
|
|
17
14
|
react: React,
|
|
18
15
|
"react/jsx-runtime": JSXRuntime,
|
|
19
16
|
"react-native": ReactNativeWeb,
|
|
20
17
|
};
|
|
18
|
+
// at the versions the app installs, as its build sends them
|
|
19
|
+
const modules = modulesOf(
|
|
20
|
+
moduleMap,
|
|
21
|
+
installedPackages(fileURLToPath(new URL("../..", import.meta.url))),
|
|
22
|
+
);
|
|
21
23
|
|
|
22
24
|
// A screen, bundled as your server bundles it and run as your app runs it,
|
|
23
25
|
// for Testing Library to draw.
|
|
@@ -11,18 +11,18 @@
|
|
|
11
11
|
"format:check": "prettier --check --ignore-unknown ."
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@backtickjs/bundler": "^0.2.
|
|
15
|
-
"@backtickjs/core": "^0.2.
|
|
16
|
-
"@backtickjs/node-plugin": "^0.2.
|
|
17
|
-
"@backtickjs/jsx-solid": "^0.2.
|
|
14
|
+
"@backtickjs/bundler": "^0.2.1",
|
|
15
|
+
"@backtickjs/core": "^0.2.1",
|
|
16
|
+
"@backtickjs/node-plugin": "^0.2.1",
|
|
17
|
+
"@backtickjs/jsx-solid": "^0.2.1",
|
|
18
18
|
"@hono/node-server": "^2.1.4",
|
|
19
19
|
"hono": "^4.13.13",
|
|
20
|
-
"solid-js": "1.9.
|
|
20
|
+
"solid-js": "1.9.17",
|
|
21
21
|
"typescript": "^6.0.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@backtickjs/prettier-plugin": "^0.2.
|
|
25
|
-
"@backtickjs/tsc": "^0.2.
|
|
24
|
+
"@backtickjs/prettier-plugin": "^0.2.1",
|
|
25
|
+
"@backtickjs/tsc": "^0.2.1",
|
|
26
26
|
"@types/node": "^25.9.1",
|
|
27
27
|
"agent-device": "^0.21.27",
|
|
28
28
|
"nodemon": "^3.1.14",
|
|
@@ -5,9 +5,9 @@ import type { JSX } from "@backtickjs/jsx-solid/jsx-runtime";
|
|
|
5
5
|
|
|
6
6
|
// The version of Solid the page loads, below: what each page is bundled for.
|
|
7
7
|
const modules = {
|
|
8
|
-
"solid-js": "1.9.
|
|
9
|
-
"solid-js/store": "1.9.
|
|
10
|
-
"solid-js/web": "1.9.
|
|
8
|
+
"solid-js": "1.9.17",
|
|
9
|
+
"solid-js/store": "1.9.17",
|
|
10
|
+
"solid-js/web": "1.9.17",
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// Development unless NODE_ENV says production, as hosts set it for a deploy.
|
|
@@ -35,9 +35,9 @@ export async function toHtml(element: JSX.Element): Promise<string> {
|
|
|
35
35
|
<script type="importmap">
|
|
36
36
|
{
|
|
37
37
|
"imports": {
|
|
38
|
-
"solid-js": "https://cdn.jsdelivr.net/npm/solid-js@1.9.
|
|
39
|
-
"solid-js/web": "https://cdn.jsdelivr.net/npm/solid-js@1.9.
|
|
40
|
-
"solid-js/store": "https://cdn.jsdelivr.net/npm/solid-js@1.9.
|
|
38
|
+
"solid-js": "https://cdn.jsdelivr.net/npm/solid-js@1.9.17/dist/solid.js",
|
|
39
|
+
"solid-js/web": "https://cdn.jsdelivr.net/npm/solid-js@1.9.17/web/dist/web.js",
|
|
40
|
+
"solid-js/store": "https://cdn.jsdelivr.net/npm/solid-js@1.9.17/store/dist/store.js"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
</script>
|