@checkstack/scripts 0.4.2 → 0.5.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/package.json +1 -1
- package/src/commands/plugin-pack.ts +25 -1
- package/src/templates/backend/package.json.hbs +1 -1
- package/src/templates/common/package.json.hbs +1 -1
- package/src/templates/frontend/index.html.hbs +11 -0
- package/src/templates/frontend/package.json.hbs +10 -2
- package/src/templates/frontend/src/index.tsx.hbs +9 -0
- package/src/templates/frontend/src/preview.tsx.hbs +7 -0
- package/src/templates/frontend/vite.config.ts.hbs +65 -0
- package/src/templates.test.ts +5 -1
package/package.json
CHANGED
|
@@ -233,16 +233,23 @@ Options:
|
|
|
233
233
|
function runScriptIfPresent({
|
|
234
234
|
cwd,
|
|
235
235
|
script,
|
|
236
|
+
env,
|
|
236
237
|
}: {
|
|
237
238
|
cwd: string;
|
|
238
239
|
script: string;
|
|
240
|
+
/** Extra env vars merged over `process.env` for the spawned script. */
|
|
241
|
+
env?: NodeJS.ProcessEnv;
|
|
239
242
|
}): void {
|
|
240
243
|
const pkg = readJson<{ scripts?: Record<string, string> }>(
|
|
241
244
|
path.join(cwd, "package.json"),
|
|
242
245
|
);
|
|
243
246
|
if (!pkg.scripts?.[script]) return;
|
|
244
247
|
console.log(`▶ bun run ${script}`);
|
|
245
|
-
const r = spawnSync("bun", ["run", script], {
|
|
248
|
+
const r = spawnSync("bun", ["run", script], {
|
|
249
|
+
cwd,
|
|
250
|
+
stdio: "inherit",
|
|
251
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
252
|
+
});
|
|
246
253
|
if (r.status !== 0) {
|
|
247
254
|
throw new Error(`bun run ${script} exited with status ${r.status}`);
|
|
248
255
|
}
|
|
@@ -323,6 +330,23 @@ async function packPackage({
|
|
|
323
330
|
const pkg = JSON.parse(original) as InstallPackageMetadata &
|
|
324
331
|
RewritablePackageJson;
|
|
325
332
|
|
|
333
|
+
// Frontend plugins must be built into a Module Federation remote (vite build
|
|
334
|
+
// → dist/ with mf-manifest.json + remoteEntry + chunks) before packing, so
|
|
335
|
+
// the host can load them at runtime. Built with the ORIGINAL package.json
|
|
336
|
+
// (deps resolve from the installed node_modules); `bun pm pack` then includes
|
|
337
|
+
// the produced dist/. Other package types have no build step.
|
|
338
|
+
if (pkg.checkstack?.type === "frontend") {
|
|
339
|
+
// Force NODE_ENV=production: the Module Federation Vite plugin skips
|
|
340
|
+
// emitting the remote (no mf-manifest.json) when NODE_ENV is "test" — e.g.
|
|
341
|
+
// when pack runs inside a test runner that sets it. A pack is always a
|
|
342
|
+
// production build.
|
|
343
|
+
runScriptIfPresent({
|
|
344
|
+
cwd: pkgDir,
|
|
345
|
+
script: "build",
|
|
346
|
+
env: { NODE_ENV: "production" },
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
326
350
|
const { rewritten, unresolved } = await rewriteWorkspaceVersions({
|
|
327
351
|
pkg,
|
|
328
352
|
resolveVersion: createWorkspaceMapResolver({ workspaceMap }),
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": { "import": "./src/index.tsx" }
|
|
11
11
|
},
|
|
12
|
+
"files": ["dist"],
|
|
12
13
|
"checkstack": {
|
|
13
14
|
"type": "frontend",
|
|
14
15
|
"pluginId": "{{pluginBaseName}}"
|
|
15
16
|
},
|
|
16
17
|
"scripts": {
|
|
17
18
|
"dev": "checkstack-dev",
|
|
18
|
-
"
|
|
19
|
+
"build": "vite build",
|
|
20
|
+
"pack": "checkstack-scripts plugin-pack",
|
|
19
21
|
"typecheck": "tsgo -b",
|
|
20
22
|
"lint": "bun run lint:code",
|
|
21
23
|
"lint:code": "eslint . --max-warnings 0",
|
|
@@ -26,7 +28,9 @@
|
|
|
26
28
|
"@checkstack/common": "workspace:*",
|
|
27
29
|
"{{scoped pluginBaseName}}-common": "workspace:*",
|
|
28
30
|
"@checkstack/ui": "workspace:*",
|
|
31
|
+
"@tanstack/react-query": "^5.100.14",
|
|
29
32
|
"react": "^18.3.1",
|
|
33
|
+
"react-dom": "^18.3.1",
|
|
30
34
|
"react-router-dom": "^7.1.1",
|
|
31
35
|
"lucide-react": "^0.469.0"
|
|
32
36
|
},
|
|
@@ -36,8 +40,12 @@
|
|
|
36
40
|
"@checkstack/frontend": "workspace:*",
|
|
37
41
|
"@checkstack/tsconfig": "workspace:*",
|
|
38
42
|
"@checkstack/test-utils-frontend": "workspace:*",
|
|
43
|
+
"@module-federation/vite": "^1.16",
|
|
39
44
|
"@types/react": "^18.3.1",
|
|
45
|
+
"@types/react-dom": "^18.3.1",
|
|
40
46
|
"@playwright/test": "^1.49.0",
|
|
41
|
-
"
|
|
47
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
48
|
+
"typescript": "^5.7.2",
|
|
49
|
+
"vite": "^8.0.16"
|
|
42
50
|
}
|
|
43
51
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createFrontendPlugin } from "@checkstack/frontend-api";
|
|
2
|
+
import { Boxes } from "lucide-react";
|
|
2
3
|
import { {{pluginNameCamel}}Routes, pluginMetadata, {{pluginNameCamel}}Access } from "{{scoped pluginBaseName}}-common";
|
|
3
4
|
|
|
4
5
|
export default createFrontendPlugin({
|
|
@@ -18,6 +19,14 @@ export default createFrontendPlugin({
|
|
|
18
19
|
})),
|
|
19
20
|
title: "{{pluginNamePascal}}",
|
|
20
21
|
accessRule: {{pluginNameCamel}}Access.read,
|
|
22
|
+
// `nav` opts this route into the left sidebar. Without it the page is
|
|
23
|
+
// still reachable by URL but not listed. Pick the section heading
|
|
24
|
+
// (group) and icon that fit your plugin.
|
|
25
|
+
nav: {
|
|
26
|
+
group: "Workspace",
|
|
27
|
+
icon: Boxes,
|
|
28
|
+
label: "{{pluginNamePascal}}",
|
|
29
|
+
},
|
|
21
30
|
},
|
|
22
31
|
],
|
|
23
32
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Build entry for `vite build` only. The Checkstack host loads this plugin as
|
|
2
|
+
// a Module Federation remote (the exposed "./plugin" module), never this file.
|
|
3
|
+
const root = document.querySelector("#root");
|
|
4
|
+
if (root) {
|
|
5
|
+
root.textContent =
|
|
6
|
+
"This is the {{pluginNamePascal}} Checkstack plugin (a federated remote).";
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { federation } from "@module-federation/vite";
|
|
5
|
+
|
|
6
|
+
// Used by `bun run build` / `bun run pack` to build this plugin as a Module
|
|
7
|
+
// Federation REMOTE. Local dev uses `checkstack-dev` (@checkstack/dev-server),
|
|
8
|
+
// which compiles the plugin into the host app directly, so this file is only
|
|
9
|
+
// exercised at pack time.
|
|
10
|
+
const pkg = JSON.parse(
|
|
11
|
+
readFileSync(new URL("package.json", import.meta.url), "utf8"),
|
|
12
|
+
) as { name: string };
|
|
13
|
+
|
|
14
|
+
// Identifier-safe MF remote name. MUST match how the Checkstack host derives
|
|
15
|
+
// it from the package name (see @checkstack/frontend's plugin-loader).
|
|
16
|
+
const remoteName = pkg.name.replace(/^@/, "").replaceAll(/[^a-zA-Z0-9]/g, "_");
|
|
17
|
+
|
|
18
|
+
export default defineConfig({
|
|
19
|
+
// The host serves this plugin's assets under /assets/plugins/<package>/, so
|
|
20
|
+
// bake that base in to make the federation manifest's chunk URLs resolve.
|
|
21
|
+
base: `/assets/plugins/${pkg.name}/`,
|
|
22
|
+
plugins: [
|
|
23
|
+
react(),
|
|
24
|
+
federation({
|
|
25
|
+
name: remoteName,
|
|
26
|
+
filename: "remoteEntry.js",
|
|
27
|
+
manifest: true,
|
|
28
|
+
// No remote type generation: the host loads this plugin dynamically and
|
|
29
|
+
// never imports its types, and the DTS step (#TYPE-001) otherwise fails
|
|
30
|
+
// and can abort the build before the federation manifest is written.
|
|
31
|
+
dts: false,
|
|
32
|
+
// The host loads `<remoteName>/plugin`.
|
|
33
|
+
exposes: { "./plugin": "./src/index.tsx" },
|
|
34
|
+
// Reuse the host's singleton instances — MUST mirror the host's shared
|
|
35
|
+
// set (@checkstack/frontend vite.config.ts). @checkstack/ui is NOT shared
|
|
36
|
+
// wholesale: it is bundled (tree-shaken) and its contexts are unified via
|
|
37
|
+
// a registered (globalThis-keyed) context. The ONE exception is the
|
|
38
|
+
// CodeEditor (Monaco / VS Code) stack, shared as a singleton so this
|
|
39
|
+
// plugin reuses the host's editor (and its workers) instead of bundling
|
|
40
|
+
// Monaco. `import: false` makes it CONSUME-ONLY: the plugin never bundles
|
|
41
|
+
// a local fallback of the editor, so the heavy `@codingame/*` /
|
|
42
|
+
// `monaco-languageclient` / `vscode` subtree is never pulled into the
|
|
43
|
+
// plugin build (which is why no `vscode` alias or worker config is needed
|
|
44
|
+
// here). The host is the sole provider.
|
|
45
|
+
shared: {
|
|
46
|
+
react: { singleton: true, requiredVersion: "^18.0.0" },
|
|
47
|
+
"react-dom": { singleton: true, requiredVersion: "^18.0.0" },
|
|
48
|
+
"react-router-dom": { singleton: true, requiredVersion: "^7.0.0" },
|
|
49
|
+
"@tanstack/react-query": { singleton: true, requiredVersion: "^5.0.0" },
|
|
50
|
+
"@checkstack/frontend-api": { singleton: true, requiredVersion: false },
|
|
51
|
+
"@checkstack/ui/code-editor": {
|
|
52
|
+
singleton: true,
|
|
53
|
+
requiredVersion: false,
|
|
54
|
+
import: false,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
build: {
|
|
60
|
+
target: "esnext",
|
|
61
|
+
outDir: "dist",
|
|
62
|
+
emptyOutDir: true,
|
|
63
|
+
minify: true,
|
|
64
|
+
},
|
|
65
|
+
});
|
package/src/templates.test.ts
CHANGED
|
@@ -192,7 +192,11 @@ describe("CLI Template Scaffolding", () => {
|
|
|
192
192
|
const pkg = JSON.parse(
|
|
193
193
|
readFileSync(path.join(targetDir, "package.json"), "utf8"),
|
|
194
194
|
) as { scripts?: Record<string, string> };
|
|
195
|
-
|
|
195
|
+
// Uses the installed `checkstack-scripts` bin (from the
|
|
196
|
+
// @checkstack/scripts devDependency), NOT `bunx @checkstack/scripts`:
|
|
197
|
+
// bunx re-resolves/caches by version and can run a stale copy, whereas
|
|
198
|
+
// the local bin always matches the pinned devDependency.
|
|
199
|
+
expect(pkg.scripts?.pack).toBe("checkstack-scripts plugin-pack");
|
|
196
200
|
if (pluginType !== "common") {
|
|
197
201
|
// Backend/frontend templates pin the dev script to the
|
|
198
202
|
// `checkstack-dev` bin (provided by `@checkstack/dev-server`,
|