The app, on any device:
The app opens on the shell, a terminal where your projects live. Its commands include git, npm, bundle, run, fullstacked, ls, cd, vi, ssh, curl and config. Run help for the full list.
The CLI, on a desktop with Node.js:
npm install -g fullstacked
In the shell or a terminal:
mkdir hello
cd hello
Create index.ts:
import fs from "node:fs";
import os from "node:os";
// Node's APIs...
fs.writeFileSync(
"/visits.txt",
String(
Number(
fs.existsSync("/visits.txt")
? fs.readFileSync("/visits.txt", "utf8")
: 0
) + 1
)
);
const visits = fs.readFileSync("/visits.txt", "utf8");
// ...and the DOM, in the same file
const h1 = document.createElement("h1");
h1.innerText = `Hello from ${os.platform()}, visit #${visits}`;
document.body.append(h1);
Run it:
fullstacked
The project is bundled into out/ and opens in a window: a new window in the app, or your browser at http://localhost:9000 with the CLI. /visits.txt is a file in the project directory, which is the root of the project’s sandbox.
npm install react react-dom
Add a tsconfig.json so JSX uses the automatic runtime:
{ "compilerOptions": { "jsx": "react-jsx" } }
// index.tsx
import { createRoot } from "react-dom/client";
import { useState } from "react";
import { readdir } from "node:fs/promises";
function App() {
const [files, setFiles] = useState<string[]>([]);
return (
<>
<button onClick={async () => setFiles(await readdir("/"))}>
List files
</button>
<ul>
{files.map((f) => (
<li key={f}>{f}</li>
))}
</ul>
</>
);
}
const root = document.createElement("div");
document.body.append(root);
createRoot(root).render(<App />);
In the app’s shell, npm install runs the core’s installer, so no Node is needed on the device. On a desktop, plain npm works too.
git clone https://github.com/<owner>/<repo>.git
cd <repo>
npm install
fullstacked