@brightspot/ui-builder 2.0.2 → 5.0.4-pre.20260626

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,5 +1,6 @@
1
1
  import { type InlineConfig } from 'vite';
2
2
  import type { BuilderConfig } from '../lib/resolve-config.js';
3
+ export type { BuilderConfig } from '../lib/resolve-config.js';
3
4
  export interface DevConfigOptions {
4
5
  builder: BuilderConfig;
5
6
  target: string;
@@ -105,7 +105,6 @@ export async function createBuildConfig({ builder }) {
105
105
  lib: {
106
106
  entry: path.resolve(cwd, entry),
107
107
  formats: [format],
108
- fileName: () => output.jsName,
109
108
  // Vite library mode requires `name` whenever format is 'iife'.
110
109
  // resolveConfig guarantees this is set when format is iife (explicit
111
110
  // or derived from package.json#name), so this guard is defensive
@@ -117,7 +116,19 @@ export async function createBuildConfig({ builder }) {
117
116
  outDir: output.dir,
118
117
  rollupOptions: {
119
118
  output: {
120
- assetFileNames: () => output.cssName,
119
+ // String form (not a function) so Rollup interpolates `[hash]`,
120
+ // `[name]`, and other tokens in `output.jsName` / `output.cssName`.
121
+ // Tokenless values pass through literally. `lib.fileName` is
122
+ // deliberately omitted — when both are set, `entryFileNames`
123
+ // takes precedence and tokens still don't interpolate inside
124
+ // `lib.fileName`'s return value.
125
+ entryFileNames: output.jsName,
126
+ assetFileNames: output.cssName,
127
+ // Hex (not Rollup's default base64) so emitted filenames match
128
+ // `[a-f0-9]+` — URL-safe lowercase, the contract any downstream
129
+ // caller that greps for hex hashes expects. No effect on
130
+ // tokenless filenames.
131
+ hashCharacters: 'hex',
121
132
  ...(format === 'iife' ? { intro: AMD_GUARD_INTRO, outro: AMD_GUARD_OUTRO } : {}),
122
133
  },
123
134
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspot/ui-builder",
3
- "version": "2.0.2",
3
+ "version": "5.0.4-pre.20260626",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "description": "Zero-config build toolkit for Brightspot CMS front-end development.",
@@ -8,6 +8,10 @@
8
8
  "brightspot-ui": "./dist/index.js"
9
9
  },
10
10
  "exports": {
11
+ "./vite": {
12
+ "types": "./dist/vite/vite-config.d.ts",
13
+ "default": "./dist/vite/vite-config.js"
14
+ },
11
15
  "./configs/tsconfig.base.json": "./configs/tsconfig.base.json",
12
16
  "./configs/eslint.config.mjs": "./configs/eslint.config.mjs",
13
17
  "./configs/prettier.config.mjs": "./configs/prettier.config.mjs"
@@ -19,6 +23,8 @@
19
23
  "scripts": {
20
24
  "build": "tsc --noEmitOnError && chmod +x ./dist/index.js",
21
25
  "clean": "shx rm -rf ./dist/*",
26
+ "prepublishOnly": "yarn install --frozen-lockfile",
27
+ "prepack": "yarn build",
22
28
  "test": "vitest run",
23
29
  "test:watch": "vitest"
24
30
  },
@@ -1 +0,0 @@
1
- export declare function resolveTarget(): string;
@@ -1,19 +0,0 @@
1
- import fs from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { log } from './logger.js';
5
- const LOCAL_URL_PATH = path.join(os.homedir(), '.brightspot', 'local-url');
6
- const DEFAULT_TARGET = 'http://localhost';
7
- export function resolveTarget() {
8
- try {
9
- const url = fs.readFileSync(LOCAL_URL_PATH, 'utf8').trim();
10
- if (url) {
11
- log.info(`Proxy target: ${url}`);
12
- return url;
13
- }
14
- }
15
- catch {
16
- log.warn(`${LOCAL_URL_PATH} not found — falling back to ${DEFAULT_TARGET}`);
17
- }
18
- return DEFAULT_TARGET;
19
- }
@@ -1,19 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http';
2
- /**
3
- * Build a `proxyRes` handler that strips occurrences of the upstream
4
- * origin from HTML response bodies. Brightspot embeds absolute URLs
5
- * (e.g. `https://localhost/storage/resource_resource/foo.js`) for
6
- * storage assets, generated from a CDN setting that ignores
7
- * X-Forwarded-* headers. Without rewriting, the browser at the proxy
8
- * origin (`https://localhost:5173`) would issue cross-origin requests
9
- * back to the upstream — module scripts then fail CORS, and mixed
10
- * content blocks plain-HTTP origins. Stripping the origin makes those
11
- * URLs relative, so the browser routes them through the dev proxy.
12
- *
13
- * Non-HTML responses pass through untouched. Empty / no-content
14
- * statuses pass through too.
15
- *
16
- * Requires `selfHandleResponse: true` on the proxy entry so the
17
- * response stream isn't already piped to the client.
18
- */
19
- export declare function rewriteHtmlBody(target: string): (proxyRes: IncomingMessage, _req: IncomingMessage, res: ServerResponse) => void;
@@ -1,125 +0,0 @@
1
- import zlib from 'node:zlib';
2
- /**
3
- * Build a `proxyRes` handler that strips occurrences of the upstream
4
- * origin from HTML response bodies. Brightspot embeds absolute URLs
5
- * (e.g. `https://localhost/storage/resource_resource/foo.js`) for
6
- * storage assets, generated from a CDN setting that ignores
7
- * X-Forwarded-* headers. Without rewriting, the browser at the proxy
8
- * origin (`https://localhost:5173`) would issue cross-origin requests
9
- * back to the upstream — module scripts then fail CORS, and mixed
10
- * content blocks plain-HTTP origins. Stripping the origin makes those
11
- * URLs relative, so the browser routes them through the dev proxy.
12
- *
13
- * Non-HTML responses pass through untouched. Empty / no-content
14
- * statuses pass through too.
15
- *
16
- * Requires `selfHandleResponse: true` on the proxy entry so the
17
- * response stream isn't already piped to the client.
18
- */
19
- export function rewriteHtmlBody(target) {
20
- const originPatterns = buildOriginPatterns(target);
21
- return (proxyRes, _req, res) => {
22
- const status = proxyRes.statusCode ?? 200;
23
- const headers = sanitizeHeaders(proxyRes.headers);
24
- // selfHandleResponse: true disables the proxy library's built-in
25
- // autoRewrite/protocolRewrite, so redirect Location headers come
26
- // through pointing at the upstream origin. Strip the upstream
27
- // origin so the browser follows the redirect via the proxy, not
28
- // directly to the upstream (which would be cross-origin / wrong
29
- // port / wrong scheme).
30
- const loc = headers['location'];
31
- if (typeof loc === 'string') {
32
- let rewritten = loc;
33
- for (const re of originPatterns)
34
- rewritten = rewritten.replace(re, '');
35
- if (rewritten !== loc)
36
- headers['location'] = rewritten;
37
- }
38
- const ct = String(headers['content-type'] ?? '').toLowerCase();
39
- const isHtml = ct.includes('text/html');
40
- const isEmpty = status === 204 || status === 304;
41
- if (!isHtml || isEmpty) {
42
- res.writeHead(status, headers);
43
- proxyRes.pipe(res);
44
- return;
45
- }
46
- const stream = decodeStream(proxyRes, String(proxyRes.headers['content-encoding'] ?? ''));
47
- const chunks = [];
48
- stream.on('data', c => chunks.push(c));
49
- stream.on('error', err => {
50
- res.writeHead(502, { 'content-type': 'text/plain' });
51
- res.end(`proxy decode error: ${err.message}`);
52
- });
53
- stream.on('end', () => {
54
- let body = Buffer.concat(chunks).toString('utf8');
55
- for (const re of originPatterns)
56
- body = body.replace(re, '');
57
- const out = Buffer.from(body, 'utf8');
58
- headers['content-length'] = String(out.length);
59
- res.writeHead(status, headers);
60
- res.end(out);
61
- });
62
- };
63
- }
64
- // Vite serves over HTTP/2 (mkcert), but the upstream speaks HTTP/1.1.
65
- // HTTP/2 forbids connection-specific headers and transfer-encoding;
66
- // passing them through triggers ERR_HTTP2_INVALID_CONNECTION_HEADERS
67
- // in node's writeHead. Also drop content-encoding because we always
68
- // decompress and re-emit identity. content-length is recomputed by
69
- // the caller after rewriting.
70
- function sanitizeHeaders(input) {
71
- const dropped = new Set([
72
- 'connection',
73
- 'transfer-encoding',
74
- 'keep-alive',
75
- 'proxy-authenticate',
76
- 'proxy-authorization',
77
- 'te',
78
- 'trailer',
79
- 'upgrade',
80
- 'http2-settings',
81
- 'content-encoding',
82
- 'content-length',
83
- ]);
84
- const out = {};
85
- for (const [k, v] of Object.entries(input)) {
86
- if (v === undefined)
87
- continue;
88
- if (dropped.has(k.toLowerCase()))
89
- continue;
90
- out[k] = v;
91
- }
92
- return out;
93
- }
94
- function decodeStream(stream, encoding) {
95
- switch (encoding.toLowerCase()) {
96
- case 'gzip':
97
- return stream.pipe(zlib.createGunzip());
98
- case 'br':
99
- return stream.pipe(zlib.createBrotliDecompress());
100
- case 'deflate':
101
- return stream.pipe(zlib.createInflate());
102
- default:
103
- return stream;
104
- }
105
- }
106
- // Build regexes that match the upstream origin in a few common
107
- // forms — both `http://` and `https://` so an http target still
108
- // strips https variants the CMS may emit when X-Forwarded-Proto is
109
- // honored, and an explicit-port form so e.g. http://localhost:8080
110
- // strips both with and without :8080.
111
- function buildOriginPatterns(target) {
112
- const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
113
- const u = new URL(target);
114
- const host = u.hostname;
115
- const explicitPort = u.port;
116
- // Form: scheme://host(:port)? — only strip when followed by a path
117
- // boundary so we don't eat unrelated strings that happen to share
118
- // the prefix.
119
- const patterns = [
120
- new RegExp(`https?://${escape(host)}(?=[/'"\\)\\s])`, 'g'),
121
- new RegExp(`https?://${escape(host)}:\\d+(?=[/'"\\)\\s])`, 'g'),
122
- ];
123
- void explicitPort;
124
- return patterns;
125
- }