@gooddata/create-pluggable-module 11.41.0-alpha.3 → 11.41.0-alpha.4

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-app-template-name-harness",
3
- "version": "11.41.0-alpha.3",
3
+ "version": "11.41.0-alpha.4",
4
4
  "private": true,
5
5
  "description": "Standalone harness for gdc-app-template-name module",
6
6
  "license": "UNLICENSED",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdc-app-template-name-module",
3
- "version": "11.41.0-alpha.3",
3
+ "version": "11.41.0-alpha.4",
4
4
  "private": true,
5
5
  "description": "{applicationTemplateTitle} pluggable application module",
6
6
  "license": "UNLICENSED",
@@ -48,6 +48,17 @@ describe("copyTemplate", () => {
48
48
  const copied = readFileSync(join(dest, "image.png"));
49
49
  expect(copied.equals(binaryContent)).toBe(true);
50
50
  });
51
+ it("copies TLS cert fixtures (.crt/.key) verbatim — never token-substituted", () => {
52
+ // Self-signed e2e certs are cryptographic artifacts. Even though PEM is ASCII, a
53
+ // token match inside the body must not rewrite the cert, so they go through the
54
+ // verbatim (BINARY_EXTENSIONS) path.
55
+ const pem = "-----BEGIN CERTIFICATE-----\nTOKENISH-base64-payload\n-----END CERTIFICATE-----\n";
56
+ writeFileSync(join(src, "server.crt"), pem);
57
+ writeFileSync(join(src, "server.key"), pem);
58
+ copyTemplate(src, dest, [["TOKENISH", "REPLACED"]]);
59
+ expect(readFileSync(join(dest, "server.crt"), "utf-8")).toBe(pem);
60
+ expect(readFileSync(join(dest, "server.key"), "utf-8")).toBe(pem);
61
+ });
51
62
  it("token substitution ORDER matters — longer-first prevents partial overlap", () => {
52
63
  writeFileSync(join(src, "a.txt"), "foo-bar foo");
53
64
  const { written: _w } = copyTemplate(src, dest, [
@@ -5,6 +5,7 @@ import { tmpdir } from "os";
5
5
  import { dirname, join, relative } from "path";
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7
7
  import { clientProfile } from "../../profiles/client.js";
8
+ import { BINARY_EXTENSIONS } from "../copyTemplate.js";
8
9
  import { runProfileWithAnswers } from "../runProfile.js";
9
10
  import { scrubPackageJsonVersions } from "./scrubPackageJsonVersions.js";
10
11
  // Resolve workspace template paths via pnpm's symlinks. The template packages
@@ -30,10 +31,11 @@ function treeToMap(root) {
30
31
  continue;
31
32
  }
32
33
  const rel = relative(root, abs);
33
- // Binary files (images, fonts) — record only their size so changes
34
- // surface in the snapshot without including raw bytes.
34
+ // Binary files (images, fonts, cert fixtures) — record only their size so
35
+ // changes surface in the snapshot without including raw bytes. Reuses the
36
+ // engine's BINARY_EXTENSIONS so this list can never drift from copyTemplate.
35
37
  const ext = entry.name.slice(entry.name.lastIndexOf(".")).toLowerCase();
36
- if ([".png", ".jpg", ".jpeg", ".gif", ".ico", ".woff", ".woff2", ".ttf", ".eot"].includes(ext)) {
38
+ if (BINARY_EXTENSIONS.has(ext)) {
37
39
  out[rel] = `<binary, ${statSync(abs).size} bytes>`;
38
40
  continue;
39
41
  }
@@ -1,4 +1,9 @@
1
1
  import type { TokenReplacements } from "../types.js";
2
+ /**
3
+ * Files for which token-substitution-in-text would corrupt the byte stream.
4
+ * Binary files must be copied verbatim.
5
+ */
6
+ export declare const BINARY_EXTENSIONS: Set<string>;
2
7
  export interface ICopyResult {
3
8
  /** Absolute paths of every file written. Used for the package.json transform pass. */
4
9
  written: string[];
@@ -1,5 +1,5 @@
1
1
  // (C) 2026 GoodData Corporation
2
- import { cpSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
2
+ import { chmodSync, cpSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3
3
  import { join } from "path";
4
4
  /**
5
5
  * Directories that should never be copied. Tooling output (`node_modules`,
@@ -13,7 +13,7 @@ const SKIP_SUFFIXES = [".tsbuildinfo"];
13
13
  * Files for which token-substitution-in-text would corrupt the byte stream.
14
14
  * Binary files must be copied verbatim.
15
15
  */
16
- const BINARY_EXTENSIONS = new Set([
16
+ export const BINARY_EXTENSIONS = new Set([
17
17
  ".ico",
18
18
  ".png",
19
19
  ".jpg",
@@ -23,6 +23,10 @@ const BINARY_EXTENSIONS = new Set([
23
23
  ".woff2",
24
24
  ".ttf",
25
25
  ".eot",
26
+ // TLS fixtures (self-signed e2e certs): cryptographic artifacts that must
27
+ // never be touched by token substitution, even though PEM is ASCII text.
28
+ ".crt",
29
+ ".key",
26
30
  ]);
27
31
  /**
28
32
  * Recursively copies srcDir → destDir applying ordered string replacement to
@@ -57,6 +61,7 @@ function copyRecursive(srcDir, destDir, replacements, written) {
57
61
  function copyFile(srcPath, destPath, name, replacements, written) {
58
62
  const ext = name.slice(name.lastIndexOf(".")).toLowerCase();
59
63
  if (BINARY_EXTENSIONS.has(ext)) {
64
+ // cpSync preserves the source mode (including the executable bit) by default.
60
65
  cpSync(srcPath, destPath);
61
66
  written.push(destPath);
62
67
  return;
@@ -66,5 +71,14 @@ function copyFile(srcPath, destPath, name, replacements, written) {
66
71
  content = content.replaceAll(token, value);
67
72
  }
68
73
  writeFileSync(destPath, content, "utf-8");
74
+ // writeFileSync creates with default perms (0o666 & ~umask), dropping the source's
75
+ // executable bit. Carry over ONLY the source's exec bits (never clearing write bits,
76
+ // so a read-only source can't make the copy read-only and break a later rewrite) so
77
+ // shell scripts stay runnable — both the package.json `./scripts/...sh` entries and the
78
+ // CI ref-workspace action invoke them directly (`./create-ref-workspace.sh`), needing +x.
79
+ const execBits = statSync(srcPath).mode & 0o111;
80
+ if (execBits) {
81
+ chmodSync(destPath, statSync(destPath).mode | execBits);
82
+ }
69
83
  written.push(destPath);
70
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/create-pluggable-module",
3
- "version": "11.41.0-alpha.3",
3
+ "version": "11.41.0-alpha.4",
4
4
  "description": "Scaffolder for GoodData pluggable applications. Invoked via `npm init @gooddata/pluggable-module`.",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -50,10 +50,10 @@
50
50
  "typescript": "5.9.3",
51
51
  "vite": "8.0.16",
52
52
  "vitest": "4.1.8",
53
- "@gooddata/eslint-config": "11.41.0-alpha.3",
54
- "gdc-app-template-name-harness": "11.41.0-alpha.3",
55
- "@gooddata/oxlint-config": "11.41.0-alpha.3",
56
- "gdc-app-template-name-module": "11.41.0-alpha.3"
53
+ "@gooddata/eslint-config": "11.41.0-alpha.4",
54
+ "@gooddata/oxlint-config": "11.41.0-alpha.4",
55
+ "gdc-app-template-name-module": "11.41.0-alpha.4",
56
+ "gdc-app-template-name-harness": "11.41.0-alpha.4"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=24.12.0"