@effindomv2/create-fui-as-app 0.1.3 → 0.1.5
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/build/templates/hello/asconfig.json +24 -0
- package/build/templates/hello/harness.ts +9 -0
- package/build/templates/hello/index.html +98 -0
- package/build/templates/hello/package.json +32 -0
- package/build/templates/hello/scripts/prepare-runtime.ts +18 -0
- package/build/templates/hello/scripts/smoke.ts +15 -0
- package/build/templates/hello/src/App.ts +7 -0
- package/build/templates/hello/src/HelloWorld.ts +105 -0
- package/build/templates/hello/src/fui/Fui.ts +1 -0
- package/build/templates/hello/src/fui/FuiBrowser.ts +1 -0
- package/build/templates/hello/src/fui/FuiExports.ts +1 -0
- package/build/templates/hello/src/fui/FuiPrimitives.ts +1 -0
- package/build/templates/hello/src/host/generated/HostEvents.ts +20 -0
- package/build/templates/hello/src/host/generated/HostServices.ts +8 -0
- package/build/templates/hello/src/host/host-events.ts +22 -0
- package/build/templates/hello/src/host/host-services.ts +18 -0
- package/build/templates/hello/tsconfig.json +9 -0
- package/build/templates/mvc/asconfig.json +24 -0
- package/build/templates/mvc/harness.ts +40 -0
- package/build/templates/mvc/index.html +11 -0
- package/build/templates/mvc/package.json +34 -0
- package/build/templates/mvc/route-shell.html +56 -0
- package/build/templates/mvc/scripts/prepare-runtime.ts +22 -0
- package/build/templates/mvc/scripts/smoke.ts +18 -0
- package/build/templates/mvc/src/fui/Fui.ts +1 -0
- package/build/templates/mvc/src/fui/FuiBrowser.ts +1 -0
- package/build/templates/mvc/src/fui/FuiExports.ts +1 -0
- package/build/templates/mvc/src/fui/FuiPrimitives.ts +1 -0
- package/build/templates/mvc/src/host/generated/HostEvents.ts +20 -0
- package/build/templates/mvc/src/host/generated/HostServices.ts +8 -0
- package/build/templates/mvc/src/host/host-events.ts +22 -0
- package/build/templates/mvc/src/host/host-services.ts +17 -0
- package/build/templates/mvc/src/routes/mvc/pages/home/HomeController.ts +55 -0
- package/build/templates/mvc/src/routes/mvc/pages/home/HomeModel.ts +8 -0
- package/build/templates/mvc/src/routes/mvc/pages/home/HomeView.ts +76 -0
- package/build/templates/mvc/src/routes/mvc/pages/settings/SettingsController.ts +27 -0
- package/build/templates/mvc/src/routes/mvc/pages/settings/SettingsModel.ts +6 -0
- package/build/templates/mvc/src/routes/mvc/pages/settings/SettingsView.ts +61 -0
- package/build/templates/mvc/src/routes/mvc/shared/design-system/MvcNavPill.ts +42 -0
- package/build/templates/mvc/src/routes/mvc/shared/design-system/MvcPrimaryButton.ts +19 -0
- package/build/templates/mvc/src/routes/mvc/shared/routes.ts +22 -0
- package/build/templates/mvc/src/routes/mvc_home.ts +20 -0
- package/build/templates/mvc/src/routes/mvc_settings.ts +20 -0
- package/build/templates/mvc/tsconfig.json +9 -0
- package/dist/scripts/sync-templates.d.ts +1 -0
- package/dist/scripts/sync-templates.js +297 -0
- package/dist/src/scaffold.d.ts +2 -0
- package/dist/src/scaffold.js +50 -5
- package/dist/src/templates.d.ts +2 -1
- package/dist/src/templates.js +39 -228
- package/dist/src/versions.d.ts +1 -1
- package/dist/src/versions.js +1 -1
- package/dist/tests/scaffold.test.js +32 -0
- package/package.json +6 -3
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { cpSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
5
|
+
const REPO_ROOT = resolve(PACKAGE_ROOT, "..", "..");
|
|
6
|
+
const FUI_AS_ROOT = join(REPO_ROOT, "v2", "fui-as");
|
|
7
|
+
const FUI_AS_TEMPLATES_ROOT = join(FUI_AS_ROOT, "templates");
|
|
8
|
+
const TEMPLATES_ROOT = join(PACKAGE_ROOT, "build", "templates");
|
|
9
|
+
const LEGACY_TEMPLATES_ROOT = join(PACKAGE_ROOT, "templates");
|
|
10
|
+
function writeTextFile(filePath, contents) {
|
|
11
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
12
|
+
writeFileSync(filePath, contents, "utf8");
|
|
13
|
+
}
|
|
14
|
+
function posixRelativeImport(fromFilePath, toFilePath) {
|
|
15
|
+
const fromDirectory = dirname(fromFilePath);
|
|
16
|
+
let specifier = relative(fromDirectory, toFilePath).replaceAll("\\", "/");
|
|
17
|
+
if (specifier.endsWith(".ts")) {
|
|
18
|
+
specifier = specifier.slice(0, -3);
|
|
19
|
+
}
|
|
20
|
+
if (!specifier.startsWith(".")) {
|
|
21
|
+
specifier = `./${specifier}`;
|
|
22
|
+
}
|
|
23
|
+
return specifier;
|
|
24
|
+
}
|
|
25
|
+
function rewriteSdkImportsInFile(filePath, templateSrcRoot) {
|
|
26
|
+
const original = readFileSync(filePath, "utf8");
|
|
27
|
+
const rewritten = original
|
|
28
|
+
.replace(/(['"])(?:\.\.\/)+src\/(Fui|FuiExports|FuiPrimitives|FuiBrowser)\1/g, (_full, quote, symbolName) => {
|
|
29
|
+
const targetPath = join(templateSrcRoot, "fui", `${symbolName}.ts`);
|
|
30
|
+
const relativeSpecifier = posixRelativeImport(filePath, targetPath);
|
|
31
|
+
return `${quote}${relativeSpecifier}${quote}`;
|
|
32
|
+
})
|
|
33
|
+
.replace(/(['"])(?:\.\.\/)+browser\/src\/(?:host-events|host-services)\1/g, '"@effindomv2/fui-as/browser"');
|
|
34
|
+
if (rewritten !== original) {
|
|
35
|
+
writeFileSync(filePath, rewritten, "utf8");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function walkFiles(root, callback) {
|
|
39
|
+
for (const entry of readdirSync(root)) {
|
|
40
|
+
const absolutePath = join(root, entry);
|
|
41
|
+
const stats = statSync(absolutePath);
|
|
42
|
+
if (stats.isDirectory()) {
|
|
43
|
+
walkFiles(absolutePath, callback);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
callback(absolutePath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function writeSharedSdkShims(templateRoot) {
|
|
50
|
+
const fuiRoot = join(templateRoot, "src", "fui");
|
|
51
|
+
writeTextFile(join(fuiRoot, "Fui.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/Fui";\n');
|
|
52
|
+
writeTextFile(join(fuiRoot, "FuiExports.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/FuiExports";\n');
|
|
53
|
+
writeTextFile(join(fuiRoot, "FuiPrimitives.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/src/FuiPrimitives";\n');
|
|
54
|
+
writeTextFile(join(fuiRoot, "FuiBrowser.ts"), 'export * from "../../node_modules/@effindomv2/fui-as/browser/src/index";\n');
|
|
55
|
+
}
|
|
56
|
+
function writeAsconfig(templateRoot) {
|
|
57
|
+
writeTextFile(join(templateRoot, "asconfig.json"), JSON.stringify({
|
|
58
|
+
targets: {
|
|
59
|
+
debug: {
|
|
60
|
+
debug: true,
|
|
61
|
+
exportRuntime: true,
|
|
62
|
+
bindings: "esm",
|
|
63
|
+
outFile: "public/app.wasm",
|
|
64
|
+
sourceMap: true,
|
|
65
|
+
textFile: "public/app.wat",
|
|
66
|
+
},
|
|
67
|
+
release: {
|
|
68
|
+
exportRuntime: true,
|
|
69
|
+
bindings: "esm",
|
|
70
|
+
optimizeLevel: 3,
|
|
71
|
+
outFile: "public/app.wasm",
|
|
72
|
+
shrinkLevel: 1,
|
|
73
|
+
sourceMap: false,
|
|
74
|
+
textFile: "public/app.wat",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
options: {
|
|
78
|
+
runtime: "stub",
|
|
79
|
+
},
|
|
80
|
+
}, null, 2) + "\n");
|
|
81
|
+
}
|
|
82
|
+
function writeTsconfig(templateRoot) {
|
|
83
|
+
writeTextFile(join(templateRoot, "tsconfig.json"), JSON.stringify({
|
|
84
|
+
extends: "assemblyscript/std/assembly.json",
|
|
85
|
+
compilerOptions: {
|
|
86
|
+
noEmit: true,
|
|
87
|
+
},
|
|
88
|
+
include: ["src/**/*.ts"],
|
|
89
|
+
}, null, 2) + "\n");
|
|
90
|
+
}
|
|
91
|
+
function writePackageJsonTemplate(templateRoot, scripts, description) {
|
|
92
|
+
writeTextFile(join(templateRoot, "package.json"), JSON.stringify({
|
|
93
|
+
name: "__PACKAGE_NAME__",
|
|
94
|
+
version: "0.1.0",
|
|
95
|
+
private: true,
|
|
96
|
+
type: "module",
|
|
97
|
+
description,
|
|
98
|
+
scripts,
|
|
99
|
+
dependencies: {
|
|
100
|
+
"@effindomv2/fui-as": "__FUI_AS_VERSION__",
|
|
101
|
+
"@effindomv2/runtime": "__RUNTIME_VERSION__",
|
|
102
|
+
},
|
|
103
|
+
devDependencies: {
|
|
104
|
+
assemblyscript: "0.28.17",
|
|
105
|
+
"chokidar-cli": "^3.0.0",
|
|
106
|
+
concurrently: "^9.2.1",
|
|
107
|
+
esbuild: "^0.27.7",
|
|
108
|
+
"http-server": "^14.1.1",
|
|
109
|
+
tsx: "^4.20.6",
|
|
110
|
+
},
|
|
111
|
+
}, null, 2) + "\n");
|
|
112
|
+
}
|
|
113
|
+
function writeHelloSupportFiles(templateRoot) {
|
|
114
|
+
writeTextFile(join(templateRoot, "scripts", "prepare-runtime.ts"), `import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
115
|
+
|
|
116
|
+
const outputDir = "public";
|
|
117
|
+
rmSync(outputDir, { recursive: true, force: true });
|
|
118
|
+
mkdirSync(\`\${outputDir}/runtime\`, { recursive: true });
|
|
119
|
+
|
|
120
|
+
cpSync("node_modules/@effindomv2/runtime/dist", \`\${outputDir}/runtime/dist\`, { recursive: true });
|
|
121
|
+
cpSync("node_modules/@effindomv2/runtime/dist/fonts", \`\${outputDir}/runtime/fonts\`, { recursive: true });
|
|
122
|
+
copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js", \`\${outputDir}/bridge.js\`);
|
|
123
|
+
if (existsSync("node_modules/@effindomv2/runtime/dist/bridge.js.map")) {
|
|
124
|
+
copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js.map", \`\${outputDir}/bridge.js.map\`);
|
|
125
|
+
}
|
|
126
|
+
writeFileSync(
|
|
127
|
+
\`\${outputDir}/effindom-runtime-config.js\`,
|
|
128
|
+
'window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, { manifestUrl: "./runtime/dist/effindom.v2.manifest.json" });\\n',
|
|
129
|
+
"utf8",
|
|
130
|
+
);
|
|
131
|
+
copyFileSync("index.html", \`\${outputDir}/index.html\`);
|
|
132
|
+
`);
|
|
133
|
+
writeTextFile(join(templateRoot, "scripts", "smoke.ts"), `import { accessSync } from "node:fs";
|
|
134
|
+
|
|
135
|
+
const expectedFiles = [
|
|
136
|
+
"public/index.html",
|
|
137
|
+
"public/harness.js",
|
|
138
|
+
"public/app.wasm",
|
|
139
|
+
"public/bridge.js",
|
|
140
|
+
"public/effindom-runtime-config.js",
|
|
141
|
+
"public/runtime/dist/effindom.v2.manifest.json",
|
|
142
|
+
"public/runtime/fonts/NotoSans-Regular.ttf",
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
for (const filePath of expectedFiles) {
|
|
146
|
+
accessSync(filePath);
|
|
147
|
+
}
|
|
148
|
+
`);
|
|
149
|
+
writePackageJsonTemplate(templateRoot, {
|
|
150
|
+
build: "npm run generate:host && npm run build:assets && npm run build:wasm && npm run build:harness",
|
|
151
|
+
"build:assets": "tsx scripts/prepare-runtime.ts",
|
|
152
|
+
"build:wasm": "asc src/App.ts --config asconfig.json --target release",
|
|
153
|
+
"build:harness": "esbuild harness.ts --bundle --format=esm --platform=browser --outfile=public/harness.js",
|
|
154
|
+
"generate:host-services": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-services.ts src/host/host-services.ts appHostServices src/host/generated/HostServices.ts ../../fui/FuiPrimitives",
|
|
155
|
+
"generate:host-events": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-events.ts src/host/host-events.ts appHostEvents src/host/generated/HostEvents.ts ../../fui/FuiPrimitives",
|
|
156
|
+
"generate:host": "npm run generate:host-services && npm run generate:host-events",
|
|
157
|
+
watch: 'chokidar "src/**/*.ts" "harness.ts" "index.html" "asconfig.json" --ignore "src/host/generated/**" -c "npm run build"',
|
|
158
|
+
serve: "http-server public -p 8080 -c-1",
|
|
159
|
+
dev: 'npm run build && concurrently -k -n watch,serve "npm run watch" "npm run serve"',
|
|
160
|
+
test: "npm run build && tsx scripts/smoke.ts",
|
|
161
|
+
}, "Scaffolded FUI-AS hello-world app");
|
|
162
|
+
}
|
|
163
|
+
function writeMvcSupportFiles(templateRoot) {
|
|
164
|
+
writeTextFile(join(templateRoot, "index.html"), `<!doctype html>
|
|
165
|
+
<html lang="en">
|
|
166
|
+
<head>
|
|
167
|
+
<meta charset="utf-8" />
|
|
168
|
+
<meta http-equiv="refresh" content="0; url=/mvc-home/" />
|
|
169
|
+
<title>FUI-AS MVC App</title>
|
|
170
|
+
</head>
|
|
171
|
+
<body>
|
|
172
|
+
<p>Redirecting to <a href="/mvc-home/">/mvc-home/</a>…</p>
|
|
173
|
+
</body>
|
|
174
|
+
</html>
|
|
175
|
+
`);
|
|
176
|
+
writeTextFile(join(templateRoot, "scripts", "prepare-runtime.ts"), `import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
177
|
+
|
|
178
|
+
const outputDir = "public";
|
|
179
|
+
rmSync(outputDir, { recursive: true, force: true });
|
|
180
|
+
mkdirSync(\`\${outputDir}/runtime\`, { recursive: true });
|
|
181
|
+
mkdirSync(\`\${outputDir}/mvc-home\`, { recursive: true });
|
|
182
|
+
mkdirSync(\`\${outputDir}/mvc-settings\`, { recursive: true });
|
|
183
|
+
|
|
184
|
+
cpSync("node_modules/@effindomv2/runtime/dist", \`\${outputDir}/runtime/dist\`, { recursive: true });
|
|
185
|
+
cpSync("node_modules/@effindomv2/runtime/dist/fonts", \`\${outputDir}/runtime/fonts\`, { recursive: true });
|
|
186
|
+
copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js", \`\${outputDir}/bridge.js\`);
|
|
187
|
+
if (existsSync("node_modules/@effindomv2/runtime/dist/bridge.js.map")) {
|
|
188
|
+
copyFileSync("node_modules/@effindomv2/runtime/dist/bridge.js.map", \`\${outputDir}/bridge.js.map\`);
|
|
189
|
+
}
|
|
190
|
+
writeFileSync(
|
|
191
|
+
\`\${outputDir}/effindom-runtime-config.js\`,
|
|
192
|
+
'window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, { manifestUrl: "./runtime/dist/effindom.v2.manifest.json" });\\n',
|
|
193
|
+
"utf8",
|
|
194
|
+
);
|
|
195
|
+
copyFileSync("index.html", \`\${outputDir}/index.html\`);
|
|
196
|
+
copyFileSync("route-shell.html", \`\${outputDir}/mvc-home/index.html\`);
|
|
197
|
+
copyFileSync("route-shell.html", \`\${outputDir}/mvc-settings/index.html\`);
|
|
198
|
+
`);
|
|
199
|
+
writeTextFile(join(templateRoot, "scripts", "smoke.ts"), `import { accessSync } from "node:fs";
|
|
200
|
+
|
|
201
|
+
const expectedFiles = [
|
|
202
|
+
"public/index.html",
|
|
203
|
+
"public/harness.js",
|
|
204
|
+
"public/mvc-home/index.html",
|
|
205
|
+
"public/mvc-settings/index.html",
|
|
206
|
+
"public/mvc-home.wasm",
|
|
207
|
+
"public/mvc-settings.wasm",
|
|
208
|
+
"public/bridge.js",
|
|
209
|
+
"public/effindom-runtime-config.js",
|
|
210
|
+
"public/runtime/dist/effindom.v2.manifest.json",
|
|
211
|
+
"public/runtime/fonts/NotoSans-Regular.ttf",
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
for (const filePath of expectedFiles) {
|
|
215
|
+
accessSync(filePath);
|
|
216
|
+
}
|
|
217
|
+
`);
|
|
218
|
+
writePackageJsonTemplate(templateRoot, {
|
|
219
|
+
build: "npm run generate:host && npm run build:assets && npm run build:wasm && npm run build:harness",
|
|
220
|
+
"build:assets": "tsx scripts/prepare-runtime.ts",
|
|
221
|
+
"build:wasm": "npm run build:wasm:home && npm run build:wasm:settings",
|
|
222
|
+
"build:wasm:home": "asc src/routes/mvc_home.ts --config asconfig.json --target release --outFile public/mvc-home.wasm",
|
|
223
|
+
"build:wasm:settings": "asc src/routes/mvc_settings.ts --config asconfig.json --target release --outFile public/mvc-settings.wasm",
|
|
224
|
+
"build:harness": "esbuild harness.ts --bundle --format=esm --platform=browser --outfile=public/harness.js",
|
|
225
|
+
"generate:host-services": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-services.ts src/host/host-services.ts appHostServices src/host/generated/HostServices.ts ../../fui/FuiPrimitives",
|
|
226
|
+
"generate:host-events": "tsx ./node_modules/@effindomv2/fui-as/scripts/generate-host-events.ts src/host/host-events.ts appHostEvents src/host/generated/HostEvents.ts ../../fui/FuiPrimitives",
|
|
227
|
+
"generate:host": "npm run generate:host-services && npm run generate:host-events",
|
|
228
|
+
watch: 'chokidar "src/**/*.ts" "harness.ts" "route-shell.html" "index.html" "asconfig.json" --ignore "src/host/generated/**" -c "npm run build"',
|
|
229
|
+
serve: "http-server public -p 8080 -c-1",
|
|
230
|
+
dev: 'npm run build && concurrently -k -n watch,serve "npm run watch" "npm run serve"',
|
|
231
|
+
test: "npm run build && tsx scripts/smoke.ts",
|
|
232
|
+
}, "Scaffolded FUI-AS MVC app");
|
|
233
|
+
}
|
|
234
|
+
function rewriteHarnessImports(filePath) {
|
|
235
|
+
const original = readFileSync(filePath, "utf8");
|
|
236
|
+
const rewritten = original
|
|
237
|
+
.replace(/(['"])(?:\.\.\/)+browser\/src\/common-harness\1/g, '"@effindomv2/fui-as/browser"')
|
|
238
|
+
.replace(/(['"])(?:\.\.\/)+browser\/src\/routed-harness\1/g, '"@effindomv2/fui-as/browser"')
|
|
239
|
+
.replaceAll("?v=midnight-6", "");
|
|
240
|
+
if (rewritten !== original) {
|
|
241
|
+
writeFileSync(filePath, rewritten, "utf8");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function normalizeGeneratedHeader(filePath, label) {
|
|
245
|
+
const original = readFileSync(filePath, "utf8");
|
|
246
|
+
const rewritten = original.replace(/^\/\/ Generated by .*$/m, `// Generated from ${label}.`);
|
|
247
|
+
if (rewritten !== original) {
|
|
248
|
+
writeFileSync(filePath, rewritten, "utf8");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function syncHelloTemplate() {
|
|
252
|
+
const templateRoot = join(TEMPLATES_ROOT, "hello");
|
|
253
|
+
const templateSrcRoot = join(templateRoot, "src");
|
|
254
|
+
rmSync(templateRoot, { recursive: true, force: true });
|
|
255
|
+
const sourceRoot = join(FUI_AS_TEMPLATES_ROOT, "demo-hello-world");
|
|
256
|
+
cpSync(join(sourceRoot, "src"), templateSrcRoot, { recursive: true });
|
|
257
|
+
cpSync(join(sourceRoot, "harness.ts"), join(templateRoot, "harness.ts"));
|
|
258
|
+
cpSync(join(sourceRoot, "index.html"), join(templateRoot, "index.html"));
|
|
259
|
+
writeSharedSdkShims(templateRoot);
|
|
260
|
+
writeAsconfig(templateRoot);
|
|
261
|
+
writeTsconfig(templateRoot);
|
|
262
|
+
writeHelloSupportFiles(templateRoot);
|
|
263
|
+
walkFiles(templateSrcRoot, (filePath) => {
|
|
264
|
+
if (filePath.endsWith(".ts")) {
|
|
265
|
+
rewriteSdkImportsInFile(filePath, templateSrcRoot);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
rewriteHarnessImports(join(templateRoot, "harness.ts"));
|
|
269
|
+
normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostEvents.ts"), "the scaffold host-events definition");
|
|
270
|
+
normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostServices.ts"), "the scaffold host-services definition");
|
|
271
|
+
}
|
|
272
|
+
function syncMvcTemplate() {
|
|
273
|
+
const templateRoot = join(TEMPLATES_ROOT, "mvc");
|
|
274
|
+
const templateSrcRoot = join(templateRoot, "src");
|
|
275
|
+
rmSync(templateRoot, { recursive: true, force: true });
|
|
276
|
+
const sourceRoot = join(FUI_AS_TEMPLATES_ROOT, "demo-mvc");
|
|
277
|
+
cpSync(join(sourceRoot, "src"), templateSrcRoot, { recursive: true });
|
|
278
|
+
cpSync(join(sourceRoot, "harness.ts"), join(templateRoot, "harness.ts"));
|
|
279
|
+
cpSync(join(sourceRoot, "route-shell.html"), join(templateRoot, "route-shell.html"));
|
|
280
|
+
writeSharedSdkShims(templateRoot);
|
|
281
|
+
writeAsconfig(templateRoot);
|
|
282
|
+
writeTsconfig(templateRoot);
|
|
283
|
+
writeMvcSupportFiles(templateRoot);
|
|
284
|
+
walkFiles(templateSrcRoot, (filePath) => {
|
|
285
|
+
if (filePath.endsWith(".ts")) {
|
|
286
|
+
rewriteSdkImportsInFile(filePath, templateSrcRoot);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
rewriteHarnessImports(join(templateRoot, "harness.ts"));
|
|
290
|
+
normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostEvents.ts"), "the scaffold host-events definition");
|
|
291
|
+
normalizeGeneratedHeader(join(templateSrcRoot, "host", "generated", "HostServices.ts"), "the scaffold host-services definition");
|
|
292
|
+
}
|
|
293
|
+
rmSync(TEMPLATES_ROOT, { recursive: true, force: true });
|
|
294
|
+
rmSync(LEGACY_TEMPLATES_ROOT, { recursive: true, force: true });
|
|
295
|
+
mkdirSync(TEMPLATES_ROOT, { recursive: true });
|
|
296
|
+
syncHelloTemplate();
|
|
297
|
+
syncMvcTemplate();
|
package/dist/src/scaffold.d.ts
CHANGED
package/dist/src/scaffold.js
CHANGED
|
@@ -18,7 +18,7 @@ function assertDirectoryIsEmpty(targetDirectory) {
|
|
|
18
18
|
}
|
|
19
19
|
export function createProject(options) {
|
|
20
20
|
assertDirectoryIsEmpty(options.targetDirectory);
|
|
21
|
-
const templateFiles = createTemplateFiles({
|
|
21
|
+
const templateFiles = createTemplateFiles(options.template ?? "hello", {
|
|
22
22
|
projectName: options.projectName,
|
|
23
23
|
packageName: normalizePackageName(options.projectName),
|
|
24
24
|
});
|
|
@@ -28,10 +28,54 @@ export function createProject(options) {
|
|
|
28
28
|
writeFileSync(absolutePath, contents, "utf8");
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
function parseCliOptions(argv) {
|
|
32
|
+
let requestedPath = null;
|
|
33
|
+
let template = "hello";
|
|
34
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
35
|
+
const argument = argv[index];
|
|
36
|
+
if (argument === "--template") {
|
|
37
|
+
const value = argv.at(index + 1);
|
|
38
|
+
if (value === undefined) {
|
|
39
|
+
return { requestedPath, template, error: "--template requires a value (hello or mvc)." };
|
|
40
|
+
}
|
|
41
|
+
if (value !== "hello" && value !== "mvc") {
|
|
42
|
+
return { requestedPath, template, error: `Unsupported template: ${value}` };
|
|
43
|
+
}
|
|
44
|
+
template = value;
|
|
45
|
+
index += 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (argument.startsWith("--template=")) {
|
|
49
|
+
const value = argument.slice("--template=".length);
|
|
50
|
+
if (value !== "hello" && value !== "mvc") {
|
|
51
|
+
return { requestedPath, template, error: `Unsupported template: ${value}` };
|
|
52
|
+
}
|
|
53
|
+
template = value;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (argument.startsWith("-")) {
|
|
57
|
+
return { requestedPath, template, error: `Unknown option: ${argument}` };
|
|
58
|
+
}
|
|
59
|
+
if (requestedPath !== null) {
|
|
60
|
+
return { requestedPath, template, error: `Unexpected argument: ${argument}` };
|
|
61
|
+
}
|
|
62
|
+
requestedPath = argument;
|
|
63
|
+
}
|
|
64
|
+
return { requestedPath, template, error: null };
|
|
65
|
+
}
|
|
66
|
+
function printUsage(logger) {
|
|
67
|
+
logger.error("Usage: create-fui-as-app <project-directory> [--template hello|mvc]");
|
|
68
|
+
}
|
|
31
69
|
export function runCli(argv, cwd, logger) {
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
34
|
-
logger.error(
|
|
70
|
+
const parsed = parseCliOptions(argv);
|
|
71
|
+
if (parsed.error !== null) {
|
|
72
|
+
logger.error(parsed.error);
|
|
73
|
+
printUsage(logger);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
const requestedPath = parsed.requestedPath;
|
|
77
|
+
if (requestedPath === null || requestedPath.length === 0) {
|
|
78
|
+
printUsage(logger);
|
|
35
79
|
return 1;
|
|
36
80
|
}
|
|
37
81
|
const targetDirectory = resolve(cwd, requestedPath);
|
|
@@ -40,13 +84,14 @@ export function runCli(argv, cwd, logger) {
|
|
|
40
84
|
createProject({
|
|
41
85
|
targetDirectory,
|
|
42
86
|
projectName,
|
|
87
|
+
template: parsed.template,
|
|
43
88
|
});
|
|
44
89
|
}
|
|
45
90
|
catch (error) {
|
|
46
91
|
logger.error(error instanceof Error ? error.message : String(error));
|
|
47
92
|
return 1;
|
|
48
93
|
}
|
|
49
|
-
logger.log(`Created ${projectName} at ${targetDirectory}`);
|
|
94
|
+
logger.log(`Created ${projectName} (${parsed.template} template) at ${targetDirectory}`);
|
|
50
95
|
logger.log("Next steps:");
|
|
51
96
|
logger.log(` cd ${requestedPath}`);
|
|
52
97
|
logger.log(" npm install");
|
package/dist/src/templates.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
export type TemplateName = "hello" | "mvc";
|
|
1
2
|
export interface TemplateContext {
|
|
2
3
|
readonly projectName: string;
|
|
3
4
|
readonly packageName: string;
|
|
4
5
|
}
|
|
5
|
-
export declare function createTemplateFiles(context: TemplateContext): Map<string, string>;
|
|
6
|
+
export declare function createTemplateFiles(template: TemplateName, context: TemplateContext): Map<string, string>;
|
package/dist/src/templates.js
CHANGED
|
@@ -1,232 +1,43 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
1
4
|
import { FUI_AS_VERSION, RUNTIME_VERSION } from "./versions.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
},
|
|
21
|
-
dependencies: {
|
|
22
|
-
"@effindomv2/fui-as": FUI_AS_VERSION,
|
|
23
|
-
"@effindomv2/runtime": RUNTIME_VERSION
|
|
24
|
-
},
|
|
25
|
-
devDependencies: {
|
|
26
|
-
assemblyscript: "0.28.17",
|
|
27
|
-
"chokidar-cli": "^3.0.0",
|
|
28
|
-
concurrently: "^9.2.1",
|
|
29
|
-
esbuild: "^0.27.7",
|
|
30
|
-
"http-server": "^14.1.1"
|
|
5
|
+
const TEMPLATE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "build", "templates");
|
|
6
|
+
function collectTemplateFiles(root, relativePath = "") {
|
|
7
|
+
const absolutePath = resolve(root, relativePath);
|
|
8
|
+
const stats = statSync(absolutePath);
|
|
9
|
+
if (stats.isFile()) {
|
|
10
|
+
return new Map([[relativePath, readFileSync(absolutePath, "utf8")]]);
|
|
11
|
+
}
|
|
12
|
+
const output = new Map();
|
|
13
|
+
for (const entry of readdirSync(absolutePath)) {
|
|
14
|
+
const nestedRelativePath = relativePath.length === 0 ? entry : `${relativePath}/${entry}`;
|
|
15
|
+
const nestedAbsolutePath = resolve(root, nestedRelativePath);
|
|
16
|
+
const nestedStats = statSync(nestedAbsolutePath);
|
|
17
|
+
if (nestedStats.isDirectory()) {
|
|
18
|
+
const nestedFiles = collectTemplateFiles(root, nestedRelativePath);
|
|
19
|
+
for (const [filePath, contents] of nestedFiles) {
|
|
20
|
+
output.set(filePath, contents);
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
31
23
|
}
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
const appTs = `import { Application } from "../node_modules/@effindomv2/fui-as/src/Fui";
|
|
35
|
-
export * from "../node_modules/@effindomv2/fui-as/src/FuiExports";
|
|
36
|
-
|
|
37
|
-
import { createHelloWorldPage } from "./HelloWorld";
|
|
38
|
-
|
|
39
|
-
Application.register((app) => app.page(createHelloWorldPage));
|
|
40
|
-
`;
|
|
41
|
-
const helloWorldTs = `import {
|
|
42
|
-
AlignItems,
|
|
43
|
-
Button,
|
|
44
|
-
Column,
|
|
45
|
-
JustifyContent,
|
|
46
|
-
rgb,
|
|
47
|
-
SelectionArea,
|
|
48
|
-
Text,
|
|
49
|
-
TextAlign,
|
|
50
|
-
Unit,
|
|
51
|
-
} from "../node_modules/@effindomv2/fui-as/src/Fui";
|
|
52
|
-
|
|
53
|
-
class HelloWorld {
|
|
54
|
-
private clickCount: i32 = 0;
|
|
55
|
-
private readonly counterLabel: Text = new Text("Clicked 0 times");
|
|
56
|
-
|
|
57
|
-
constructor() {
|
|
58
|
-
this.counterLabel.fontSize(20.0);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
buildPage(): SelectionArea {
|
|
62
|
-
const title = new Text("Hello world from FUI-AS")
|
|
63
|
-
.fontSize(36.0)
|
|
64
|
-
.textAlign(TextAlign.Center)
|
|
65
|
-
.width(100.0, Unit.Percent);
|
|
66
|
-
|
|
67
|
-
const subtitle = new Text("A tiny app in two files: App.ts + HelloWorld.ts")
|
|
68
|
-
.fontSize(16.0)
|
|
69
|
-
.textAlign(TextAlign.Center)
|
|
70
|
-
.width(100.0, Unit.Percent);
|
|
71
|
-
|
|
72
|
-
const button = new Button("Click me")
|
|
73
|
-
.margin(0.0, 18.0, 0.0, 12.0)
|
|
74
|
-
.onClickWith<HelloWorld>(this, (owner) => {
|
|
75
|
-
owner.clickCount += 1;
|
|
76
|
-
owner.counterLabel.text(
|
|
77
|
-
"Clicked " + owner.clickCount.toString() + " time" + (owner.clickCount == 1 ? "" : "s"),
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const note = new Text(
|
|
82
|
-
"For production apps, move to an explicit MVC structure once screens, state, or host integration grows.",
|
|
83
|
-
)
|
|
84
|
-
.fontSize(14.0)
|
|
85
|
-
.textAlign(TextAlign.Center)
|
|
86
|
-
.width(680.0, Unit.Pixel);
|
|
87
|
-
|
|
88
|
-
return new SelectionArea()
|
|
89
|
-
.fillWidth()
|
|
90
|
-
.fillHeight()
|
|
91
|
-
.child(
|
|
92
|
-
Column(
|
|
93
|
-
title,
|
|
94
|
-
subtitle,
|
|
95
|
-
button,
|
|
96
|
-
this.counterLabel,
|
|
97
|
-
note,
|
|
98
|
-
)
|
|
99
|
-
.fillWidth()
|
|
100
|
-
.fillHeight()
|
|
101
|
-
.padding(24.0, 24.0, 24.0, 24.0)
|
|
102
|
-
.justifyContent(JustifyContent.Center)
|
|
103
|
-
.alignItems(AlignItems.Center),
|
|
104
|
-
)
|
|
105
|
-
.bgColor(rgb(0, 0, 0)) as SelectionArea;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function createHelloWorldPage(): SelectionArea {
|
|
110
|
-
return new HelloWorld().buildPage();
|
|
111
|
-
}
|
|
112
|
-
`;
|
|
113
|
-
const harnessTs = `import { startHarness } from "@effindomv2/fui-as/browser";
|
|
114
|
-
|
|
115
|
-
startHarness({
|
|
116
|
-
wasmPath: "./app.wasm",
|
|
117
|
-
});
|
|
118
|
-
`;
|
|
119
|
-
const indexHtml = `<!doctype html>
|
|
120
|
-
<html lang="en">
|
|
121
|
-
<head>
|
|
122
|
-
<meta charset="utf-8" />
|
|
123
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
124
|
-
<title>FUI-AS Hello World</title>
|
|
125
|
-
<style>
|
|
126
|
-
html,
|
|
127
|
-
body {
|
|
128
|
-
width: 100%;
|
|
129
|
-
height: 100%;
|
|
130
|
-
margin: 0;
|
|
131
|
-
padding: 0;
|
|
132
|
-
overflow: hidden;
|
|
133
|
-
background: #0f172a;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
[data-effindom-canvas-size-source] {
|
|
137
|
-
position: fixed;
|
|
138
|
-
inset: 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
#fui-canvas {
|
|
142
|
-
display: block;
|
|
143
|
-
width: 100%;
|
|
144
|
-
height: 100%;
|
|
145
|
-
}
|
|
146
|
-
</style>
|
|
147
|
-
</head>
|
|
148
|
-
<body>
|
|
149
|
-
<main data-effindom-canvas-size-source>
|
|
150
|
-
<canvas id="fui-canvas"></canvas>
|
|
151
|
-
</main>
|
|
152
|
-
<script>
|
|
153
|
-
window.__effindomRuntime = Object.assign({}, window.__effindomRuntime, {
|
|
154
|
-
manifestUrl: "./runtime/dist/effindom.v2.manifest.json",
|
|
155
|
-
});
|
|
156
|
-
</script>
|
|
157
|
-
<script src="./runtime/dist/bridge.js"></script>
|
|
158
|
-
<script type="module" src="./harness.js"></script>
|
|
159
|
-
</body>
|
|
160
|
-
</html>
|
|
161
|
-
`;
|
|
162
|
-
const asconfigJson = `{
|
|
163
|
-
"targets": {
|
|
164
|
-
"debug": {
|
|
165
|
-
"debug": true,
|
|
166
|
-
"exportRuntime": true,
|
|
167
|
-
"bindings": "esm",
|
|
168
|
-
"outFile": "public/app.wasm",
|
|
169
|
-
"sourceMap": true,
|
|
170
|
-
"textFile": "public/app.wat"
|
|
171
|
-
},
|
|
172
|
-
"release": {
|
|
173
|
-
"exportRuntime": true,
|
|
174
|
-
"bindings": "esm",
|
|
175
|
-
"optimizeLevel": 3,
|
|
176
|
-
"outFile": "public/app.wasm",
|
|
177
|
-
"shrinkLevel": 1,
|
|
178
|
-
"sourceMap": false,
|
|
179
|
-
"textFile": "public/app.wat"
|
|
24
|
+
output.set(nestedRelativePath, readFileSync(nestedAbsolutePath, "utf8"));
|
|
180
25
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
rmSync(outputDir, { recursive: true, force: true });
|
|
199
|
-
mkdirSync(\`\${outputDir}/runtime\`, { recursive: true });
|
|
200
|
-
|
|
201
|
-
cpSync("node_modules/@effindomv2/runtime/dist", \`\${outputDir}/runtime/dist\`, { recursive: true });
|
|
202
|
-
cpSync("node_modules/@effindomv2/runtime/dist/fonts", \`\${outputDir}/runtime/fonts\`, { recursive: true });
|
|
203
|
-
copyFileSync("index.html", \`\${outputDir}/index.html\`);
|
|
204
|
-
`;
|
|
205
|
-
const smokeMjs = `import { accessSync } from "node:fs";
|
|
206
|
-
|
|
207
|
-
const expectedFiles = [
|
|
208
|
-
"public/index.html",
|
|
209
|
-
"public/harness.js",
|
|
210
|
-
"public/app.wasm",
|
|
211
|
-
"public/runtime/dist/bridge.js",
|
|
212
|
-
"public/runtime/dist/effindom.v2.manifest.json",
|
|
213
|
-
"public/runtime/fonts/NotoSans-Regular.ttf",
|
|
214
|
-
];
|
|
215
|
-
|
|
216
|
-
for (const filePath of expectedFiles) {
|
|
217
|
-
accessSync(filePath);
|
|
218
|
-
}
|
|
219
|
-
`;
|
|
220
|
-
export function createTemplateFiles(context) {
|
|
221
|
-
return new Map([
|
|
222
|
-
["package.json", renderPackageJson(context)],
|
|
223
|
-
["index.html", indexHtml],
|
|
224
|
-
["asconfig.json", asconfigJson],
|
|
225
|
-
["tsconfig.json", tsconfigJson],
|
|
226
|
-
["harness.ts", harnessTs],
|
|
227
|
-
["src/App.ts", appTs],
|
|
228
|
-
["src/HelloWorld.ts", helloWorldTs],
|
|
229
|
-
["scripts/prepare-runtime.mjs", prepareRuntimeMjs],
|
|
230
|
-
["scripts/smoke.mjs", smokeMjs],
|
|
231
|
-
]);
|
|
26
|
+
return output;
|
|
27
|
+
}
|
|
28
|
+
function replaceTemplateTokens(value, context) {
|
|
29
|
+
return value
|
|
30
|
+
.replaceAll("__PACKAGE_NAME__", context.packageName)
|
|
31
|
+
.replaceAll("__PROJECT_NAME__", context.projectName)
|
|
32
|
+
.replaceAll("__FUI_AS_VERSION__", FUI_AS_VERSION)
|
|
33
|
+
.replaceAll("__RUNTIME_VERSION__", RUNTIME_VERSION);
|
|
34
|
+
}
|
|
35
|
+
export function createTemplateFiles(template, context) {
|
|
36
|
+
const templateDirectory = resolve(TEMPLATE_ROOT, template);
|
|
37
|
+
const files = collectTemplateFiles(templateDirectory);
|
|
38
|
+
const output = new Map();
|
|
39
|
+
for (const [filePath, contents] of files) {
|
|
40
|
+
output.set(filePath, replaceTemplateTokens(contents, context));
|
|
41
|
+
}
|
|
42
|
+
return output;
|
|
232
43
|
}
|
package/dist/src/versions.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const FUI_AS_VERSION = "0.1.
|
|
1
|
+
export declare const FUI_AS_VERSION = "0.1.2";
|
|
2
2
|
export declare const RUNTIME_VERSION = "0.1.0";
|