primate 0.32.7 → 0.33.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.
- package/lib/app.tsconfig.json +55 -0
- package/lib/bin.d.ts +3 -0
- package/{src → lib}/bin.js +1 -0
- package/lib/commands/build.d.ts +4 -0
- package/lib/commands/build.js +9 -0
- package/lib/commands/dev.d.ts +3 -0
- package/lib/commands/dev.js +8 -0
- package/lib/commands/index.d.ts +3 -0
- package/lib/commands/index.js +13 -0
- package/lib/commands/init.d.ts +2 -0
- package/lib/commands/init.js +348 -0
- package/lib/commands/serve.d.ts +3 -0
- package/lib/commands/serve.js +13 -0
- package/lib/commands/test.d.ts +3 -0
- package/lib/commands/test.js +133 -0
- package/lib/init.d.ts +3 -0
- package/lib/init.js +12 -0
- package/lib/private/test.d.ts +32 -0
- package/lib/private/test.js +8 -0
- package/lib/public/Loader.d.ts +2 -0
- package/lib/public/Loader.js +2 -0
- package/lib/public/Module.d.ts +2 -0
- package/lib/public/Module.js +2 -0
- package/lib/public/RequestFacade.d.ts +2 -0
- package/lib/public/RequestFacade.js +2 -0
- package/lib/public/client/app.d.ts +4 -0
- package/lib/public/client/app.js +3 -0
- package/lib/public/config/i18n.d.ts +2 -0
- package/lib/public/config/i18n.js +2 -0
- package/lib/public/config/session.d.ts +2 -0
- package/lib/public/config/session.js +2 -0
- package/lib/public/config.d.ts +2 -0
- package/lib/public/config.js +2 -0
- package/lib/public/database/default.d.ts +2 -0
- package/lib/public/database/default.js +2 -0
- package/lib/public/database/wrap.d.ts +2 -0
- package/lib/public/database/wrap.js +2 -0
- package/lib/public/i18n/locale.d.ts +2 -0
- package/lib/public/i18n/locale.js +2 -0
- package/lib/public/load-text.d.ts +4 -0
- package/lib/public/load-text.js +3 -0
- package/lib/public/request/Facade.d.ts +2 -0
- package/lib/public/request/Facade.js +2 -0
- package/lib/public/response/Status.d.ts +2 -0
- package/lib/public/response/Status.js +2 -0
- package/lib/public/response/binary.d.ts +2 -0
- package/lib/public/response/binary.js +2 -0
- package/lib/public/response/error.d.ts +2 -0
- package/lib/public/response/error.js +2 -0
- package/lib/public/response/json.d.ts +2 -0
- package/lib/public/response/json.js +2 -0
- package/lib/public/response/redirect.d.ts +2 -0
- package/lib/public/response/redirect.js +2 -0
- package/lib/public/response/sse.d.ts +2 -0
- package/lib/public/response/sse.js +2 -0
- package/lib/public/response/text.d.ts +2 -0
- package/lib/public/response/text.js +2 -0
- package/lib/public/response/view.d.ts +2 -0
- package/lib/public/response/view.js +2 -0
- package/lib/public/response/ws.d.ts +2 -0
- package/lib/public/response/ws.js +2 -0
- package/lib/public/response.d.ts +22 -0
- package/lib/public/response.js +19 -0
- package/lib/public/route.d.ts +3 -0
- package/lib/public/route.js +3 -0
- package/lib/public/router.d.ts +2 -0
- package/lib/public/router.js +2 -0
- package/lib/public/s/config.d.ts +2 -0
- package/lib/public/s/config.js +2 -0
- package/lib/public/s/internal.d.ts +2 -0
- package/lib/public/s/internal.js +2 -0
- package/lib/public/serve.d.ts +2 -0
- package/{src → lib}/public/serve.js +1 -0
- package/lib/public/session/Manager.d.ts +2 -0
- package/lib/public/session/Manager.js +2 -0
- package/lib/public/store.d.ts +4 -0
- package/lib/public/store.js +3 -0
- package/lib/public/symbol/config.d.ts +2 -0
- package/lib/public/symbol/config.js +2 -0
- package/lib/public/test.d.ts +2 -0
- package/lib/public/test.js +2 -0
- package/lib/public/wasm/instantiate.d.ts +2 -0
- package/lib/public/wasm/instantiate.js +2 -0
- package/lib/runtime/FileRef.d.ts +2 -0
- package/lib/runtime/FileRef.js +2 -0
- package/package.json +47 -23
- package/src/commands/build.js +0 -4
- package/src/commands/dev.js +0 -8
- package/src/commands/exports.js +0 -5
- package/src/commands/serve.js +0 -8
- package/src/handlers/error.js +0 -1
- package/src/handlers/json.js +0 -1
- package/src/handlers/redirect.js +0 -1
- package/src/handlers/sse.js +0 -1
- package/src/handlers/stream.js +0 -1
- package/src/handlers/text.js +0 -1
- package/src/handlers/view.js +0 -1
- package/src/handlers/ws.js +0 -1
- package/src/init.js +0 -12
- package/src/public/load-text.js +0 -3
- package/src/public/loader.js +0 -30
- package/src/public/serve-asset.js +0 -9
- package/src/runtime/file.js +0 -3
- package/types/index.d.ts +0 -89
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"baseUrl": "${configDir}",
|
|
5
|
+
"target": "esnext",
|
|
6
|
+
"module": "nodenext",
|
|
7
|
+
"moduleResolution": "nodenext",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"customConditions": ["apekit"],
|
|
11
|
+
"erasableSyntaxOnly": true,
|
|
12
|
+
"exactOptionalPropertyTypes": true,
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"paths": {
|
|
15
|
+
"#route/*": ["routes/*"],
|
|
16
|
+
"#component/*": [
|
|
17
|
+
"components/*.tsx",
|
|
18
|
+
"components/*.jsx",
|
|
19
|
+
"components/*.vue",
|
|
20
|
+
"components/*.svelte",
|
|
21
|
+
"components/*.component.ts",
|
|
22
|
+
"components/*.ts",
|
|
23
|
+
"components/*.js",
|
|
24
|
+
"components/*"
|
|
25
|
+
],
|
|
26
|
+
"#store/*": ["stores/*", "stores/*.ts", "stores/*.js"],
|
|
27
|
+
"#locale/*": ["locales/*", "locales/*.ts", "locales/*.js"],
|
|
28
|
+
"#module/*": ["modules/*", "modules/*.ts", "modules/*.js"],
|
|
29
|
+
"#config/*": ["config/*", "config/*.ts", "config/*.js"],
|
|
30
|
+
"#static/*": ["static/*", "static/*.ts", "static/*.js"],
|
|
31
|
+
"#database/*": ["config/database/*.ts", "config/database/*.js"],
|
|
32
|
+
"#app": ["config/app.ts", "config/app.js"],
|
|
33
|
+
"#session": ["config/session.ts", "config/session.js"],
|
|
34
|
+
"#i18n": ["config/i18n.ts", "config/i18n.js"],
|
|
35
|
+
"#database": [
|
|
36
|
+
"config/database/index.ts",
|
|
37
|
+
"config/database/index.js",
|
|
38
|
+
"config/database/default.js",
|
|
39
|
+
"config/database/default.ts"
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"include": [
|
|
44
|
+
"${configDir}/config",
|
|
45
|
+
"${configDir}/routes",
|
|
46
|
+
"${configDir}/components",
|
|
47
|
+
"${configDir}/stores",
|
|
48
|
+
"${configDir}/modules",
|
|
49
|
+
"${configDir}/test",
|
|
50
|
+
"${configDir}/static",
|
|
51
|
+
"${configDir}/primate.config.js",
|
|
52
|
+
"${configDir}/primate.config.ts"
|
|
53
|
+
],
|
|
54
|
+
"exclude": ["node_modules"]
|
|
55
|
+
}
|
package/lib/bin.d.ts
ADDED
package/{src → lib}/bin.js
RENAMED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import build from "@primate/core/build";
|
|
2
|
+
const T_FLAG = "--target=";
|
|
3
|
+
// build for production
|
|
4
|
+
export default (flags, mode = "production") => {
|
|
5
|
+
const target = flags.find(f => f.startsWith(T_FLAG))?.slice(T_FLAG.length)
|
|
6
|
+
?? "web";
|
|
7
|
+
return build(mode, target);
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import build from "./build.js";
|
|
2
|
+
import serve from "./serve.js";
|
|
3
|
+
// build for development and serve
|
|
4
|
+
export default async () => {
|
|
5
|
+
// will only serve is build is successful
|
|
6
|
+
await build(["--target=web"], "development") === true && serve();
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default as build } from "./build.js";
|
|
2
|
+
import { default as dev } from "./dev.js";
|
|
3
|
+
import { default as init } from "./init.js";
|
|
4
|
+
import { default as serve } from "./serve.js";
|
|
5
|
+
import { default as test } from "./test.js";
|
|
6
|
+
export default (name) => ({
|
|
7
|
+
build,
|
|
8
|
+
dev,
|
|
9
|
+
init,
|
|
10
|
+
serve,
|
|
11
|
+
test,
|
|
12
|
+
})[name] ?? dev;
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import cancel from "@rcompat/cli/prompts/cancel";
|
|
2
|
+
import intro from "@rcompat/cli/prompts/intro";
|
|
3
|
+
import isCancel from "@rcompat/cli/prompts/is-cancel";
|
|
4
|
+
import multiselect from "@rcompat/cli/prompts/multiselect";
|
|
5
|
+
import outro from "@rcompat/cli/prompts/outro";
|
|
6
|
+
import select from "@rcompat/cli/prompts/select";
|
|
7
|
+
import text from "@rcompat/cli/prompts/text";
|
|
8
|
+
import FileRef from "@rcompat/fs/FileRef";
|
|
9
|
+
import dedent from "@rcompat/string/dedent";
|
|
10
|
+
const FRONTEND_OPTIONS = [
|
|
11
|
+
{ label: "Angular", value: "angular" },
|
|
12
|
+
{ label: "Eta", value: "eta" },
|
|
13
|
+
{ label: "HTML", value: "html" },
|
|
14
|
+
{ label: "HTMX", value: "htmx" },
|
|
15
|
+
{ label: "Handlebars", value: "handlebars" },
|
|
16
|
+
{ label: "Markdown", value: "markdown" },
|
|
17
|
+
{ label: "Marko", value: "marko" },
|
|
18
|
+
{ label: "React", value: "react" },
|
|
19
|
+
{ label: "Solid", value: "solid" },
|
|
20
|
+
{ label: "Svelte", value: "svelte" },
|
|
21
|
+
{ label: "Voby", value: "voby" },
|
|
22
|
+
{ label: "Vue", value: "vue" },
|
|
23
|
+
{ label: "Web Components", value: "webc" },
|
|
24
|
+
];
|
|
25
|
+
const BACKEND_OPTIONS = [
|
|
26
|
+
{ label: "Go", value: "go" },
|
|
27
|
+
{ label: "Python", value: "python" },
|
|
28
|
+
{ label: "Ruby", value: "ruby" },
|
|
29
|
+
{ label: "Grain", value: "grain" },
|
|
30
|
+
];
|
|
31
|
+
const DATABASE_OPTIONS = [
|
|
32
|
+
{ label: "SQLite", value: "sqlite" },
|
|
33
|
+
{ label: "PostgreSQL", value: "postgresql" },
|
|
34
|
+
{ label: "MySQL", value: "mysql" },
|
|
35
|
+
{ label: "MongoDB", value: "mongodb" },
|
|
36
|
+
{ label: "SurrealDB", value: "surrealdb" },
|
|
37
|
+
];
|
|
38
|
+
// peer deps per frontend (npm names)
|
|
39
|
+
const FRONTEND_PEER_DEPS = {
|
|
40
|
+
angular: [],
|
|
41
|
+
eta: [],
|
|
42
|
+
html: [],
|
|
43
|
+
htmx: [],
|
|
44
|
+
handlebars: [],
|
|
45
|
+
markdown: [],
|
|
46
|
+
marko: [],
|
|
47
|
+
react: ["react", "react-dom"],
|
|
48
|
+
solid: ["solid-js"],
|
|
49
|
+
svelte: ["svelte"],
|
|
50
|
+
voby: [],
|
|
51
|
+
vue: ["vue"],
|
|
52
|
+
"webc": [],
|
|
53
|
+
};
|
|
54
|
+
export default async function init() {
|
|
55
|
+
intro("Create a new Primate app");
|
|
56
|
+
let directory;
|
|
57
|
+
let target;
|
|
58
|
+
while (true) {
|
|
59
|
+
const ans = await text({ message: "Directory to create app?", initial: "." });
|
|
60
|
+
if (typeof ans === "symbol" || isCancel(ans))
|
|
61
|
+
return cancel("Aborted.");
|
|
62
|
+
target = new FileRef(ans);
|
|
63
|
+
if (await empty(target)) {
|
|
64
|
+
directory = ans;
|
|
65
|
+
break; // Valid directory found, exit loop
|
|
66
|
+
}
|
|
67
|
+
// Directory not empty, show error but continue loop
|
|
68
|
+
console.log("Directory not empty, choose another.");
|
|
69
|
+
}
|
|
70
|
+
// frontends — Enter = skip (none)
|
|
71
|
+
const fronts = await multiselect({
|
|
72
|
+
message: "Choose frontend (press Enter to skip)",
|
|
73
|
+
options: FRONTEND_OPTIONS,
|
|
74
|
+
initial: [], // indices
|
|
75
|
+
});
|
|
76
|
+
if (typeof fronts === "symbol" || isCancel(fronts))
|
|
77
|
+
return cancel("Aborted.");
|
|
78
|
+
// backends — Enter = skip (none)
|
|
79
|
+
const backs = await multiselect({
|
|
80
|
+
message: "Choose backend (press Enter to skip)",
|
|
81
|
+
options: BACKEND_OPTIONS,
|
|
82
|
+
initial: [], // indices
|
|
83
|
+
});
|
|
84
|
+
if (typeof backs === "symbol" || isCancel(backs))
|
|
85
|
+
return cancel("Aborted.");
|
|
86
|
+
// runtime (must choose one)
|
|
87
|
+
const runtime = await select({
|
|
88
|
+
message: "Choose runtime",
|
|
89
|
+
options: [
|
|
90
|
+
{ label: "Node", value: "node" },
|
|
91
|
+
{ label: "Deno", value: "deno" },
|
|
92
|
+
{ label: "Bun", value: "bun" },
|
|
93
|
+
],
|
|
94
|
+
initial: 0,
|
|
95
|
+
});
|
|
96
|
+
if (typeof runtime === "symbol" || isCancel(runtime))
|
|
97
|
+
return cancel("Aborted.");
|
|
98
|
+
// database — Enter = skip (none); if multiple chosen, take the first
|
|
99
|
+
const dbChoices = await multiselect({
|
|
100
|
+
message: "Choose a database (press Enter to skip)",
|
|
101
|
+
options: DATABASE_OPTIONS,
|
|
102
|
+
initial: [], // indices
|
|
103
|
+
});
|
|
104
|
+
if (typeof dbChoices === "symbol" || isCancel(dbChoices))
|
|
105
|
+
return cancel("Aborted.");
|
|
106
|
+
const db = dbChoices[0];
|
|
107
|
+
// i18n
|
|
108
|
+
const i18n = await select({
|
|
109
|
+
message: "Enable i18n?",
|
|
110
|
+
options: [
|
|
111
|
+
{ label: "Yes", value: "yes" },
|
|
112
|
+
{ label: "No", value: "no" },
|
|
113
|
+
],
|
|
114
|
+
initial: 1,
|
|
115
|
+
});
|
|
116
|
+
if (typeof i18n === "symbol" || isCancel(i18n))
|
|
117
|
+
return cancel("Aborted.");
|
|
118
|
+
const withI18n = i18n === "yes";
|
|
119
|
+
// sessions
|
|
120
|
+
const sessions = await select({
|
|
121
|
+
message: "Configure sessions?",
|
|
122
|
+
options: [
|
|
123
|
+
{ label: "Yes", value: "yes" },
|
|
124
|
+
{ label: "No", value: "no" },
|
|
125
|
+
],
|
|
126
|
+
initial: 1,
|
|
127
|
+
});
|
|
128
|
+
if (typeof sessions === "symbol" || isCancel(sessions))
|
|
129
|
+
return cancel("Aborted.");
|
|
130
|
+
const withSessions = sessions === "yes";
|
|
131
|
+
// scaffold dirs
|
|
132
|
+
await target.create({ recursive: true });
|
|
133
|
+
await target.join("routes").create({ recursive: true });
|
|
134
|
+
await target.join("components").create({ recursive: true });
|
|
135
|
+
if (db)
|
|
136
|
+
await target.join("stores").create({ recursive: true });
|
|
137
|
+
// files
|
|
138
|
+
await gitignore(target);
|
|
139
|
+
await tsconfig_json(target);
|
|
140
|
+
await app_config(target, { fronts, backs, runtime });
|
|
141
|
+
if (withI18n)
|
|
142
|
+
await i18n_config(target);
|
|
143
|
+
if (withSessions)
|
|
144
|
+
await session_config(target);
|
|
145
|
+
if (db)
|
|
146
|
+
await database_config(target, db);
|
|
147
|
+
await package_json(target, { directory, runtime });
|
|
148
|
+
const packages = compute_packages({ fronts, backs, db });
|
|
149
|
+
const install = buildInstallCommand(runtime, packages, directory);
|
|
150
|
+
outro([
|
|
151
|
+
"Done, now run",
|
|
152
|
+
`\n ${install.print}`,
|
|
153
|
+
].join("\n"));
|
|
154
|
+
process.exit();
|
|
155
|
+
}
|
|
156
|
+
async function empty(directory) {
|
|
157
|
+
try {
|
|
158
|
+
if (!(await directory.exists()))
|
|
159
|
+
return true;
|
|
160
|
+
const entries = await directory.list();
|
|
161
|
+
return entries.length === 0;
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function gitignore(root) {
|
|
168
|
+
const gi = root.join(".gitignore");
|
|
169
|
+
await gi.directory.create({ recursive: true });
|
|
170
|
+
const content = [
|
|
171
|
+
"node_modules",
|
|
172
|
+
"build",
|
|
173
|
+
"dist",
|
|
174
|
+
".DS_Store",
|
|
175
|
+
"*.log",
|
|
176
|
+
"npm-debug.log*",
|
|
177
|
+
"yarn-debug.log*",
|
|
178
|
+
"yarn-error.log*",
|
|
179
|
+
"pnpm-debug.log*",
|
|
180
|
+
"",
|
|
181
|
+
].join("\n");
|
|
182
|
+
await gi.write(content);
|
|
183
|
+
}
|
|
184
|
+
async function app_config(root, c) {
|
|
185
|
+
const cfg = root.join("config").join("app.ts");
|
|
186
|
+
await cfg.directory.create({ recursive: true });
|
|
187
|
+
const frontendImports = c.fronts
|
|
188
|
+
.map((f) => `import ${toIdent(f)} from "@primate/${f}";`)
|
|
189
|
+
.join("\n");
|
|
190
|
+
const backendImports = c.backs
|
|
191
|
+
.map((b) => `import ${toIdent(b)} from "@primate/${b}";`)
|
|
192
|
+
.join("\n");
|
|
193
|
+
const modules = [
|
|
194
|
+
...c.fronts.map((f) => `${toIdent(f)}()`),
|
|
195
|
+
...c.backs.map((b) => `${toIdent(b)}()`),
|
|
196
|
+
];
|
|
197
|
+
const body = dedent `import config from "primate/config";
|
|
198
|
+
${frontendImports}
|
|
199
|
+
${backendImports}
|
|
200
|
+
|
|
201
|
+
export default config({
|
|
202
|
+
runtime: "${c.runtime}",
|
|
203
|
+
modules: [
|
|
204
|
+
${modules.join(",\n ")}
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
`;
|
|
208
|
+
await cfg.write(body);
|
|
209
|
+
}
|
|
210
|
+
// i18n scaffold: config + a default locale file
|
|
211
|
+
async function i18n_config(root) {
|
|
212
|
+
const locales = root.join("locales");
|
|
213
|
+
const en_us = locales.join("en-US.ts");
|
|
214
|
+
const i18i = root.join("config").join("i18n.ts");
|
|
215
|
+
await en_us.directory.create({ recursive: true });
|
|
216
|
+
await i18i.directory.create({ recursive: true });
|
|
217
|
+
const locale = `import locale from "primate/i18n/locale";
|
|
218
|
+
|
|
219
|
+
export default locale({
|
|
220
|
+
hi: "Hello",
|
|
221
|
+
placeheld: "Hello, {name}",
|
|
222
|
+
});
|
|
223
|
+
`;
|
|
224
|
+
await en_us.write(locale);
|
|
225
|
+
const config = `import en from "#locale/en-US";
|
|
226
|
+
import i18n from "primate/config/i18n";
|
|
227
|
+
|
|
228
|
+
export default i18n({
|
|
229
|
+
defaultLocale: "en-US",
|
|
230
|
+
locales: {
|
|
231
|
+
"en-US": en,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
`;
|
|
235
|
+
await i18i.write(config);
|
|
236
|
+
}
|
|
237
|
+
async function session_config(root) {
|
|
238
|
+
const file = root.join("config").join("session.ts");
|
|
239
|
+
await file.directory.create({ recursive: true });
|
|
240
|
+
const body = `import session from "primate/config/session";
|
|
241
|
+
|
|
242
|
+
export default session({});
|
|
243
|
+
`;
|
|
244
|
+
await file.write(body);
|
|
245
|
+
}
|
|
246
|
+
async function database_config(root, db) {
|
|
247
|
+
const file = root.join("config").join("database").join("index.ts");
|
|
248
|
+
await file.directory.create({ recursive: true });
|
|
249
|
+
const ident = toIdent(db);
|
|
250
|
+
const body = `import ${ident} from "@primate/${db}";
|
|
251
|
+
|
|
252
|
+
export default ${ident}();
|
|
253
|
+
`;
|
|
254
|
+
await file.write(body);
|
|
255
|
+
}
|
|
256
|
+
async function package_json(root, c) {
|
|
257
|
+
const pkgFile = root.join("package.json");
|
|
258
|
+
const pkg = {
|
|
259
|
+
name: safe(c.directory),
|
|
260
|
+
type: "module",
|
|
261
|
+
scripts: {},
|
|
262
|
+
};
|
|
263
|
+
if (c.runtime === "deno") {
|
|
264
|
+
pkg.scripts.start = "deno run -A npm:primate";
|
|
265
|
+
pkg.scripts.build = "deno run -A npm:primate build";
|
|
266
|
+
pkg.scripts.serve = "deno run -A npm:primate serve";
|
|
267
|
+
pkg.scripts.dev = "deno task start";
|
|
268
|
+
}
|
|
269
|
+
else if (c.runtime === "bun") {
|
|
270
|
+
pkg.scripts.start = "bunx --bun primate";
|
|
271
|
+
pkg.scripts.build = "bunx --bun primate build";
|
|
272
|
+
pkg.scripts.serve = "bunx --bun primate serve";
|
|
273
|
+
pkg.scripts.dev = "bun run start";
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
pkg.scripts.start = "npx primate";
|
|
277
|
+
pkg.scripts.build = "npx primate build";
|
|
278
|
+
pkg.scripts.serve = "npx primate serve";
|
|
279
|
+
pkg.scripts.dev = "npm run start";
|
|
280
|
+
}
|
|
281
|
+
await pkgFile.writeJSON(pkg);
|
|
282
|
+
}
|
|
283
|
+
function safe(s) {
|
|
284
|
+
return s.trim().toLowerCase().replace(/\s+/g, "-")
|
|
285
|
+
.replace(/[^a-z0-9._-]/g, "") || "primate-app";
|
|
286
|
+
}
|
|
287
|
+
function toIdent(token) {
|
|
288
|
+
// turn tokens like "web-components" into valid identifiers: "web_components"
|
|
289
|
+
return token.replace(/[^a-zA-Z0-9_$]/g, "_");
|
|
290
|
+
}
|
|
291
|
+
function compute_packages(args) {
|
|
292
|
+
const deps = new Set();
|
|
293
|
+
const devDeps = new Set();
|
|
294
|
+
deps.add("primate");
|
|
295
|
+
// Always add TypeScript as dev dependency
|
|
296
|
+
devDeps.add("typescript");
|
|
297
|
+
// frontends → @primate/<token> (+ peer deps)
|
|
298
|
+
for (const f of args.fronts) {
|
|
299
|
+
deps.add(`@primate/${f}`);
|
|
300
|
+
const extras = FRONTEND_PEER_DEPS[f] || [];
|
|
301
|
+
for (const extra of extras)
|
|
302
|
+
deps.add(extra);
|
|
303
|
+
}
|
|
304
|
+
// backends → @primate/<token>
|
|
305
|
+
for (const b of args.backs) {
|
|
306
|
+
deps.add(`@primate/${b}`);
|
|
307
|
+
}
|
|
308
|
+
// database → @primate/<token>, if selected
|
|
309
|
+
if (args.db)
|
|
310
|
+
deps.add(`@primate/${args.db}`);
|
|
311
|
+
return {
|
|
312
|
+
dependencies: Array.from(deps),
|
|
313
|
+
devDependencies: Array.from(devDeps),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function shQuote(p) {
|
|
317
|
+
// POSIX shell-safe quoting
|
|
318
|
+
return `'${p.replace(/'/g, "'\\''")}'`;
|
|
319
|
+
}
|
|
320
|
+
function buildInstallCommand(runtime, packages, dir) {
|
|
321
|
+
const { dependencies, devDependencies } = packages;
|
|
322
|
+
const allPkgs = [...dependencies, ...devDependencies];
|
|
323
|
+
if (allPkgs.length === 0) {
|
|
324
|
+
return { print: "No packages to install.", run: "" };
|
|
325
|
+
}
|
|
326
|
+
const cd = `cd ${shQuote(dir)} && `;
|
|
327
|
+
if (runtime === "bun") {
|
|
328
|
+
const depCmd = dependencies.length > 0 ? `bun add ${dependencies.join(" ")}` : "";
|
|
329
|
+
const devCmd = devDependencies.length > 0 ? `bun add -d ${devDependencies.join(" ")}` : "";
|
|
330
|
+
const commands = [depCmd, devCmd].filter(Boolean);
|
|
331
|
+
return { print: cd + commands.join(" && "), run: "" };
|
|
332
|
+
}
|
|
333
|
+
if (runtime === "deno") {
|
|
334
|
+
const inner = `deno add ${allPkgs.map((p) => `npm:${p}`).join(" ")}`;
|
|
335
|
+
return { print: cd + inner, run: "" };
|
|
336
|
+
}
|
|
337
|
+
// default: Node
|
|
338
|
+
const depCmd = dependencies.length > 0 ? `npm install ${dependencies.join(" ")}` : "";
|
|
339
|
+
const devCmd = devDependencies.length > 0 ? `npm install -D ${devDependencies.join(" ")}` : "";
|
|
340
|
+
const commands = [depCmd, devCmd].filter(Boolean);
|
|
341
|
+
return { print: cd + commands.join(" && "), run: "" };
|
|
342
|
+
}
|
|
343
|
+
async function tsconfig_json(root) {
|
|
344
|
+
await root.join("tsconfig.json").writeJSON({
|
|
345
|
+
extends: "primate/tsconfig",
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import FileRef from "@rcompat/fs/FileRef";
|
|
2
|
+
import root from "@rcompat/package/root";
|
|
3
|
+
const load = async () => {
|
|
4
|
+
try {
|
|
5
|
+
return await root();
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return FileRef.resolve();
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
// serve from build directory
|
|
12
|
+
export default async () => (await load()).join("./build/serve.js").import();
|
|
13
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { tests } from "#test";
|
|
2
|
+
import build from "@primate/core/build";
|
|
3
|
+
import green from "@rcompat/cli/color/green";
|
|
4
|
+
import red from "@rcompat/cli/color/red";
|
|
5
|
+
import root from "@rcompat/package/root";
|
|
6
|
+
import entries from "@rcompat/record/entries";
|
|
7
|
+
import equals from "@rcompat/test/equals";
|
|
8
|
+
import includes from "@rcompat/test/includes";
|
|
9
|
+
import serve from "./serve.js";
|
|
10
|
+
const directory = "test";
|
|
11
|
+
const fetch_options = { redirect: "manual" };
|
|
12
|
+
const first_error = (left, right) => {
|
|
13
|
+
const length = left.length > right.length ? right.length : left.length;
|
|
14
|
+
for (let i = 0; i < length; i++) {
|
|
15
|
+
if (left[i] !== right[i]) {
|
|
16
|
+
return i;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export default async () => {
|
|
21
|
+
await build("testing", "web");
|
|
22
|
+
const app = (await serve()).default;
|
|
23
|
+
const files = await (await root()).join(directory)
|
|
24
|
+
.list(({ path }) => path.endsWith(".ts") || path.endsWith(".js"));
|
|
25
|
+
// side effects
|
|
26
|
+
await Promise.all(files.map(file => file.import()));
|
|
27
|
+
for (const test of tests) {
|
|
28
|
+
let init;
|
|
29
|
+
let path;
|
|
30
|
+
const route = test.route;
|
|
31
|
+
if (typeof route === "string") {
|
|
32
|
+
path = route;
|
|
33
|
+
init = new Request(`${app.url}${route}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
path = route.url.replace(app.url, "");
|
|
37
|
+
init = new Request(route.url, {
|
|
38
|
+
body: route.body,
|
|
39
|
+
// @ts-expect-error nonsense
|
|
40
|
+
duplex: "half",
|
|
41
|
+
headers: route.headers,
|
|
42
|
+
method: route.method,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const response = await fetch(init, fetch_options);
|
|
46
|
+
const checks = [];
|
|
47
|
+
const mocked_response = {
|
|
48
|
+
body: {
|
|
49
|
+
equals(expected) {
|
|
50
|
+
checks.push(async () => {
|
|
51
|
+
const actual = await (typeof expected === "string"
|
|
52
|
+
? response.text()
|
|
53
|
+
: response.json());
|
|
54
|
+
return [equals(actual, expected), expected, actual];
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
includes(expected) {
|
|
58
|
+
checks.push(async () => {
|
|
59
|
+
const actual = await (typeof expected === "string"
|
|
60
|
+
? response.text()
|
|
61
|
+
: response.json());
|
|
62
|
+
const $expected = typeof expected === "string"
|
|
63
|
+
? expected.replaceAll("\n", "").replaceAll(" ", "")
|
|
64
|
+
: expected;
|
|
65
|
+
return [includes(actual, $expected), $expected, actual];
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
headers: {
|
|
70
|
+
get(name) {
|
|
71
|
+
const actual = response.headers.get(name);
|
|
72
|
+
return {
|
|
73
|
+
equals(expected) {
|
|
74
|
+
checks.push(() => {
|
|
75
|
+
return [equals(actual, expected), expected, actual];
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
includes(expected) {
|
|
79
|
+
checks.push(() => {
|
|
80
|
+
return [includes(actual, expected), expected, actual];
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
includes(expected) {
|
|
86
|
+
checks.push(() => {
|
|
87
|
+
const actual = Object.fromEntries(response.headers.entries());
|
|
88
|
+
const lowercased = entries(expected)
|
|
89
|
+
.keymap(([key]) => key.toLowerCase()).get();
|
|
90
|
+
return [includes(actual, lowercased), lowercased, actual];
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
status: {
|
|
95
|
+
equals(status) {
|
|
96
|
+
checks.push(() => {
|
|
97
|
+
return [response.status === status, status, response.status];
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
test.tester(mocked_response);
|
|
103
|
+
const results = await Promise.all(checks.map(async (check) => {
|
|
104
|
+
try {
|
|
105
|
+
return await check();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.log(error);
|
|
109
|
+
return [
|
|
110
|
+
false, "()", "test execution failed",
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
}));
|
|
114
|
+
const failed = results.find(result => !result[0]);
|
|
115
|
+
const verb = test.verb.toUpperCase();
|
|
116
|
+
if (failed !== undefined) {
|
|
117
|
+
const routeText = typeof test.route === "string"
|
|
118
|
+
? test.route
|
|
119
|
+
: new URL(test.route.url).pathname;
|
|
120
|
+
console.log(red(`${verb} ${routeText}`));
|
|
121
|
+
const expected = JSON.stringify(failed[1]);
|
|
122
|
+
const actual = JSON.stringify(failed[2]);
|
|
123
|
+
const n = first_error(expected, actual);
|
|
124
|
+
console.log(`expected: ${expected.slice(0, n)}${green(expected[n])}${expected.slice(n + 1)}`);
|
|
125
|
+
console.log(`actual: ${actual.slice(0, n)}${red(actual[n])}${actual.slice(n + 1)}`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
console.log(green(`${verb} ${path}`));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
await app.stop();
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=test.js.map
|
package/lib/init.d.ts
ADDED
package/lib/init.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import blue from "@rcompat/cli/color/blue";
|
|
2
|
+
import bold from "@rcompat/cli/color/bold";
|
|
3
|
+
import print from "@rcompat/cli/print";
|
|
4
|
+
import json from "@rcompat/package/json";
|
|
5
|
+
import find from "./commands/index.js";
|
|
6
|
+
export default async (...args) => {
|
|
7
|
+
const [command, ...flags] = args;
|
|
8
|
+
const { name, version, } = await (await json(import.meta.url)).json();
|
|
9
|
+
print(blue(bold(name)), blue(version), "\n");
|
|
10
|
+
find(command)(flags);
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import verbs from "@primate/core/request/verbs";
|
|
2
|
+
import type Dict from "@rcompat/type/Dict";
|
|
3
|
+
import type JSONValue from "@rcompat/type/JSONValue";
|
|
4
|
+
export type Body = JSONValue;
|
|
5
|
+
export type MockedResponse = {
|
|
6
|
+
body: {
|
|
7
|
+
equals(body: Body): void;
|
|
8
|
+
includes(body: Body): void;
|
|
9
|
+
};
|
|
10
|
+
headers: {
|
|
11
|
+
get(header: string): {
|
|
12
|
+
equals(value: string): void;
|
|
13
|
+
includes(value: string): void;
|
|
14
|
+
};
|
|
15
|
+
includes(headers: Dict<string>): void;
|
|
16
|
+
};
|
|
17
|
+
status: {
|
|
18
|
+
equals(status: number): void;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
type Verb = typeof verbs[number];
|
|
22
|
+
type Tester = (response: MockedResponse) => void;
|
|
23
|
+
type Route = string;
|
|
24
|
+
type Test = {
|
|
25
|
+
route: Request | Route;
|
|
26
|
+
tester: Tester;
|
|
27
|
+
verb: Verb;
|
|
28
|
+
};
|
|
29
|
+
export declare const tests: Test[];
|
|
30
|
+
declare const _default: { [K in Verb]: (path: Request | Route, tester: Tester) => void; };
|
|
31
|
+
export default _default;
|
|
32
|
+
//# sourceMappingURL=test.d.ts.map
|