create-backtick-app 0.0.0-stage → 0.1.0

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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +146 -0
  5. package/package.json +52 -3
  6. package/templates/react/.prettierignore +8 -0
  7. package/templates/react/.prettierrc +3 -0
  8. package/templates/react/.turbo/turbo-typecheck.log +1 -0
  9. package/templates/react/.vscode/extensions.json +6 -0
  10. package/templates/react/gitignore +16 -0
  11. package/templates/react/node_modules/.bin/backtick-tsc +22 -0
  12. package/templates/react/node_modules/.bin/prettier +22 -0
  13. package/templates/react/node_modules/.bin/tsc +22 -0
  14. package/templates/react/node_modules/.bin/tsserver +22 -0
  15. package/templates/react/package.json +31 -0
  16. package/templates/react/scripts/reset-project.mjs +21 -0
  17. package/templates/react/scripts/start.mjs +20 -0
  18. package/templates/react/server/HelloWave.tsx +30 -0
  19. package/templates/react/server/Home.tsx +65 -0
  20. package/templates/react/server/html.ts +62 -0
  21. package/templates/react/server/index.tsx +33 -0
  22. package/templates/react/tsconfig.json +18 -0
  23. package/templates/react-native/.prettierignore +16 -0
  24. package/templates/react-native/.prettierrc +3 -0
  25. package/templates/react-native/.turbo/turbo-typecheck.log +1 -0
  26. package/templates/react-native/.vscode/extensions.json +6 -0
  27. package/templates/react-native/App.tsx +117 -0
  28. package/templates/react-native/app.json +9 -0
  29. package/templates/react-native/gitignore +41 -0
  30. package/templates/react-native/index.ts +8 -0
  31. package/templates/react-native/node_modules/.bin/backtick-tsc +22 -0
  32. package/templates/react-native/node_modules/.bin/expo +22 -0
  33. package/templates/react-native/node_modules/.bin/expo-modules-autolinking +22 -0
  34. package/templates/react-native/node_modules/.bin/fingerprint +22 -0
  35. package/templates/react-native/node_modules/.bin/prettier +22 -0
  36. package/templates/react-native/node_modules/.bin/react-native +22 -0
  37. package/templates/react-native/node_modules/.bin/tsc +22 -0
  38. package/templates/react-native/node_modules/.bin/tsserver +22 -0
  39. package/templates/react-native/package.json +44 -0
  40. package/templates/react-native/scripts/reset-project.mjs +22 -0
  41. package/templates/react-native/scripts/start.mjs +31 -0
  42. package/templates/react-native/server/HelloWave.tsx +51 -0
  43. package/templates/react-native/server/Home.tsx +84 -0
  44. package/templates/react-native/server/index.tsx +50 -0
  45. package/templates/react-native/server/tsconfig.json +17 -0
  46. package/templates/react-native/tsconfig.json +7 -0
  47. package/templates/solid-js/.prettierignore +8 -0
  48. package/templates/solid-js/.prettierrc +3 -0
  49. package/templates/solid-js/.turbo/turbo-typecheck.log +1 -0
  50. package/templates/solid-js/.vscode/extensions.json +6 -0
  51. package/templates/solid-js/gitignore +16 -0
  52. package/templates/solid-js/node_modules/.bin/backtick-tsc +22 -0
  53. package/templates/solid-js/node_modules/.bin/prettier +22 -0
  54. package/templates/solid-js/node_modules/.bin/tsc +22 -0
  55. package/templates/solid-js/node_modules/.bin/tsserver +22 -0
  56. package/templates/solid-js/package.json +31 -0
  57. package/templates/solid-js/scripts/reset-project.mjs +21 -0
  58. package/templates/solid-js/scripts/start.mjs +20 -0
  59. package/templates/solid-js/server/HelloWave.tsx +34 -0
  60. package/templates/solid-js/server/Home.tsx +65 -0
  61. package/templates/solid-js/server/html.ts +62 -0
  62. package/templates/solid-js/server/index.tsx +33 -0
  63. package/templates/solid-js/tsconfig.json +18 -0
@@ -0,0 +1,51 @@
1
+ import { cs } from "@backtickjs/core";
2
+ import { useEffect, useRef } from "@backtickjs/react";
3
+ import { Animated, Pressable } from "@backtickjs/react-native";
4
+
5
+ // A client component: it runs on the phone, so its animation does too. It
6
+ // waves when it appears, and again when you tap it.
7
+ export const HelloWave = cs`() => {
8
+ const rotation = $useRef(new $Animated.Value(0)).current;
9
+ const wave = () =>
10
+ $Animated
11
+ .loop(
12
+ $Animated.sequence([
13
+ $Animated.timing(rotation, {
14
+ toValue: 1,
15
+ duration: 150,
16
+ useNativeDriver: true,
17
+ }),
18
+ $Animated.timing(rotation, {
19
+ toValue: 0,
20
+ duration: 150,
21
+ useNativeDriver: true,
22
+ }),
23
+ ]),
24
+ { iterations: 4 },
25
+ )
26
+ .start();
27
+
28
+ $useEffect(wave, []);
29
+
30
+ return (
31
+ <$Pressable onPress={wave}>
32
+ <$Animated.Text
33
+ style={{
34
+ fontSize: 28,
35
+ lineHeight: 32,
36
+ marginTop: -6,
37
+ transform: [
38
+ {
39
+ rotate: rotation.interpolate({
40
+ inputRange: [0, 1],
41
+ outputRange: ["0deg", "25deg"],
42
+ }),
43
+ },
44
+ ],
45
+ }}
46
+ >
47
+ 👋
48
+ </$Animated.Text>
49
+ </$Pressable>
50
+ );
51
+ }`;
@@ -0,0 +1,84 @@
1
+ import { cs } from "@backtickjs/core";
2
+ import {
3
+ Linking,
4
+ ScrollView,
5
+ StatusBar,
6
+ StyleSheet,
7
+ Text,
8
+ View,
9
+ } from "@backtickjs/react-native";
10
+ import { HelloWave } from "./HelloWave.js";
11
+
12
+ // A server component: it runs on your server for every request, and hands the
13
+ // app its data and its client components.
14
+ export async function Home() {
15
+ // Read here, on your server, and sent to the phone as plain values.
16
+ const node = process.version;
17
+ const assembledAt = new Date().toLocaleTimeString();
18
+
19
+ // The whole layout is the screen's, safe areas included: iOS insets a root
20
+ // scroll view itself, and Android draws under its status bar, so the screen
21
+ // pads for it.
22
+ return cs`{
23
+ const styles = $StyleSheet.create({
24
+ screen: {
25
+ padding: 24,
26
+ paddingTop: ($StatusBar.currentHeight ?? 0) + 24,
27
+ gap: 24,
28
+ },
29
+ titleRow: { flexDirection: "row", alignItems: "center", gap: 8 },
30
+ title: { fontSize: 32, fontWeight: "bold", lineHeight: 32 },
31
+ step: { gap: 8 },
32
+ subtitle: { fontSize: 20, fontWeight: "bold" },
33
+ body: { fontSize: 16, lineHeight: 24 },
34
+ code: { fontWeight: "600" },
35
+ link: { fontSize: 16, lineHeight: 30, color: "#0a7ea4" },
36
+ });
37
+
38
+ return (
39
+ <$ScrollView
40
+ contentInsetAdjustmentBehavior="automatic"
41
+ style={{ backgroundColor: "#fff" }}
42
+ contentContainerStyle={styles.screen}
43
+ >
44
+ <$View style={styles.titleRow}>
45
+ <$Text style={styles.title}>Welcome!</$Text>
46
+ <$HelloWave />
47
+ </$View>
48
+
49
+ <$View style={styles.step}>
50
+ <$Text style={styles.subtitle}>Step 1: Try it</$Text>
51
+ <$Text style={styles.body}>
52
+ Edit <$Text style={styles.code}>server/Home.tsx</$Text> and save.
53
+ This screen redraws without rebuilding the app.
54
+ </$Text>
55
+ </$View>
56
+
57
+ <$View style={styles.step}>
58
+ <$Text style={styles.subtitle}>Step 2: Server and client</$Text>
59
+ <$Text style={styles.body}>
60
+ Your server assembled this screen with Node {$node} at{" "}
61
+ {$assembledAt}. The waving hand is a client component: its animation
62
+ runs on your phone. Tap it.
63
+ </$Text>
64
+ </$View>
65
+
66
+ <$View style={styles.step}>
67
+ <$Text style={styles.subtitle}>Step 3: Get a fresh start</$Text>
68
+ <$Text style={styles.body}>
69
+ When you're ready, run{" "}
70
+ <$Text style={styles.code}>npm run reset-project</$Text> for a blank{" "}
71
+ <$Text style={styles.code}>server/Home.tsx</$Text>.
72
+ </$Text>
73
+ </$View>
74
+
75
+ <$Text
76
+ style={styles.link}
77
+ onPress={() => $Linking.openURL("https://backtickjs.com/docs")}
78
+ >
79
+ Read the docs →
80
+ </$Text>
81
+ </$ScrollView>
82
+ );
83
+ }`;
84
+ }
@@ -0,0 +1,50 @@
1
+ import { createServer } from "node:http";
2
+ import { createRequire } from "node:module";
3
+ import { bundler } from "@backtickjs/bundler";
4
+ import { Home } from "./Home.js";
5
+
6
+ // Development is only what `npm start` runs, which sets NODE_ENV: any other
7
+ // run, a deploy included, is production. In development, this run of the
8
+ // server: `--watch` starts a new one whenever you save, and the app, seeing a
9
+ // new one at `/live`, draws its screen again.
10
+ const development = process.env.NODE_ENV === "development";
11
+ const run = crypto.randomUUID();
12
+
13
+ // The versions of React and React Native the app is built with: this
14
+ // project's own, as the app and this server share a package.json. A server
15
+ // for apps of many versions would tell them apart, by their user agent for
16
+ // one, and bundle each for its own.
17
+ const require = createRequire(import.meta.url);
18
+ const packageVersions = {
19
+ react: require("react/package.json").version,
20
+ "react-native": require("react-native/package.json").version,
21
+ };
22
+
23
+ const server = createServer(async (request, response) => {
24
+ // `npm run web` serves the app from another origin.
25
+ response.setHeader("access-control-allow-origin", "*");
26
+ if (development && request.url === "/live") {
27
+ response.end(run);
28
+ return;
29
+ }
30
+ if (request.url === "/home") {
31
+ try {
32
+ const bundle = await bundler.build({ input: <Home />, packageVersions });
33
+ const { code } = bundle.generate({ format: "cjs" });
34
+ response.setHeader("content-type", "text/javascript");
35
+ response.end(code);
36
+ } catch (error) {
37
+ // Answered rather than crashed on, so the app can show why.
38
+ console.error(error);
39
+ response.statusCode = 500;
40
+ response.end(String(error));
41
+ }
42
+ return;
43
+ }
44
+ response.statusCode = 404;
45
+ response.end();
46
+ });
47
+
48
+ server.listen(3000, () => {
49
+ console.log("Backtick server on http://localhost:3000");
50
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Imports kept as written: a name a screen only splices, as `$View`, looks
4
+ // unused to TypeScript, which would otherwise drop its import.
5
+ "verbatimModuleSyntax": true,
6
+ "module": "esnext",
7
+ "moduleResolution": "bundler",
8
+ "target": "esnext",
9
+ "strict": true,
10
+ "noEmit": true,
11
+ "skipLibCheck": true,
12
+ "types": ["node"],
13
+ "jsx": "react-jsx",
14
+ "jsxImportSource": "@backtickjs/react"
15
+ },
16
+ "include": ["."]
17
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true
5
+ },
6
+ "exclude": ["server"]
7
+ }
@@ -0,0 +1,8 @@
1
+ # Written by the package manager.
2
+ pnpm-lock.yaml
3
+ package-lock.json
4
+ yarn.lock
5
+ bun.lock
6
+
7
+ # Written the way VS Code writes them, one entry per line.
8
+ .vscode/extensions.json
@@ -0,0 +1,3 @@
1
+ {
2
+ "plugins": ["@backtickjs/prettier-plugin"]
3
+ }
@@ -0,0 +1 @@
1
+ $ backtick-tsc --noEmit
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "backtickjs.backtick-vscode",
4
+ "esbenp.prettier-vscode"
5
+ ]
6
+ }
@@ -0,0 +1,16 @@
1
+ # dependencies
2
+ node_modules/
3
+
4
+ # debug
5
+ npm-debug.*
6
+ yarn-debug.*
7
+ yarn-error.*
8
+
9
+ # macOS
10
+ .DS_Store
11
+
12
+ # local env files
13
+ .env*.local
14
+
15
+ # typescript
16
+ *.tsbuildinfo
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../@backtickjs/tsc/bin/backtick-tsc.js" "$@"
19
+ else
20
+ exec node "$basedir/../@backtickjs/tsc/bin/backtick-tsc.js" "$@"
21
+ fi
22
+ # cmd-shim-target=/home/runner/work/backtick/backtick/packages/create-backtick-app/templates/solid-js/node_modules/@backtickjs/tsc/bin/backtick-tsc.js
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/prettier@3.8.4/node_modules/prettier/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/prettier@3.8.4/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/prettier@3.8.4/node_modules/prettier/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/prettier@3.8.4/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../prettier/bin/prettier.cjs" "$@"
19
+ else
20
+ exec node "$basedir/../prettier/bin/prettier.cjs" "$@"
21
+ fi
22
+ # cmd-shim-target=/home/runner/work/backtick/backtick/packages/create-backtick-app/templates/solid-js/node_modules/prettier/bin/prettier.cjs
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsc" "$@"
21
+ fi
22
+ # cmd-shim-target=/home/runner/work/backtick/backtick/packages/create-backtick-app/templates/solid-js/node_modules/typescript/bin/tsc
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
+
4
+ case `uname` in
5
+ *CYGWIN*|*MINGW*|*MSYS*)
6
+ if command -v cygpath > /dev/null 2>&1; then
7
+ basedir=`cygpath -w "$basedir"`
8
+ fi
9
+ ;;
10
+ esac
11
+
12
+ if [ -z "$NODE_PATH" ]; then
13
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules"
14
+ else
15
+ export NODE_PATH="/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/typescript@6.0.3/node_modules:/home/runner/work/backtick/backtick/node_modules/.pnpm/node_modules:$NODE_PATH"
16
+ fi
17
+ if [ -x "$basedir/node" ]; then
18
+ exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
19
+ else
20
+ exec node "$basedir/../typescript/bin/tsserver" "$@"
21
+ fi
22
+ # cmd-shim-target=/home/runner/work/backtick/backtick/packages/create-backtick-app/templates/solid-js/node_modules/typescript/bin/tsserver
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "backtick-app-solid-js",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "node scripts/start.mjs",
8
+ "reset-project": "node scripts/reset-project.mjs",
9
+ "typecheck": "backtick-tsc --noEmit",
10
+ "format": "prettier --write --ignore-unknown .",
11
+ "format:check": "prettier --check --ignore-unknown ."
12
+ },
13
+ "dependencies": {
14
+ "@backtickjs/bundler": "^0.1.0",
15
+ "@backtickjs/core": "^0.1.0",
16
+ "@backtickjs/node-plugin": "^0.1.0",
17
+ "@backtickjs/solid-js": "^1.9.14",
18
+ "typescript": "^6.0.3"
19
+ },
20
+ "devDependencies": {
21
+ "@backtickjs/prettier-plugin": "^0.1.0",
22
+ "@backtickjs/tsc": "^0.1.0",
23
+ "@types/node": "^25.9.1",
24
+ "prettier": "^3.6.2"
25
+ },
26
+ "backtick": {
27
+ "plugins": [
28
+ "@backtickjs/solid-js/plugin"
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,21 @@
1
+ // Replaces the welcome page with a blank one, so you start fresh.
2
+ import { rmSync, writeFileSync } from "node:fs";
3
+
4
+ writeFileSync(
5
+ "server/Home.tsx",
6
+ `import { cs } from "@backtickjs/core";
7
+
8
+ export async function Home() {
9
+ return cs\`(
10
+ <main style={{ padding: "24px", "font-family": "system-ui, sans-serif" }}>
11
+ <p>Edit server/Home.tsx to edit this page.</p>
12
+ </main>
13
+ )\`;
14
+ }
15
+ `,
16
+ );
17
+ rmSync("server/HelloWave.tsx", { force: true });
18
+
19
+ console.log(
20
+ "✅ Project reset. server/Home.tsx is now a blank page, and the welcome example is gone.",
21
+ );
@@ -0,0 +1,20 @@
1
+ // Starts your Backtick server in development: it restarts whenever a file
2
+ // under `server/` changes, and the page reloads with it. Any other way of
3
+ // running the server, a deploy included, is production.
4
+ import { spawn } from "node:child_process";
5
+ import { readFileSync } from "node:fs";
6
+
7
+ // The runtime is the one package.json has a Backtick loader for: Bun's, or
8
+ // else Node's.
9
+ const { dependencies } = JSON.parse(readFileSync("package.json", "utf8"));
10
+ const [runtime, ...loader] =
11
+ "@backtickjs/bun-plugin" in dependencies
12
+ ? ["bun", "--preload", "@backtickjs/bun-plugin"]
13
+ : ["node", "--enable-source-maps", "--import", "@backtickjs/node-plugin"];
14
+
15
+ const server = spawn(runtime, ["--watch", ...loader, "server/index.tsx"], {
16
+ stdio: "inherit",
17
+ env: { ...process.env, NODE_ENV: process.env.NODE_ENV ?? "development" },
18
+ });
19
+
20
+ server.on("exit", (code) => process.exit(code ?? 0));
@@ -0,0 +1,34 @@
1
+ import { cs } from "@backtickjs/core";
2
+ import { onMount } from "@backtickjs/solid-js";
3
+
4
+ // A client component: it runs in the browser, so its animation does too. It
5
+ // waves when it appears, and again when you click it.
6
+ export const HelloWave = cs`() => {
7
+ let hand!: HTMLSpanElement;
8
+ const wave = () => {
9
+ hand.animate(
10
+ [
11
+ { transform: "rotate(0deg)" },
12
+ { transform: "rotate(25deg)" },
13
+ { transform: "rotate(0deg)" },
14
+ ],
15
+ { duration: 300, iterations: 4 },
16
+ );
17
+ };
18
+
19
+ $onMount(wave);
20
+
21
+ return (
22
+ <span
23
+ ref={hand}
24
+ onClick={wave}
25
+ style={{
26
+ display: "inline-block",
27
+ cursor: "pointer",
28
+ "font-size": "28px",
29
+ }}
30
+ >
31
+ 👋
32
+ </span>
33
+ );
34
+ }`;
@@ -0,0 +1,65 @@
1
+ import { cs } from "@backtickjs/core";
2
+ import { HelloWave } from "./HelloWave.js";
3
+
4
+ // A server component: it runs on your server for every request, and hands the
5
+ // browser its data and its client components.
6
+ export async function Home() {
7
+ // Read here, on your server, and sent to the browser as plain values.
8
+ const node = process.version;
9
+ const assembledAt = new Date().toLocaleTimeString();
10
+
11
+ return cs`{
12
+ const styles = {
13
+ page: {
14
+ "max-width": "640px",
15
+ padding: "24px",
16
+ "font-family": "system-ui, sans-serif",
17
+ display: "grid",
18
+ gap: "24px",
19
+ },
20
+ titleRow: { display: "flex", "align-items": "center", gap: "8px" },
21
+ title: { margin: "0", "font-size": "32px", "line-height": "1" },
22
+ step: { display: "grid", gap: "8px" },
23
+ subtitle: { margin: "0", "font-size": "20px" },
24
+ body: { margin: "0", "font-size": "16px", "line-height": "1.5" },
25
+ link: { "font-size": "16px", color: "#0a7ea4" },
26
+ };
27
+
28
+ return (
29
+ <main style={styles.page}>
30
+ <div style={styles.titleRow}>
31
+ <h1 style={styles.title}>Welcome!</h1>
32
+ <$HelloWave />
33
+ </div>
34
+
35
+ <section style={styles.step}>
36
+ <h2 style={styles.subtitle}>Step 1: Try it</h2>
37
+ <p style={styles.body}>
38
+ Edit <b>server/Home.tsx</b> and save. This page redraws on its own.
39
+ </p>
40
+ </section>
41
+
42
+ <section style={styles.step}>
43
+ <h2 style={styles.subtitle}>Step 2: Server and client</h2>
44
+ <p style={styles.body}>
45
+ Your server assembled this page with Node {$node} at {$assembledAt}.
46
+ The waving hand is a client component: its animation runs in your
47
+ browser. Click it.
48
+ </p>
49
+ </section>
50
+
51
+ <section style={styles.step}>
52
+ <h2 style={styles.subtitle}>Step 3: Get a fresh start</h2>
53
+ <p style={styles.body}>
54
+ When you're ready, run <b>npm run reset-project</b> for a blank{" "}
55
+ <b>server/Home.tsx</b>.
56
+ </p>
57
+ </section>
58
+
59
+ <a style={styles.link} href="https://backtickjs.com/docs">
60
+ Read the docs →
61
+ </a>
62
+ </main>
63
+ );
64
+ }`;
65
+ }
@@ -0,0 +1,62 @@
1
+ import { bundler } from "@backtickjs/bundler";
2
+ import { cs } from "@backtickjs/core";
3
+ import { render } from "@backtickjs/solid-js/web";
4
+ import type { JSX } from "@backtickjs/solid-js/jsx-runtime";
5
+
6
+ // The version of Solid the page loads, below: what each page is bundled for.
7
+ const packageVersions = { "solid-js": "1.9.14" };
8
+
9
+ // Development is only what `npm start` runs, which sets NODE_ENV: any other
10
+ // run, a deploy included, is production.
11
+ export const development = process.env.NODE_ENV === "development";
12
+
13
+ // In development, what reloads the page whenever the server restarts, as it
14
+ // does when you save.
15
+ const reload = `
16
+ <script type="module">
17
+ let run;
18
+ setInterval(async () => {
19
+ try {
20
+ const next = await (await fetch("/live")).text();
21
+ if (run !== undefined && next !== run) location.reload();
22
+ run = next;
23
+ } catch {}
24
+ }, 1000);
25
+ </script>`;
26
+
27
+ // A page: the element, bundled for the browser, and Solid from a CDN.
28
+ export async function toHtml(element: JSX.Element): Promise<string> {
29
+ const bundle = await bundler.build({
30
+ input: cs`$render(() => $element, document.getElementById("app")!)`,
31
+ packageVersions,
32
+ });
33
+ // In development, a map back to the files you wrote, for the browser's
34
+ // devtools.
35
+ const { code } = bundle.generate({
36
+ format: "es",
37
+ sourcemap: development ? "inline" : false,
38
+ });
39
+
40
+ return `<!doctype html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="utf-8">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1">
45
+ <title>Backtick</title>
46
+ <script type="importmap">
47
+ {
48
+ "imports": {
49
+ "solid-js": "https://cdn.jsdelivr.net/npm/solid-js@1.9.14/dist/solid.js",
50
+ "solid-js/web": "https://cdn.jsdelivr.net/npm/solid-js@1.9.14/web/dist/web.js",
51
+ "solid-js/store": "https://cdn.jsdelivr.net/npm/solid-js@1.9.14/store/dist/store.js"
52
+ }
53
+ }
54
+ </script>
55
+ </head>
56
+ <body style="margin: 0">
57
+ <div id="app"></div>
58
+ <script type="module" src="data:text/javascript,${encodeURIComponent(code)}"></script>
59
+ ${development ? reload : ""}
60
+ </body>
61
+ </html>`;
62
+ }
@@ -0,0 +1,33 @@
1
+ import { createServer } from "node:http";
2
+ import { Home } from "./Home.js";
3
+ import { development, toHtml } from "./html.js";
4
+
5
+ // In development, this run of the server: `--watch` starts a new one whenever
6
+ // you save, and the page, seeing a new one at `/live`, reloads.
7
+ const run = crypto.randomUUID();
8
+
9
+ const server = createServer(async (request, response) => {
10
+ if (development && request.url === "/live") {
11
+ response.end(run);
12
+ return;
13
+ }
14
+ if (request.url === "/") {
15
+ try {
16
+ const html = await toHtml(<Home />);
17
+ response.setHeader("content-type", "text/html");
18
+ response.end(html);
19
+ } catch (error) {
20
+ // Answered rather than crashed on, so the browser can show why.
21
+ console.error(error);
22
+ response.statusCode = 500;
23
+ response.end(String(error));
24
+ }
25
+ return;
26
+ }
27
+ response.statusCode = 404;
28
+ response.end();
29
+ });
30
+
31
+ server.listen(3000, () => {
32
+ console.log("Backtick server on http://localhost:3000");
33
+ });
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Imports kept as written: a name a page only splices, as `$onMount`, looks
4
+ // unused to TypeScript, which would otherwise drop its import.
5
+ "verbatimModuleSyntax": true,
6
+ "module": "esnext",
7
+ "moduleResolution": "bundler",
8
+ "target": "esnext",
9
+ "strict": true,
10
+ "noEmit": true,
11
+ "skipLibCheck": true,
12
+ "lib": ["esnext", "dom"],
13
+ "types": ["node"],
14
+ "jsx": "react-jsx",
15
+ "jsxImportSource": "@backtickjs/solid-js"
16
+ },
17
+ "include": ["server"]
18
+ }