@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/scripts",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Checkstack tooling: plugin scaffolding, codegen, and the plugin-pack CLI used by external plugin authors.",
5
5
  "license": "Elastic-2.0",
6
6
  "type": "module",
@@ -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], { cwd, stdio: "inherit" });
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 }),
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "scripts": {
21
21
  "dev": "checkstack-dev",
22
- "pack": "bunx @checkstack/scripts plugin-pack",
22
+ "pack": "checkstack-scripts plugin-pack",
23
23
  "typecheck": "tsgo -b",
24
24
  "lint": "bun run lint:code",
25
25
  "lint:code": "eslint . --max-warnings 0",
@@ -14,7 +14,7 @@
14
14
  "pluginId": "{{pluginBaseName}}"
15
15
  },
16
16
  "scripts": {
17
- "pack": "bunx @checkstack/scripts plugin-pack",
17
+ "pack": "checkstack-scripts plugin-pack",
18
18
  "typecheck": "tsgo -b",
19
19
  "lint": "bun run lint:code",
20
20
  "lint:code": "eslint . --max-warnings 0"
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>{{pluginNamePascal}} (Checkstack plugin remote)</title>
6
+ </head>
7
+ <body>
8
+ <div id="root"></div>
9
+ <script type="module" src="/src/preview.tsx"></script>
10
+ </body>
11
+ </html>
@@ -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
- "pack": "bunx @checkstack/scripts plugin-pack",
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
- "typescript": "^5.7.2"
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
+ });
@@ -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
- expect(pkg.scripts?.pack).toBe("bunx @checkstack/scripts plugin-pack");
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`,