@savvy-web/github-action-builder 2.0.6 → 2.1.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/README.md CHANGED
@@ -106,6 +106,8 @@ export default GitHubAction.create({
106
106
  });
107
107
  ```
108
108
 
109
+ Every build runs in production mode, so `minify` alone decides whether the output is minified and a local build produces the same bytes CI does. License notices from bundled dependencies are folded into the bundle, keeping the output a single committed file with its attribution intact.
110
+
109
111
  ## action.yml requirements
110
112
 
111
113
  Your `action.yml` must use Node.js 24:
@@ -24,7 +24,7 @@ const rootCommand = Command.make("github-action-builder").pipe(Command.withSubco
24
24
  /**
25
25
  * CLI application: reads argv from the Stdio service provided by NodeServices.
26
26
  */
27
- const cli = Command.run(rootCommand, { version: "2.0.6" });
27
+ const cli = Command.run(rootCommand, { version: "2.1.0" });
28
28
  /**
29
29
  * Combined layer: AppLayer + the Node implementations of the CLI
30
30
  * environment (FileSystem, Path, Terminal, Stdio, ChildProcessSpawner).
@@ -20,7 +20,7 @@ const forceOption = Flag.boolean("force").pipe(Flag.withAlias("f"), Flag.withDes
20
20
  * Get current package version (replaced at build time).
21
21
  */
22
22
  const getPackageVersion = () => {
23
- return "2.0.6";
23
+ return "2.1.0";
24
24
  };
25
25
  /**
26
26
  * Generate package.json content.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/github-action-builder",
3
- "version": "2.0.6",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "description": "A zero-config build tool for creating GitHub Actions from TypeScript. Bundles with rsbuild, validates action.yml against GitHub's schema, and outputs production-ready Node.js 24 actions.",
6
6
  "keywords": [
@@ -3,8 +3,8 @@ import { BuildService } from "./build.js";
3
3
  import { ConfigService } from "./config.js";
4
4
  import { buildNativeDynamicImportRules } from "./native-dynamic-imports.js";
5
5
  import { Effect, Layer, Result } from "effect";
6
- import { existsSync, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
7
- import { resolve } from "node:path";
6
+ import { existsSync, mkdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
7
+ import { basename, resolve } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { createRsbuild } from "@rsbuild/core";
10
10
 
@@ -99,6 +99,36 @@ function writeFile(path, content) {
99
99
  });
100
100
  }
101
101
  /**
102
+ * Fold an extracted `*.LICENSE.txt` sidecar back into its bundle.
103
+ *
104
+ * `legalComments: "linked"` is the only mode whose extraction actually sees
105
+ * bundled license banners — the "inline" mode's SWC comment-preservation path
106
+ * never receives them and silently drops attribution (verified against
107
+ * rsbuild 2.1.8). A committed action still must not carry sidecar files
108
+ * (issue #94), so the sidecar's verbatim comment blocks replace the
109
+ * `LICENSE:` reference banner in the bundle and the sidecar is deleted —
110
+ * attribution inline, no extra dist file.
111
+ */
112
+ function inlineLicenseSidecar(outputPath) {
113
+ return Effect.try({
114
+ try: () => {
115
+ const sidecarPath = `${outputPath}.LICENSE.txt`;
116
+ if (!existsSync(sidecarPath)) return;
117
+ const licenses = readFileSync(sidecarPath, "utf8").trim();
118
+ const bundle = readFileSync(outputPath, "utf8");
119
+ const reference = `/*! LICENSE: ${basename(sidecarPath)} */`;
120
+ const afterReference = bundle.startsWith(reference) && bundle.charAt(reference.length) === "\n" ? reference.length + 1 : bundle.startsWith(reference) ? reference.length : 0;
121
+ writeFileSync(outputPath, `${licenses}\n${bundle.slice(afterReference)}`, "utf8");
122
+ unlinkSync(sidecarPath);
123
+ },
124
+ /* v8 ignore next 5 - error branch requires fs permission failures */
125
+ catch: (error) => new WriteError({
126
+ path: outputPath,
127
+ cause: error
128
+ })
129
+ });
130
+ }
131
+ /**
102
132
  * Bundle a single entry with rsbuild.
103
133
  */
104
134
  /* v8 ignore start - bundling requires actual rsbuild execution */
@@ -118,6 +148,7 @@ function bundleEntry(entry, config, cwd) {
118
148
  }
119
149
  const rsbuild = yield* Effect.tryPromise({
120
150
  try: () => createRsbuild({ rsbuildConfig: {
151
+ mode: "production",
121
152
  source: { entry: { [entry.type]: entry.path } },
122
153
  resolve: { alias: ignoreAlias },
123
154
  output: {
@@ -133,7 +164,7 @@ function bundleEntry(entry, config, cwd) {
133
164
  return false;
134
165
  },
135
166
  cleanDistPath: false,
136
- legalComments: "inline",
167
+ legalComments: "linked",
137
168
  minify: config.build.minify,
138
169
  sourceMap: config.build.sourceMap ? { js: "source-map" } : false
139
170
  },
@@ -171,6 +202,7 @@ function bundleEntry(entry, config, cwd) {
171
202
  })
172
203
  });
173
204
  const outputPath = resolve(outputDir, `${entry.type}.js`);
205
+ yield* inlineLicenseSidecar(outputPath);
174
206
  const size = yield* Effect.try({
175
207
  try: () => statSync(outputPath).size,
176
208
  catch: (error) => new BundleFailed({