create-nifra 3.5.0 → 4.0.2
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 +3 -3
- package/dist/agent-files.d.ts +41 -26
- package/dist/agent-files.d.ts.map +1 -1
- package/dist/agent-files.js +80 -47
- package/dist/agent-files.js.map +1 -1
- package/dist/agents.d.ts +3 -3
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +65 -69
- package/dist/agents.js.map +1 -1
- package/dist/auth.d.ts +5 -4
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +27 -14
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts +23 -25
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +141 -130
- package/dist/cli.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +49 -48
- package/dist/db.js.map +1 -1
- package/dist/scaffold/frameworks.d.ts +13 -38
- package/dist/scaffold/frameworks.d.ts.map +1 -1
- package/dist/scaffold/frameworks.js +9 -32
- package/dist/scaffold/frameworks.js.map +1 -1
- package/dist/scaffold/route-types.d.ts +9 -0
- package/dist/scaffold/route-types.d.ts.map +1 -0
- package/dist/scaffold/route-types.js +61 -0
- package/dist/scaffold/route-types.js.map +1 -0
- package/dist/scaffold/site-files.d.ts +14 -8
- package/dist/scaffold/site-files.d.ts.map +1 -1
- package/dist/scaffold/site-files.js +29 -60
- package/dist/scaffold/site-files.js.map +1 -1
- package/dist/scaffold/site.d.ts +8 -5
- package/dist/scaffold/site.d.ts.map +1 -1
- package/dist/scaffold/site.js +47 -34
- package/dist/scaffold/site.js.map +1 -1
- package/dist/scaffold/targets.d.ts +39 -0
- package/dist/scaffold/targets.d.ts.map +1 -0
- package/dist/scaffold/targets.js +116 -0
- package/dist/scaffold/targets.js.map +1 -0
- package/package.json +4 -1
- package/src/agent-files.ts +89 -47
- package/template/README.md +12 -6
- package/template/{src → backend}/app.test.ts +2 -2
- package/template/{src → backend}/app.ts +8 -3
- package/template/{src → backend}/index.ts +2 -2
- package/template/{src → backend}/routes.ts +1 -1
- package/template/nifra.assurance.ts +16 -14
- package/template/package.json +7 -7
- package/template/tsconfig.json +1 -1
- package/template-batteries/README.md +10 -6
- package/template-batteries/{src → backend}/app.test.ts +23 -23
- package/template-batteries/{src → backend}/app.ts +8 -3
- package/template-batteries/{src → backend}/index.ts +2 -2
- package/template-batteries/{src → backend}/notes.ts +1 -1
- package/template-batteries/nifra.assurance.ts +16 -14
- package/template-batteries/package.json +10 -9
- package/template-batteries/tsconfig.json +1 -1
- package/template-isr/README.md +3 -0
- package/template-isr/{backend.ts → backend/app.ts} +2 -0
- package/template-isr/{server.ts → backend/dev-server.ts} +4 -4
- package/template-isr/{worker.ts → backend/worker.ts} +4 -2
- package/template-isr/build.ts +3 -1
- package/template-isr/gitignore +7 -1
- package/template-isr/nifra.assurance.ts +15 -13
- package/template-isr/package.json +10 -9
- package/template-isr/routes/index.backend.ts +14 -0
- package/template-isr/routes/index.tsx +5 -17
- package/template-isr/tsconfig.json +5 -4
- package/template-isr/wrangler.toml +6 -1
- package/template-site/README.md +24 -23
- package/template-site/{backend.ts → backend/app.ts} +6 -4
- package/template-site/gitignore +5 -10
- package/template-site/nifra.assurance.ts +15 -13
- package/template-site/nifra.config.ts +4 -4
- package/template-site/routes/index.backend.ts +18 -0
- package/template-site/routes/index.tsx +2 -15
- package/template-site-preact/README.md +21 -20
- package/template-site-preact/nifra.config.ts +4 -4
- package/template-site-preact/routes/_layout.tsx +3 -1
- package/template-site-preact/routes/index.tsx +2 -15
- package/template-site-solid/README.md +21 -19
- package/template-site-solid/nifra.config.ts +5 -4
- package/template-site-solid/routes/index.tsx +2 -15
- package/template-site-svelte/README.md +21 -19
- package/template-site-svelte/nifra.config.ts +3 -2
- package/template-site-svelte/routes/index.svelte +3 -16
- package/template-site-vue/README.md +21 -20
- package/template-site-vue/nifra.config.ts +4 -3
- package/template-site-vue/routes/_layout.vue +21 -25
- package/template-site-vue/routes/index.vue +12 -18
- package/dist/scaffold/site-build.d.ts +0 -60
- package/dist/scaffold/site-build.d.ts.map +0 -1
- package/dist/scaffold/site-build.js +0 -198
- package/dist/scaffold/site-build.js.map +0 -1
- package/template-site/.dockerignore +0 -14
- package/template-site/Dockerfile +0 -25
- package/template-site/_worker.ts +0 -17
- package/template-site/deno.json +0 -7
- package/template-site/server-bun.ts +0 -35
- package/template-site/server-deno.ts +0 -38
- package/template-site/server-node.ts +0 -40
- package/template-site/server-vercel.ts +0 -17
- package/template-site/wrangler.toml +0 -4
- /package/template-isr/{page.ts → backend/page.ts} +0 -0
- /package/template-site/{counter.ts → backend/counter.ts} +0 -0
|
@@ -1,93 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* which framework they name, and keeping five copies is how `.vercel` came to be excluded from four
|
|
6
|
-
* tsconfigs and not the fifth.
|
|
2
|
+
* A site scaffold's mechanical files: the adapter re-export, the manifest, the tsconfig. These were
|
|
3
|
+
* five copies each whose only real difference was which framework they name, and keeping five copies
|
|
4
|
+
* is how `.vercel` came to be excluded from four tsconfigs and not the fifth.
|
|
7
5
|
*/
|
|
8
6
|
import { NIFRA_DEP_RANGE } from "./frameworks.js";
|
|
7
|
+
import { deployScript, TARGETS } from "./targets.js";
|
|
9
8
|
/** JSON as these files are written: two-space indent, trailing newline. */
|
|
10
9
|
const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
|
|
11
10
|
/**
|
|
12
|
-
* `framework.ts` - the
|
|
13
|
-
*
|
|
11
|
+
* `backend/framework.ts` - the file the generated server entry imports the adapter from. It stays
|
|
12
|
+
* edge-bundlable: CLI-only tooling (Vite plugins, compilers) lives in `nifra.config.ts`.
|
|
14
13
|
*/
|
|
15
14
|
export function renderFrameworkModule(framework) {
|
|
16
15
|
return `// The frontend adapter for this app. \`create-nifra --framework <react|preact|vue|solid|svelte>\` swaps
|
|
17
|
-
// this one line (and the routes + build config);
|
|
18
|
-
//
|
|
16
|
+
// this one line (and the routes + build config); the server entry \`nifra build\` generates imports the
|
|
17
|
+
// adapter from here, so it stays framework-agnostic.
|
|
19
18
|
import { ${framework.adapter} } from "${framework.package}"
|
|
20
19
|
|
|
21
20
|
export const adapter = ${framework.adapter}
|
|
22
21
|
`;
|
|
23
22
|
}
|
|
24
|
-
/**
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
build: "bun run build.ts",
|
|
29
|
-
"build:bun": "bun run build-bun.ts",
|
|
30
|
-
"build:node": "bun run build-node.ts",
|
|
31
|
-
"build:deno": "bun run build-deno.ts",
|
|
32
|
-
"build:vercel": "bun run build-vercel.ts",
|
|
33
|
-
start: "bun dist-bun/server-bun.js",
|
|
34
|
-
"start:node": "node dist-node/server-node.js",
|
|
35
|
-
"deploy:cf": "wrangler pages deploy dist",
|
|
36
|
-
"deploy:vercel": "vercel deploy --prebuilt",
|
|
37
|
-
check: "nifra check && nifra assure",
|
|
38
|
-
};
|
|
39
|
-
/** Nifra packages every site depends on, whatever it renders with. */
|
|
40
|
-
const NIFRA_RUNTIME = ["client", "core", "middleware", "schema", "deno", "node", "web"];
|
|
41
|
-
export function renderPackageJson(framework) {
|
|
23
|
+
/** Nifra packages every site depends on, whatever it renders with and wherever it deploys. */
|
|
24
|
+
const NIFRA_RUNTIME = ["client", "core", "middleware", "schema", "web"];
|
|
25
|
+
export function renderPackageJson(framework, site) {
|
|
26
|
+
const spec = TARGETS[site.target];
|
|
42
27
|
const dependencies = {};
|
|
43
|
-
for (const name of NIFRA_RUNTIME)
|
|
28
|
+
for (const name of [...NIFRA_RUNTIME, ...(spec.runtime ?? [])].sort()) {
|
|
44
29
|
dependencies[`@nifrajs/${name}`] = NIFRA_DEP_RANGE;
|
|
30
|
+
}
|
|
45
31
|
dependencies[framework.package] = NIFRA_DEP_RANGE;
|
|
46
32
|
Object.assign(dependencies, framework.runtimeDependencies);
|
|
33
|
+
const deploy = deployScript(site.target, site.name, site.docker);
|
|
47
34
|
return json({
|
|
48
35
|
name: "nifra-site",
|
|
49
36
|
version: "0.0.0",
|
|
50
37
|
type: "module",
|
|
51
38
|
private: true,
|
|
52
|
-
scripts:
|
|
39
|
+
scripts: {
|
|
40
|
+
dev: "nifra dev",
|
|
41
|
+
build: "nifra build",
|
|
42
|
+
...(spec.start === undefined ? {} : { start: spec.start }),
|
|
43
|
+
...(deploy === undefined ? {} : { deploy }),
|
|
44
|
+
check: "nifra check && nifra assure",
|
|
45
|
+
},
|
|
53
46
|
dependencies,
|
|
54
47
|
devDependencies: {
|
|
55
48
|
"@nifrajs/cli": NIFRA_DEP_RANGE,
|
|
49
|
+
"@types/bun": "^1.4.2",
|
|
56
50
|
...framework.devDependencies,
|
|
57
51
|
typescript: "^6.0.3",
|
|
58
52
|
vite: "^8.2.1",
|
|
59
53
|
},
|
|
60
54
|
});
|
|
61
55
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
* rather than typechecked as part of the app.
|
|
65
|
-
*
|
|
66
|
-
* `.vercel` is in here for every framework. It was missing from React's copy alone, which is the shape
|
|
67
|
-
* this whole refactor is about: `build-vercel.ts` writes there in all five, and four tsconfigs knew.
|
|
68
|
-
*/
|
|
69
|
-
const EXCLUDE = [
|
|
70
|
-
"node_modules",
|
|
71
|
-
"dist",
|
|
72
|
-
"dist-bun",
|
|
73
|
-
"dist-node",
|
|
74
|
-
"dist-deno",
|
|
75
|
-
"dist-vercel",
|
|
76
|
-
".nifra",
|
|
77
|
-
".vercel",
|
|
78
|
-
".wrangler",
|
|
79
|
-
"server-manifest.ts",
|
|
80
|
-
"server-bun.ts",
|
|
81
|
-
"server-node.ts",
|
|
82
|
-
"server-deno.ts",
|
|
83
|
-
"server-vercel.ts",
|
|
84
|
-
"_worker.ts",
|
|
85
|
-
"build.ts",
|
|
86
|
-
"build-bun.ts",
|
|
87
|
-
"build-node.ts",
|
|
88
|
-
"build-deno.ts",
|
|
89
|
-
"build-vercel.ts",
|
|
90
|
-
];
|
|
56
|
+
/** Directories a build writes and a typecheck must not read. */
|
|
57
|
+
const EXCLUDE = ["node_modules", "dist", ".nifra", ".nifra-build", ".vercel", ".wrangler"];
|
|
91
58
|
export function renderTsconfig(framework) {
|
|
92
59
|
const ts = framework.typescript;
|
|
93
60
|
return json({
|
|
@@ -103,9 +70,11 @@ export function renderTsconfig(framework) {
|
|
|
103
70
|
skipLibCheck: true,
|
|
104
71
|
noEmit: true,
|
|
105
72
|
verbatimModuleSyntax: true,
|
|
106
|
-
|
|
73
|
+
types: ["bun", ...(ts.types ?? [])],
|
|
74
|
+
// Merges the generated `.nifra/types` tree in, so a route imports its types as `./+types/<name>`.
|
|
75
|
+
rootDirs: [".", "./.nifra/types"],
|
|
107
76
|
},
|
|
108
|
-
include: ts.
|
|
77
|
+
include: ts.include ?? ["**/*.ts", "**/*.tsx"],
|
|
109
78
|
exclude: EXCLUDE,
|
|
110
79
|
});
|
|
111
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-files.js","sourceRoot":"","sources":["../../src/scaffold/site-files.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"site-files.js","sourceRoot":"","sources":["../../src/scaffold/site-files.ts"],"names":[],"mappings":"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"}
|
package/dist/scaffold/site.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FrameworkSpec } from "./frameworks.js";
|
|
2
|
+
import { type SiteTarget } from "./site-files.js";
|
|
2
3
|
/**
|
|
3
4
|
* Files identical in every site scaffold, taken from the base directory.
|
|
4
5
|
*
|
|
@@ -9,16 +10,18 @@ import { type FrameworkSpec } from "./frameworks.js";
|
|
|
9
10
|
export declare const SHARED_SITE_FILES: readonly string[];
|
|
10
11
|
/** Files each framework supplies itself, relative to its overlay directory. */
|
|
11
12
|
export declare const FRAMEWORK_SITE_FILES: readonly string[];
|
|
12
|
-
/** Route files, whose extension is the framework's own. */
|
|
13
|
+
/** Route frontend files, whose extension is the framework's own. The backend half is shared. */
|
|
13
14
|
export declare const ROUTE_BASENAMES: readonly string[];
|
|
14
15
|
/** Extension the framework's route files carry. */
|
|
15
16
|
export declare function routeExtension(framework: FrameworkSpec): string;
|
|
16
17
|
/** The base directory: the shared files plus React's own, which is also the React overlay. */
|
|
17
18
|
export declare const SITE_BASE_DIR: string;
|
|
18
19
|
export declare function overlayDir(framework: FrameworkSpec): string;
|
|
19
|
-
/** Every file a site scaffold
|
|
20
|
-
export declare function generatedSiteFiles(framework: FrameworkSpec): Map<string, string>;
|
|
21
|
-
|
|
20
|
+
/** Every file a site scaffold generates, and the text it holds. */
|
|
21
|
+
export declare function generatedSiteFiles(framework: FrameworkSpec, site: SiteTarget): Map<string, string>;
|
|
22
|
+
/** The framework's `nifra.config.ts`, declaring the target `nifra build` emits. */
|
|
23
|
+
export declare function withTarget(config: string, site: SiteTarget): string;
|
|
24
|
+
export interface MaterializeOptions extends SiteTarget {
|
|
22
25
|
/** Overwrite an existing destination. Default false, which refuses rather than clobbers. */
|
|
23
26
|
readonly force?: boolean;
|
|
24
27
|
}
|
|
@@ -29,5 +32,5 @@ export interface MaterializeOptions {
|
|
|
29
32
|
* `errorOnExist` did that before this was generated, and losing it would turn a mistyped path into
|
|
30
33
|
* silent data loss in someone's working directory. `--force` is what `bun create nifra .` needs.
|
|
31
34
|
*/
|
|
32
|
-
export declare function materializeSite(target: string, id: string, options
|
|
35
|
+
export declare function materializeSite(target: string, id: string, options: MaterializeOptions): Promise<void>;
|
|
33
36
|
//# sourceMappingURL=site.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../src/scaffold/site.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../../src/scaffold/site.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,aAAa,EAAiB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAA;AAGxB;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAM9C,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAAqC,CAAA;AAEvF,gGAAgG;AAChG,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAAiC,CAAA;AAE9E,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAI/D;AAGD,8FAA8F;AAC9F,eAAO,MAAM,aAAa,QAA0C,CAAA;AACpE,wBAAgB,UAAU,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAI3D;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,aAAa,EACxB,IAAI,EAAE,UAAU,GACf,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQrB;AAcD,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAEnE;AAED,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
package/dist/scaffold/site.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Compose a site scaffold: shared base, generated files, framework overlay.
|
|
2
|
+
* Compose a site scaffold: shared base, generated files, framework overlay, target files.
|
|
3
3
|
*
|
|
4
|
-
* A site is
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A site is the same few files whatever it renders with or deploys to - the backend, the assurance
|
|
5
|
+
* config, the landing page's backend half - plus what is mechanical enough to emit from a model (the
|
|
6
|
+
* manifest, the tsconfig, the adapter module, the target's config), and the files that are genuinely
|
|
7
|
+
* the framework's own. There is no per-runtime server entry or build script: `nifra build` generates
|
|
8
|
+
* the target's entry from `backend/` and `routes/`.
|
|
8
9
|
*
|
|
9
|
-
* The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* The framework files stay literal on purpose. `nifra.config.ts` explains why Solid wants a `solid`
|
|
11
|
+
* resolve condition and what `@preact/preset-vite` is; the routes are the app a user reads first. That
|
|
12
|
+
* prose belongs in a file you can open, not in a TypeScript string.
|
|
12
13
|
*/
|
|
13
|
-
import { cp, mkdir, writeFile } from "node:fs/promises";
|
|
14
|
+
import { cp, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
14
15
|
import { dirname, join } from "node:path";
|
|
15
16
|
import { fileURLToPath } from "node:url";
|
|
16
17
|
import { frameworkSpec } from "./frameworks.js";
|
|
17
|
-
import {
|
|
18
|
-
import { renderFrameworkModule, renderPackageJson, renderTsconfig } from "./site-files.js";
|
|
18
|
+
import { starterRouteTypes } from "./route-types.js";
|
|
19
|
+
import { renderFrameworkModule, renderPackageJson, renderTsconfig, } from "./site-files.js";
|
|
20
|
+
import { targetFiles } from "./targets.js";
|
|
19
21
|
/**
|
|
20
22
|
* Files identical in every site scaffold, taken from the base directory.
|
|
21
23
|
*
|
|
@@ -24,23 +26,15 @@ import { renderFrameworkModule, renderPackageJson, renderTsconfig } from "./site
|
|
|
24
26
|
* here, and `site-composition.test.ts` fails if this list and the directory disagree.
|
|
25
27
|
*/
|
|
26
28
|
export const SHARED_SITE_FILES = [
|
|
27
|
-
".
|
|
28
|
-
"
|
|
29
|
-
"_worker.ts",
|
|
30
|
-
"backend.ts",
|
|
31
|
-
"counter.ts",
|
|
32
|
-
"deno.json",
|
|
29
|
+
"backend/app.ts",
|
|
30
|
+
"backend/counter.ts",
|
|
33
31
|
"gitignore",
|
|
34
32
|
"nifra.assurance.ts",
|
|
35
|
-
"
|
|
36
|
-
"server-deno.ts",
|
|
37
|
-
"server-node.ts",
|
|
38
|
-
"server-vercel.ts",
|
|
39
|
-
"wrangler.toml",
|
|
33
|
+
"routes/index.backend.ts",
|
|
40
34
|
];
|
|
41
35
|
/** Files each framework supplies itself, relative to its overlay directory. */
|
|
42
36
|
export const FRAMEWORK_SITE_FILES = ["README.md", "nifra.config.ts"];
|
|
43
|
-
/** Route files, whose extension is the framework's own. */
|
|
37
|
+
/** Route frontend files, whose extension is the framework's own. The backend half is shared. */
|
|
44
38
|
export const ROUTE_BASENAMES = ["_404", "_layout", "index"];
|
|
45
39
|
/** Extension the framework's route files carry. */
|
|
46
40
|
export function routeExtension(framework) {
|
|
@@ -58,16 +52,33 @@ export function overlayDir(framework) {
|
|
|
58
52
|
? SITE_BASE_DIR
|
|
59
53
|
: join(here, "..", "..", `template-site-${framework.id}`);
|
|
60
54
|
}
|
|
61
|
-
/** Every file a site scaffold
|
|
62
|
-
export function generatedSiteFiles(framework) {
|
|
55
|
+
/** Every file a site scaffold generates, and the text it holds. */
|
|
56
|
+
export function generatedSiteFiles(framework, site) {
|
|
63
57
|
const files = new Map();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
files.set("framework.ts", renderFrameworkModule(framework));
|
|
67
|
-
files.set("package.json", renderPackageJson(framework));
|
|
58
|
+
files.set("backend/framework.ts", renderFrameworkModule(framework));
|
|
59
|
+
files.set("package.json", renderPackageJson(framework, site));
|
|
68
60
|
files.set("tsconfig.json", renderTsconfig(framework));
|
|
61
|
+
for (const [file, text] of targetFiles(site.target, site.name, site.docker))
|
|
62
|
+
files.set(file, text);
|
|
63
|
+
for (const [file, text] of starterRouteTypes(routeExtension(framework)))
|
|
64
|
+
files.set(file, text);
|
|
69
65
|
return files;
|
|
70
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Declared for every target, not just the edge ones, so `nifra target cloudflare` later keeps the
|
|
69
|
+
* shared backend's rate limit working without a second edit.
|
|
70
|
+
*/
|
|
71
|
+
const CLIENT_IP_DECLARATION = `// Cloudflare and Vercel expose no socket: the caller's address arrives in a header their edge
|
|
72
|
+
// overwrites (\`cf-connecting-ip\` / \`x-real-ip\`), and this trusts it so backend/app.ts's rate limit can
|
|
73
|
+
// tell visitors apart. Without it an edge build has no caller address and the limit refuses every
|
|
74
|
+
// request. It holds only while that edge is the one way in - served any other way (\`wrangler pages
|
|
75
|
+
// dev\`, your own workerd) the header is whatever the client sent. Bun, Node and Deno use the socket.
|
|
76
|
+
export const clientIp = "platform"
|
|
77
|
+
`;
|
|
78
|
+
/** The framework's `nifra.config.ts`, declaring the target `nifra build` emits. */
|
|
79
|
+
export function withTarget(config, site) {
|
|
80
|
+
return `${config.replace(/\n*$/, "\n")}\n${CLIENT_IP_DECLARATION}\n// The deploy target \`nifra build\` emits; \`nifra target <t>\` switches it.\nexport const target = "${site.target}"\n`;
|
|
81
|
+
}
|
|
71
82
|
/**
|
|
72
83
|
* Write a complete site scaffold into `target`.
|
|
73
84
|
*
|
|
@@ -75,21 +86,23 @@ export function generatedSiteFiles(framework) {
|
|
|
75
86
|
* `errorOnExist` did that before this was generated, and losing it would turn a mistyped path into
|
|
76
87
|
* silent data loss in someone's working directory. `--force` is what `bun create nifra .` needs.
|
|
77
88
|
*/
|
|
78
|
-
export async function materializeSite(target, id, options
|
|
89
|
+
export async function materializeSite(target, id, options) {
|
|
79
90
|
const framework = frameworkSpec(id);
|
|
80
91
|
const overlay = overlayDir(framework);
|
|
81
92
|
const force = options.force === true;
|
|
82
93
|
const copy = (from, to) => cp(from, to, force ? { force: true } : { errorOnExist: true, force: false });
|
|
83
94
|
// `wx` fails when the file exists, which is the write-side equivalent of `errorOnExist`.
|
|
84
95
|
const emit = (to, contents) => writeFile(to, contents, force ? {} : { flag: "wx" });
|
|
85
|
-
|
|
96
|
+
for (const dir of ["routes", "backend"])
|
|
97
|
+
await mkdir(join(target, dir), { recursive: true });
|
|
86
98
|
for (const file of SHARED_SITE_FILES)
|
|
87
99
|
await copy(join(SITE_BASE_DIR, file), join(target, file));
|
|
88
|
-
for (const [file, contents] of generatedSiteFiles(framework)) {
|
|
100
|
+
for (const [file, contents] of generatedSiteFiles(framework, options)) {
|
|
101
|
+
await mkdir(dirname(join(target, file)), { recursive: true });
|
|
89
102
|
await emit(join(target, file), contents);
|
|
90
103
|
}
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
await copy(join(overlay, "README.md"), join(target, "README.md"));
|
|
105
|
+
await emit(join(target, "nifra.config.ts"), withTarget(await readFile(join(overlay, "nifra.config.ts"), "utf8"), options));
|
|
93
106
|
const extension = routeExtension(framework);
|
|
94
107
|
for (const base of ROUTE_BASENAMES) {
|
|
95
108
|
const name = join("routes", `${base}.${extension}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/scaffold/site.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/scaffold/site.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a site deploys. A scaffold picks ONE target (`--target`, default `bun`): `nifra build` generates
|
|
3
|
+
* that target's server entry from `backend/` and `routes/`, so the app carries no per-runtime entry or
|
|
4
|
+
* build script - only the target's own config file, when it has one, and the scripts that run it.
|
|
5
|
+
* `nifra target <t>` switches it later.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEPLOY_TARGETS: readonly ["bun", "node", "deno", "cloudflare", "vercel"];
|
|
8
|
+
export type DeployTarget = (typeof DEPLOY_TARGETS)[number];
|
|
9
|
+
export interface TargetSpec {
|
|
10
|
+
readonly label: string;
|
|
11
|
+
/** `start` script: run the built app locally. */
|
|
12
|
+
readonly start?: string;
|
|
13
|
+
/** `deploy` script. Self-hosted targets have none unless the app is built into a Docker image. */
|
|
14
|
+
readonly deploy?: string;
|
|
15
|
+
/** `@nifrajs/*` packages the generated server entry imports beyond the shared set. */
|
|
16
|
+
readonly runtime?: readonly string[];
|
|
17
|
+
/** Whether `--docker` applies: a self-hosting server, not a platform's own runtime. */
|
|
18
|
+
readonly docker: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare const TARGETS: Readonly<Record<DeployTarget, TargetSpec>>;
|
|
21
|
+
export declare function isDeployTarget(value: string): value is DeployTarget;
|
|
22
|
+
/**
|
|
23
|
+
* The project name as a platform knows the app: a Docker image tag and a Cloudflare Pages or Deno Deploy
|
|
24
|
+
* project name take lowercase letters, digits, and "-", where the project itself may be `MyApp`,
|
|
25
|
+
* `my_app`, or `my.app`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function deployName(name: string): string;
|
|
28
|
+
/** The `deploy` script for `target`, or `undefined` when there is none. */
|
|
29
|
+
export declare function deployScript(target: DeployTarget, name: string, docker: boolean): string | undefined;
|
|
30
|
+
/** A Dockerfile that builds with Bun and runs the self-contained `dist/` on the target's runtime. */
|
|
31
|
+
export declare function renderDockerfile(target: "bun" | "node"): string;
|
|
32
|
+
export declare const DOCKERIGNORE = "node_modules\ndist\n.nifra\n.nifra-build\n.vercel\n.wrangler\n.git\n*.log\n";
|
|
33
|
+
/** `deno.json` for the Deno target: Bun builds, Deno runs and deploys the bundle. */
|
|
34
|
+
export declare const DENO_JSON: string;
|
|
35
|
+
/** `wrangler.toml` for the Cloudflare target; `NAME` is the Pages project. */
|
|
36
|
+
export declare const WRANGLER_TOML = "name = \"NAME\"\npages_build_output_dir = \"dist\"\n# 2025-04-01 or later: from that date `nodejs_compat` fills `process.env` from the project's variables,\n# where backend/app.ts reads NIFRA_ALLOW_MEMORY_RATE_LIMIT; until that variable is \"true\" the worker\n# refuses to start. Set it (a Pages variable, or a [vars] table here) only if a per-isolate rate limit\n# will do: Cloudflare runs many isolates and each counts alone. `bun run start` sets it for its one\n# local process.\ncompatibility_date = \"2025-04-01\"\ncompatibility_flags = [\"nodejs_compat\"]\n";
|
|
37
|
+
/** The config files `target` needs at the app root, by name. */
|
|
38
|
+
export declare function targetFiles(target: DeployTarget, name: string, docker: boolean): Map<string, string>;
|
|
39
|
+
//# sourceMappingURL=targets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../../src/scaffold/targets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,cAAc,0DAA2D,CAAA;AACtF,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,kGAAkG;IAClG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACpC,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,eAAO,MAAM,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,CAiB9D,CAAA;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAC1B,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,GACd,MAAM,GAAG,SAAS,CAKpB;AAQD,qGAAqG;AACrG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAwB/D;AAED,eAAO,MAAM,YAAY,gFAQxB,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,SAAS,QAOjB,CAAA;AAEL,8EAA8E;AAC9E,eAAO,MAAM,aAAa,yjBASzB,CAAA;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CACzB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,GACd,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAUrB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a site deploys. A scaffold picks ONE target (`--target`, default `bun`): `nifra build` generates
|
|
3
|
+
* that target's server entry from `backend/` and `routes/`, so the app carries no per-runtime entry or
|
|
4
|
+
* build script - only the target's own config file, when it has one, and the scripts that run it.
|
|
5
|
+
* `nifra target <t>` switches it later.
|
|
6
|
+
*/
|
|
7
|
+
export const DEPLOY_TARGETS = ["bun", "node", "deno", "cloudflare", "vercel"];
|
|
8
|
+
export const TARGETS = {
|
|
9
|
+
bun: { label: "Bun", start: "bun dist/server.js", docker: true },
|
|
10
|
+
node: { label: "Node", start: "node dist/server.js", runtime: ["node"], docker: true },
|
|
11
|
+
deno: {
|
|
12
|
+
label: "Deno Deploy",
|
|
13
|
+
start: "deno run --allow-net --allow-read --allow-env dist/server.js",
|
|
14
|
+
deploy: "deployctl deploy --prod --entrypoint=dist/server.js",
|
|
15
|
+
docker: false,
|
|
16
|
+
},
|
|
17
|
+
cloudflare: {
|
|
18
|
+
label: "Cloudflare Pages",
|
|
19
|
+
// `pages dev` is only ever one local process - the single instance the memory rate limit allows.
|
|
20
|
+
start: "wrangler pages dev dist --binding NIFRA_ALLOW_MEMORY_RATE_LIMIT=true",
|
|
21
|
+
deploy: "wrangler pages deploy dist",
|
|
22
|
+
docker: false,
|
|
23
|
+
},
|
|
24
|
+
vercel: { label: "Vercel", deploy: "vercel deploy --prebuilt", docker: false },
|
|
25
|
+
};
|
|
26
|
+
export function isDeployTarget(value) {
|
|
27
|
+
return DEPLOY_TARGETS.includes(value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The project name as a platform knows the app: a Docker image tag and a Cloudflare Pages or Deno Deploy
|
|
31
|
+
* project name take lowercase letters, digits, and "-", where the project itself may be `MyApp`,
|
|
32
|
+
* `my_app`, or `my.app`.
|
|
33
|
+
*/
|
|
34
|
+
export function deployName(name) {
|
|
35
|
+
return name
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
38
|
+
.replace(/-+$/, "");
|
|
39
|
+
}
|
|
40
|
+
/** The `deploy` script for `target`, or `undefined` when there is none. */
|
|
41
|
+
export function deployScript(target, name, docker) {
|
|
42
|
+
const image = deployName(name);
|
|
43
|
+
return docker
|
|
44
|
+
? `docker build -t ${image} . && docker run -p 3000:3000 ${image}`
|
|
45
|
+
: TARGETS[target].deploy;
|
|
46
|
+
}
|
|
47
|
+
/** The image a Docker scaffold runs the built server in. */
|
|
48
|
+
const DOCKER_RUN = {
|
|
49
|
+
bun: { image: "oven/bun:1-slim", user: "bun", cmd: '["bun", "dist/server.js"]' },
|
|
50
|
+
node: { image: "node:22-slim", user: "node", cmd: '["node", "dist/server.js"]' },
|
|
51
|
+
};
|
|
52
|
+
/** A Dockerfile that builds with Bun and runs the self-contained `dist/` on the target's runtime. */
|
|
53
|
+
export function renderDockerfile(target) {
|
|
54
|
+
const run = DOCKER_RUN[target];
|
|
55
|
+
return `# Build with Bun (nifra's build toolchain), then run the self-contained bundle - the final image
|
|
56
|
+
# carries no build tools or node_modules.
|
|
57
|
+
# docker build -t my-nifra-app .
|
|
58
|
+
# docker run -p 3000:3000 my-nifra-app
|
|
59
|
+
FROM oven/bun:1 AS build
|
|
60
|
+
WORKDIR /app
|
|
61
|
+
# Copy manifests first so the install layer caches across source-only changes.
|
|
62
|
+
COPY package.json bun.lock* ./
|
|
63
|
+
RUN bun install
|
|
64
|
+
COPY . .
|
|
65
|
+
RUN bun run build
|
|
66
|
+
|
|
67
|
+
FROM ${run.image} AS run
|
|
68
|
+
WORKDIR /app
|
|
69
|
+
ENV NODE_ENV=production
|
|
70
|
+
ENV PORT=3000
|
|
71
|
+
COPY --from=build /app/dist ./dist
|
|
72
|
+
# The bundle needs no elevated privileges; a compromised request handler should not inherit root.
|
|
73
|
+
USER ${run.user}
|
|
74
|
+
EXPOSE 3000
|
|
75
|
+
CMD ${run.cmd}
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
export const DOCKERIGNORE = `node_modules
|
|
79
|
+
dist
|
|
80
|
+
.nifra
|
|
81
|
+
.nifra-build
|
|
82
|
+
.vercel
|
|
83
|
+
.wrangler
|
|
84
|
+
.git
|
|
85
|
+
*.log
|
|
86
|
+
`;
|
|
87
|
+
/** `deno.json` for the Deno target: Bun builds, Deno runs and deploys the bundle. */
|
|
88
|
+
export const DENO_JSON = `${JSON.stringify({
|
|
89
|
+
tasks: { start: TARGETS.deno.start },
|
|
90
|
+
"//": "Build with Bun (`bun run build`), run the self-contained bundle on Deno. Deploy: deployctl deploy --prod --entrypoint=dist/server.js",
|
|
91
|
+
}, null, 2)}\n`;
|
|
92
|
+
/** `wrangler.toml` for the Cloudflare target; `NAME` is the Pages project. */
|
|
93
|
+
export const WRANGLER_TOML = `name = "NAME"
|
|
94
|
+
pages_build_output_dir = "dist"
|
|
95
|
+
# 2025-04-01 or later: from that date \`nodejs_compat\` fills \`process.env\` from the project's variables,
|
|
96
|
+
# where backend/app.ts reads NIFRA_ALLOW_MEMORY_RATE_LIMIT; until that variable is "true" the worker
|
|
97
|
+
# refuses to start. Set it (a Pages variable, or a [vars] table here) only if a per-isolate rate limit
|
|
98
|
+
# will do: Cloudflare runs many isolates and each counts alone. \`bun run start\` sets it for its one
|
|
99
|
+
# local process.
|
|
100
|
+
compatibility_date = "2025-04-01"
|
|
101
|
+
compatibility_flags = ["nodejs_compat"]
|
|
102
|
+
`;
|
|
103
|
+
/** The config files `target` needs at the app root, by name. */
|
|
104
|
+
export function targetFiles(target, name, docker) {
|
|
105
|
+
const files = new Map();
|
|
106
|
+
if (target === "deno")
|
|
107
|
+
files.set("deno.json", DENO_JSON);
|
|
108
|
+
if (target === "cloudflare")
|
|
109
|
+
files.set("wrangler.toml", WRANGLER_TOML.replace("NAME", deployName(name)));
|
|
110
|
+
if (docker && (target === "bun" || target === "node")) {
|
|
111
|
+
files.set("Dockerfile", renderDockerfile(target));
|
|
112
|
+
files.set(".dockerignore", DOCKERIGNORE);
|
|
113
|
+
}
|
|
114
|
+
return files;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.js","sourceRoot":"","sources":["../../src/scaffold/targets.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nifra",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Scaffold a new nifra app - `bun create nifra <dir>`.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"cli"
|
|
18
18
|
],
|
|
19
19
|
"type": "module",
|
|
20
|
+
"nifra": {
|
|
21
|
+
"environment": "backend"
|
|
22
|
+
},
|
|
20
23
|
"bin": {
|
|
21
24
|
"create-nifra": "./dist/cli.js"
|
|
22
25
|
},
|