@brightspot/ui-builder 1.1.0 → 2.0.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/configs/prettier.config.mjs +13 -1
- package/dist/commands/_load-config.d.ts +2 -0
- package/dist/commands/_load-config.js +23 -0
- package/dist/commands/build.js +5 -5
- package/dist/commands/dev.js +22 -5
- package/dist/commands/init.js +0 -2
- package/dist/index.js +1 -1
- package/dist/lib/resolve-config.d.ts +17 -3
- package/dist/lib/resolve-config.js +106 -16
- package/dist/vite/auto-plugins.d.ts +2 -0
- package/dist/vite/auto-plugins.js +41 -0
- package/dist/vite/plugin-html-inject.d.ts +12 -3
- package/dist/vite/plugin-html-inject.js +8 -5
- package/dist/vite/vite-config.d.ts +9 -7
- package/dist/vite/vite-config.js +75 -16
- package/package.json +34 -11
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url)
|
|
4
|
+
const plugins = ['prettier-plugin-organize-imports']
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
require.resolve('prettier-plugin-svelte')
|
|
8
|
+
plugins.push('prettier-plugin-svelte')
|
|
9
|
+
} catch {
|
|
10
|
+
// prettier-plugin-svelte is an optional peer dep — skip if not installed
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
/** @type {import('prettier').Config} */
|
|
2
14
|
export default {
|
|
3
15
|
arrowParens: 'avoid',
|
|
@@ -15,5 +27,5 @@ export default {
|
|
|
15
27
|
tabWidth: 2,
|
|
16
28
|
useTabs: false,
|
|
17
29
|
organizeImportsSkipDestructiveCodeActions: true,
|
|
18
|
-
plugins
|
|
30
|
+
plugins,
|
|
19
31
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { log } from '../lib/logger.js';
|
|
2
|
+
import { ConfigError, resolveConfig } from '../lib/resolve-config.js';
|
|
3
|
+
// CLI-side wrapper around resolveConfig: prints warnings + a one-line
|
|
4
|
+
// summary, and on ConfigError logs the message and exits 1. Lives next
|
|
5
|
+
// to commands so the lib module stays free of process termination.
|
|
6
|
+
export function loadOrExit() {
|
|
7
|
+
try {
|
|
8
|
+
const { config, warnings } = resolveConfig();
|
|
9
|
+
for (const w of warnings)
|
|
10
|
+
log.warn(w);
|
|
11
|
+
log.info(`basePath: ${config.basePath}`);
|
|
12
|
+
log.info(`entry: ${config.entry}`);
|
|
13
|
+
log.info(`format: ${config.format}${config.format === 'iife' && config.name ? ` (name: ${config.name})` : ''}`);
|
|
14
|
+
return config;
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
if (e instanceof ConfigError) {
|
|
18
|
+
log.error(e.message);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
throw e;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/dist/commands/build.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { build as viteBuild } from 'vite';
|
|
2
2
|
import { log } from '../lib/logger.js';
|
|
3
|
-
import { resolveConfig } from '../lib/resolve-config.js';
|
|
4
3
|
import { createBuildConfig } from '../vite/vite-config.js';
|
|
4
|
+
import { loadOrExit } from './_load-config.js';
|
|
5
5
|
export async function build() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const config = createBuildConfig(
|
|
6
|
+
const builder = loadOrExit();
|
|
7
|
+
log.info(`Building ${builder.format.toUpperCase()} bundle...`);
|
|
8
|
+
const config = await createBuildConfig({ builder });
|
|
9
9
|
await viteBuild(config);
|
|
10
|
-
log.success(
|
|
10
|
+
log.success(`Build complete — output in ${builder.output.dir}/`);
|
|
11
11
|
}
|
package/dist/commands/dev.js
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { createServer } from 'vite';
|
|
2
5
|
import { log } from '../lib/logger.js';
|
|
3
|
-
import { resolveConfig } from '../lib/resolve-config.js';
|
|
4
|
-
import { resolveTarget } from '../lib/resolve-target.js';
|
|
5
6
|
import { createDevConfig } from '../vite/vite-config.js';
|
|
7
|
+
import { loadOrExit } from './_load-config.js';
|
|
8
|
+
const LOCAL_URL_PATH = path.join(os.homedir(), '.brightspot', 'local-url');
|
|
9
|
+
const DEFAULT_TARGET = 'http://localhost';
|
|
6
10
|
export async function dev(opts) {
|
|
7
|
-
const
|
|
8
|
-
const target = opts.url ??
|
|
9
|
-
const config = createDevConfig({ target, port: opts.port
|
|
11
|
+
const builder = loadOrExit();
|
|
12
|
+
const target = opts.url ?? readLocalTarget();
|
|
13
|
+
const config = await createDevConfig({ builder, target, port: opts.port });
|
|
10
14
|
const server = await createServer(config);
|
|
11
15
|
await server.listen();
|
|
12
16
|
server.printUrls();
|
|
13
17
|
log.success('Dev server running — proxying to ' + target);
|
|
14
18
|
}
|
|
19
|
+
function readLocalTarget() {
|
|
20
|
+
try {
|
|
21
|
+
const url = fs.readFileSync(LOCAL_URL_PATH, 'utf8').trim();
|
|
22
|
+
if (url) {
|
|
23
|
+
log.info(`Proxy target: ${url}`);
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
log.warn(`${LOCAL_URL_PATH} not found — falling back to ${DEFAULT_TARGET}`);
|
|
29
|
+
}
|
|
30
|
+
return DEFAULT_TARGET;
|
|
31
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { log } from '../lib/logger.js';
|
|
4
|
-
import { resolveTarget } from '../lib/resolve-target.js';
|
|
5
4
|
const cwd = process.cwd();
|
|
6
5
|
export async function init() {
|
|
7
6
|
log.info('Initializing Brightspot UI project...');
|
|
@@ -9,7 +8,6 @@ export async function init() {
|
|
|
9
8
|
writeEslintConfig();
|
|
10
9
|
patchPackageJson();
|
|
11
10
|
scaffoldEntryPoint();
|
|
12
|
-
resolveTarget();
|
|
13
11
|
log.success('Project initialized. Run `brightspot-ui dev` to start developing.');
|
|
14
12
|
}
|
|
15
13
|
function writeTsConfig() {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
interface BuilderConfig {
|
|
1
|
+
export interface BuilderConfig {
|
|
2
2
|
basePath: string;
|
|
3
|
+
entry: string;
|
|
4
|
+
format: 'es' | 'iife';
|
|
5
|
+
name?: string;
|
|
6
|
+
output: {
|
|
7
|
+
dir: string;
|
|
8
|
+
jsName: string;
|
|
9
|
+
cssName: string;
|
|
10
|
+
};
|
|
3
11
|
}
|
|
4
|
-
export
|
|
5
|
-
|
|
12
|
+
export interface ResolveResult {
|
|
13
|
+
config: BuilderConfig;
|
|
14
|
+
warnings: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare class ConfigError extends Error {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
export declare function resolveConfig(): ResolveResult;
|
|
@@ -1,25 +1,115 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
export class ConfigError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'ConfigError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const DEFAULTS = {
|
|
10
|
+
entry: 'src/index.ts',
|
|
11
|
+
format: 'es',
|
|
12
|
+
output: {
|
|
13
|
+
dir: 'dist',
|
|
14
|
+
jsName: 'bundle.js',
|
|
15
|
+
cssName: 'style.css',
|
|
16
|
+
},
|
|
17
|
+
};
|
|
5
18
|
export function resolveConfig() {
|
|
6
19
|
const pkgPath = path.resolve(process.cwd(), 'package.json');
|
|
20
|
+
let pkg;
|
|
7
21
|
try {
|
|
8
|
-
|
|
9
|
-
const config = pkg['brightspot-ui'];
|
|
10
|
-
if (config?.basePath) {
|
|
11
|
-
const basePath = config.basePath;
|
|
12
|
-
if (!basePath.startsWith('/') || !basePath.endsWith('/')) {
|
|
13
|
-
log.warn(`basePath "${basePath}" must start and end with "/" — using default`);
|
|
14
|
-
return { basePath: DEFAULT_BASE_PATH };
|
|
15
|
-
}
|
|
16
|
-
log.info(`basePath: ${basePath}`);
|
|
17
|
-
return { basePath };
|
|
18
|
-
}
|
|
22
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
19
23
|
}
|
|
20
24
|
catch {
|
|
21
|
-
|
|
25
|
+
throw new ConfigError(`Could not read ${pkgPath}.`);
|
|
26
|
+
}
|
|
27
|
+
const raw = pkg['brightspot-ui'] ?? {};
|
|
28
|
+
const warnings = [];
|
|
29
|
+
const pkgName = pkg.name;
|
|
30
|
+
const basePath = resolveBasePath(raw.basePath, pkgName, warnings);
|
|
31
|
+
// Normalize leading `./` so downstream consumers (the proxy regex,
|
|
32
|
+
// the dev-entry bootstrap import URL) get a canonical form.
|
|
33
|
+
const entry = (raw.entry ?? DEFAULTS.entry).replace(/^\.\//, '');
|
|
34
|
+
const format = resolveFormat(raw.format);
|
|
35
|
+
const name = resolveIifeName(raw.name, format, pkgName, warnings);
|
|
36
|
+
const output = {
|
|
37
|
+
dir: raw.output?.dir ?? DEFAULTS.output.dir,
|
|
38
|
+
jsName: raw.output?.jsName ?? DEFAULTS.output.jsName,
|
|
39
|
+
cssName: raw.output?.cssName ?? DEFAULTS.output.cssName,
|
|
40
|
+
};
|
|
41
|
+
return { config: { basePath, entry, format, name, output }, warnings };
|
|
42
|
+
}
|
|
43
|
+
function resolveBasePath(explicit, pkgName, warnings) {
|
|
44
|
+
if (explicit) {
|
|
45
|
+
if (!explicit.startsWith('/') || !explicit.endsWith('/')) {
|
|
46
|
+
throw new ConfigError(`basePath "${explicit}" must start and end with "/".`);
|
|
47
|
+
}
|
|
48
|
+
return explicit;
|
|
49
|
+
}
|
|
50
|
+
if (!pkgName) {
|
|
51
|
+
// BREAKING (2.0): pre-2.0 builds silently fell back to /_resource/dist/
|
|
52
|
+
// when both knobs were absent. We now exit so misconfigured monorepo
|
|
53
|
+
// packages don't ship to the wrong URL. Migration paths below.
|
|
54
|
+
throw new ConfigError('No basePath set in package.json#brightspot-ui.basePath, and no package.json#name to derive one from. ' +
|
|
55
|
+
'To migrate from @brightspot/ui-builder 1.x: ' +
|
|
56
|
+
'either set "name": "<slug>" in package.json (basePath will be inferred as /_resource/<slug>/cms/dist/), ' +
|
|
57
|
+
'or set "brightspot-ui": { "basePath": "/_resource/<slug>/cms/dist/" } explicitly.');
|
|
58
|
+
}
|
|
59
|
+
const inferred = `/_resource/${stripScope(pkgName)}/cms/dist/`;
|
|
60
|
+
warnings.push(`basePath was not set — inferred "${inferred}" from package.json#name "${pkgName}". ` +
|
|
61
|
+
`Set "brightspot-ui": { "basePath": "..." } in package.json to silence this warning.`);
|
|
62
|
+
return inferred;
|
|
63
|
+
}
|
|
64
|
+
function resolveFormat(value) {
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
return DEFAULTS.format;
|
|
67
|
+
if (value !== 'es' && value !== 'iife') {
|
|
68
|
+
throw new ConfigError(`format "${value}" must be "es" or "iife".`);
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
// Vite library mode requires `name` whenever formats include 'iife' or
|
|
73
|
+
// 'umd' — it's a config-time check, not a function-of-whether-the-entry-
|
|
74
|
+
// has-exports check. So when the consumer chooses iife and hasn't set
|
|
75
|
+
// name, we derive a PascalCase identifier from package.json#name
|
|
76
|
+
// (@brightspot/platform-tours -> PlatformTours) and warn. Returns
|
|
77
|
+
// undefined when format is es (name is irrelevant). Throws when iife
|
|
78
|
+
// is chosen but no valid identifier can be derived.
|
|
79
|
+
function resolveIifeName(explicit, format, pkgName, warnings) {
|
|
80
|
+
if (explicit !== undefined) {
|
|
81
|
+
if (!isValidJsIdentifier(explicit)) {
|
|
82
|
+
throw new ConfigError(`name "${explicit}" is not a valid JavaScript identifier — Vite library mode emits ` +
|
|
83
|
+
`\`var ${explicit} = ...\` for iife output, which would produce a syntax error. ` +
|
|
84
|
+
`Set "brightspot-ui": { "name": "..." } to a PascalCase identifier (e.g. "PlatformTours").`);
|
|
85
|
+
}
|
|
86
|
+
return explicit;
|
|
22
87
|
}
|
|
23
|
-
|
|
24
|
-
|
|
88
|
+
if (format !== 'iife')
|
|
89
|
+
return undefined;
|
|
90
|
+
const derived = deriveIifeName(pkgName);
|
|
91
|
+
if (derived === null) {
|
|
92
|
+
throw new ConfigError(`format "iife" requires a "name" — could not derive a valid JavaScript identifier from package.json#name "${pkgName ?? '<unset>'}". ` +
|
|
93
|
+
`Set "brightspot-ui": { "name": "..." } in package.json (e.g. "PlatformTours").`);
|
|
94
|
+
}
|
|
95
|
+
warnings.push(`name was not set — derived "${derived}" from package.json#name "${pkgName}". ` +
|
|
96
|
+
`Set "brightspot-ui": { "name": "..." } in package.json to silence this warning.`);
|
|
97
|
+
return derived;
|
|
98
|
+
}
|
|
99
|
+
function deriveIifeName(pkgName) {
|
|
100
|
+
if (!pkgName)
|
|
101
|
+
return null;
|
|
102
|
+
const parts = stripScope(pkgName).split(/[-_.]/).filter(Boolean);
|
|
103
|
+
if (parts.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
const pascal = parts.map(p => p[0].toUpperCase() + p.slice(1)).join('');
|
|
106
|
+
return isValidJsIdentifier(pascal) ? pascal : null;
|
|
107
|
+
}
|
|
108
|
+
// ASCII-only by design: matches what minifiers and bundlers handle
|
|
109
|
+
// without surprises. ECMAScript permits a wider Unicode range.
|
|
110
|
+
function isValidJsIdentifier(s) {
|
|
111
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
|
|
112
|
+
}
|
|
113
|
+
function stripScope(pkgName) {
|
|
114
|
+
return pkgName.replace(/^@[^/]+\//, '');
|
|
25
115
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { resolve as resolveEsm } from 'import-meta-resolve';
|
|
4
|
+
import { log } from '../lib/logger.js';
|
|
5
|
+
export async function loadAutoPlugins(cwd) {
|
|
6
|
+
const candidates = await Promise.all([loadSveltePlugin(cwd)]);
|
|
7
|
+
return candidates.filter(p => p !== null);
|
|
8
|
+
}
|
|
9
|
+
// Resolve peer-optional plugins from the consumer's cwd, not the builder's
|
|
10
|
+
// own location. A bare `await import(specifier)` resolves relative to this
|
|
11
|
+
// file, which only works under npm flat hoisting. It breaks for yarn 1
|
|
12
|
+
// link, yarn 2+ workspaces, and pnpm strict — exactly the layouts
|
|
13
|
+
// consumers use during development. We use ESM resolution rooted at the
|
|
14
|
+
// consumer's package.json so the `import` condition is honored, unlike CJS
|
|
15
|
+
// require.resolve which trips on ESM-only packages with an `exports`
|
|
16
|
+
// block. Node's native import.meta.resolve has a 2-arg form that does
|
|
17
|
+
// exactly this, but it remains flag-gated behind
|
|
18
|
+
// --experimental-import-meta-resolve in Node 22; the import-meta-resolve
|
|
19
|
+
// polyfill mirrors the same semantics without the flag. Only
|
|
20
|
+
// module-not-found resolution failures become silent skips; other
|
|
21
|
+
// resolution failures (broken package.json#exports, malformed
|
|
22
|
+
// node_modules) and factory errors propagate so the consumer sees a real
|
|
23
|
+
// diagnostic instead of a confusing "plugin not installed".
|
|
24
|
+
async function loadOptionalPlugin(specifier, cwd, detectedMessage, invoke) {
|
|
25
|
+
const parent = pathToFileURL(path.join(cwd, 'package.json')).href;
|
|
26
|
+
let resolved;
|
|
27
|
+
try {
|
|
28
|
+
resolved = resolveEsm(specifier, parent);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e.code === 'ERR_MODULE_NOT_FOUND')
|
|
32
|
+
return null;
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
log.info(detectedMessage);
|
|
36
|
+
const mod = (await import(resolved));
|
|
37
|
+
return invoke(mod);
|
|
38
|
+
}
|
|
39
|
+
function loadSveltePlugin(cwd) {
|
|
40
|
+
return loadOptionalPlugin('@sveltejs/vite-plugin-svelte', cwd, 'Detected @sveltejs/vite-plugin-svelte — registering Svelte plugin.', mod => mod.svelte());
|
|
41
|
+
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
+
export interface DevEntryOptions {
|
|
3
|
+
basePath: string;
|
|
4
|
+
entry: string;
|
|
5
|
+
jsName: string;
|
|
6
|
+
cssName: string;
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
|
-
* Serves a dev bootstrap script at the bundle
|
|
9
|
+
* Serves a dev bootstrap script at the bundle path so that Vite's
|
|
4
10
|
* HMR client and the real entry point are loaded as ES modules.
|
|
5
|
-
* Also serves an empty
|
|
11
|
+
* Also serves an empty stylesheet at the css path since Vite injects
|
|
12
|
+
* CSS via JS in dev. Both filenames mirror the build output knobs
|
|
13
|
+
* (output.jsName / output.cssName) so a consumer who renames them
|
|
14
|
+
* keeps dev/build parity.
|
|
6
15
|
*/
|
|
7
|
-
export declare function devEntryPlugin(basePath
|
|
16
|
+
export declare function devEntryPlugin({ basePath, entry, jsName, cssName }: DevEntryOptions): Plugin;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Serves a dev bootstrap script at the bundle
|
|
2
|
+
* Serves a dev bootstrap script at the bundle path so that Vite's
|
|
3
3
|
* HMR client and the real entry point are loaded as ES modules.
|
|
4
|
-
* Also serves an empty
|
|
4
|
+
* Also serves an empty stylesheet at the css path since Vite injects
|
|
5
|
+
* CSS via JS in dev. Both filenames mirror the build output knobs
|
|
6
|
+
* (output.jsName / output.cssName) so a consumer who renames them
|
|
7
|
+
* keeps dev/build parity.
|
|
5
8
|
*/
|
|
6
|
-
export function devEntryPlugin(basePath, entry) {
|
|
7
|
-
const bundlePath = `${basePath}
|
|
8
|
-
const cssPath = `${basePath}
|
|
9
|
+
export function devEntryPlugin({ basePath, entry, jsName, cssName }) {
|
|
10
|
+
const bundlePath = `${basePath}${jsName}`;
|
|
11
|
+
const cssPath = `${basePath}${cssName}`;
|
|
9
12
|
return {
|
|
10
13
|
name: 'brightspot-dev-entry',
|
|
11
14
|
configureServer(server) {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { type InlineConfig } from 'vite';
|
|
2
|
+
import type { BuilderConfig } from '../lib/resolve-config.js';
|
|
3
|
+
export interface DevConfigOptions {
|
|
4
|
+
builder: BuilderConfig;
|
|
3
5
|
target: string;
|
|
4
6
|
port?: number;
|
|
5
|
-
basePath: string;
|
|
6
|
-
entry?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function createDevConfig({ target, port
|
|
9
|
-
export
|
|
10
|
-
|
|
8
|
+
export declare function createDevConfig({ builder, target, port }: DevConfigOptions): Promise<InlineConfig>;
|
|
9
|
+
export interface BuildConfigOptions {
|
|
10
|
+
builder: BuilderConfig;
|
|
11
|
+
}
|
|
12
|
+
export declare function createBuildConfig({ builder }: BuildConfigOptions): Promise<InlineConfig>;
|
package/dist/vite/vite-config.js
CHANGED
|
@@ -1,20 +1,34 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { loadConfigFromFile, mergeConfig } from 'vite';
|
|
3
|
+
import { log } from '../lib/logger.js';
|
|
4
|
+
import { loadAutoPlugins } from './auto-plugins.js';
|
|
2
5
|
import { devEntryPlugin } from './plugin-html-inject.js';
|
|
3
|
-
export function createDevConfig({ target, port
|
|
6
|
+
export async function createDevConfig({ builder, target, port }) {
|
|
4
7
|
const cwd = process.cwd();
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
+
const { basePath, entry, output: { jsName, cssName }, } = builder;
|
|
9
|
+
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
10
|
+
const escaped = escapeRegex(basePath.replace(/^\//, ''));
|
|
11
|
+
// entry.split('/')[0] yields the file itself (e.g. 'index.ts') for a
|
|
12
|
+
// root-level entry, which never matches a URL — skip the segment in
|
|
13
|
+
// that case so /index.ts isn't proxied to the upstream application.
|
|
14
|
+
const entryParts = entry.split('/');
|
|
15
|
+
const entryDirAlt = entryParts.length > 1 ? `|${escapeRegex(entryParts[0])}/` : '';
|
|
16
|
+
const [mkcert, autoPlugins] = await Promise.all([loadMkcertPlugin(), loadAutoPlugins(cwd)]);
|
|
17
|
+
const base = {
|
|
8
18
|
root: cwd,
|
|
9
19
|
base: '/',
|
|
10
|
-
plugins: [
|
|
20
|
+
plugins: [mkcert, devEntryPlugin({ basePath, entry, jsName, cssName }), ...autoPlugins],
|
|
11
21
|
resolve: {
|
|
12
22
|
preserveSymlinks: true,
|
|
23
|
+
// Vite 8 native: replaces the deprecated vite-tsconfig-paths plugin.
|
|
24
|
+
// Follows tsconfig `extends` chains and applies to all importers
|
|
25
|
+
// (including .svelte / .vue) without the plugin's loose-mode dance.
|
|
26
|
+
tsconfigPaths: true,
|
|
13
27
|
},
|
|
14
28
|
server: {
|
|
15
29
|
port,
|
|
16
30
|
proxy: {
|
|
17
|
-
[`^/(?!(${escaped}
|
|
31
|
+
[`^/(?!(${escaped}${entryDirAlt}|@vite|@fs|node_modules))`]: {
|
|
18
32
|
target,
|
|
19
33
|
autoRewrite: true,
|
|
20
34
|
changeOrigin: true,
|
|
@@ -28,32 +42,77 @@ export function createDevConfig({ target, port, basePath, entry = 'src/index.ts'
|
|
|
28
42
|
},
|
|
29
43
|
},
|
|
30
44
|
};
|
|
45
|
+
return applyUserConfig(base, cwd, 'serve');
|
|
31
46
|
}
|
|
32
|
-
export function createBuildConfig(
|
|
47
|
+
export async function createBuildConfig({ builder }) {
|
|
33
48
|
const cwd = process.cwd();
|
|
34
|
-
|
|
49
|
+
const { basePath, entry, format, name, output } = builder;
|
|
50
|
+
const autoPlugins = await loadAutoPlugins(cwd);
|
|
51
|
+
const base = {
|
|
35
52
|
root: cwd,
|
|
36
|
-
|
|
53
|
+
plugins: [...autoPlugins],
|
|
37
54
|
resolve: {
|
|
38
55
|
preserveSymlinks: true,
|
|
56
|
+
// Vite 8 native: replaces the deprecated vite-tsconfig-paths plugin.
|
|
57
|
+
// Follows tsconfig `extends` chains and applies to all importers
|
|
58
|
+
// (including .svelte / .vue) without the plugin's loose-mode dance.
|
|
59
|
+
tsconfigPaths: true,
|
|
60
|
+
},
|
|
61
|
+
build: {
|
|
62
|
+
cssCodeSplit: false,
|
|
63
|
+
// Vite 7's `build.lib` implied a single bundle. Vite 8 dropped that
|
|
64
|
+
// default, so any consumer with `await import(...)` in its graph
|
|
65
|
+
// emits sidecar `*-<hash>.mjs` chunks that the toolkit's promised
|
|
66
|
+
// two-file output (bundle.js + style.css) doesn't account for —
|
|
67
|
+
// and that the gradle processResources task doesn't pattern-match
|
|
68
|
+
// against, so chunks 404 in production. Default-off here restores
|
|
69
|
+
// the contract; consumers who want code-splitting can opt back in
|
|
70
|
+
// via Layer 3 (vite.config.ts → build.rollupOptions.output.
|
|
71
|
+
// codeSplitting = true). Note: Rolldown deprecated the equivalent
|
|
72
|
+
// `inlineDynamicImports: true` in favor of `codeSplitting: false`.
|
|
73
|
+
rollupOptions: {
|
|
74
|
+
output: {
|
|
75
|
+
codeSplitting: false,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
39
78
|
},
|
|
79
|
+
};
|
|
80
|
+
const knobs = {
|
|
81
|
+
base: basePath,
|
|
40
82
|
build: {
|
|
41
83
|
lib: {
|
|
42
|
-
entry: path.resolve(cwd,
|
|
43
|
-
formats: [
|
|
44
|
-
|
|
45
|
-
|
|
84
|
+
entry: path.resolve(cwd, entry),
|
|
85
|
+
formats: [format],
|
|
86
|
+
fileName: () => output.jsName,
|
|
87
|
+
// Vite library mode requires `name` whenever format is 'iife'.
|
|
88
|
+
// resolveConfig guarantees this is set when format is iife (explicit
|
|
89
|
+
// or derived from package.json#name), so this guard is defensive
|
|
90
|
+
// against a manually-constructed BuilderConfig that violates the
|
|
91
|
+
// invariant — we propagate undefined to Vite rather than inject a
|
|
92
|
+
// silent default; Vite then surfaces its own clear error.
|
|
93
|
+
...(format === 'iife' && name ? { name } : {}),
|
|
46
94
|
},
|
|
95
|
+
outDir: output.dir,
|
|
47
96
|
rollupOptions: {
|
|
48
97
|
output: {
|
|
49
|
-
assetFileNames: () =>
|
|
98
|
+
assetFileNames: () => output.cssName,
|
|
50
99
|
},
|
|
51
100
|
},
|
|
52
|
-
cssCodeSplit: false,
|
|
53
101
|
},
|
|
54
102
|
};
|
|
103
|
+
return applyUserConfig(mergeConfig(base, knobs), cwd, 'build');
|
|
104
|
+
}
|
|
105
|
+
async function applyUserConfig(merged, cwd, command) {
|
|
106
|
+
const loaded = await loadConfigFromFile({ command, mode: command === 'serve' ? 'development' : 'production' }, undefined, cwd);
|
|
107
|
+
if (loaded)
|
|
108
|
+
log.info(`Merging consumer Vite config from ${path.relative(cwd, loaded.path)}.`);
|
|
109
|
+
const base = loaded ? mergeConfig(merged, loaded.config) : merged;
|
|
110
|
+
// configFile: false prevents Vite from re-resolving + double-merging
|
|
111
|
+
// the consumer's vite.config when the InlineConfig is passed to
|
|
112
|
+
// viteBuild / createServer.
|
|
113
|
+
return { ...base, configFile: false };
|
|
55
114
|
}
|
|
56
|
-
async function
|
|
115
|
+
async function loadMkcertPlugin() {
|
|
57
116
|
const mkcert = await import('vite-plugin-mkcert');
|
|
58
117
|
return mkcert.default();
|
|
59
118
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspot/ui-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"description": "Zero-config build toolkit for Brightspot CMS front-end development.",
|
|
@@ -18,26 +18,49 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc --noEmitOnError && chmod +x ./dist/index.js",
|
|
21
|
-
"clean": "shx rm -rf ./dist/*"
|
|
21
|
+
"clean": "shx rm -rf ./dist/*",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
22
24
|
},
|
|
23
25
|
"dependencies": {
|
|
26
|
+
"@eslint/js": "^9.23.0",
|
|
27
|
+
"autoprefixer": "^10.0.0",
|
|
24
28
|
"commander": "^13.0.0",
|
|
25
|
-
"picocolors": "^1.1.0",
|
|
26
|
-
"vite": "^7.0.0",
|
|
27
|
-
"vite-plugin-mkcert": "^1.17.0",
|
|
28
29
|
"eslint": "^9.39.0",
|
|
29
|
-
"@eslint/js": "^9.23.0",
|
|
30
|
-
"typescript-eslint": "^8.29.0",
|
|
31
|
-
"eslint-plugin-prettier": "^5.5.0",
|
|
32
30
|
"eslint-config-prettier": "^10.0.0",
|
|
31
|
+
"eslint-plugin-prettier": "^5.5.0",
|
|
32
|
+
"globals": "^16.0.0",
|
|
33
|
+
"import-meta-resolve": "^4.2.0",
|
|
34
|
+
"picocolors": "^1.1.0",
|
|
33
35
|
"prettier": "^3.2.0",
|
|
34
36
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
35
37
|
"typescript": "^5.9.0",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
+
"typescript-eslint": "^8.29.0",
|
|
39
|
+
"vite": "^8.0.0",
|
|
40
|
+
"vite-plugin-mkcert": "^1.17.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
|
44
|
+
"prettier-plugin-svelte": "^3.0.0",
|
|
45
|
+
"svelte": "^5.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"@sveltejs/vite-plugin-svelte": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"svelte": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"prettier-plugin-svelte": {
|
|
55
|
+
"optional": true
|
|
56
|
+
}
|
|
38
57
|
},
|
|
39
58
|
"devDependencies": {
|
|
59
|
+
"@types/node": "^24.10.0",
|
|
40
60
|
"shx": "^0.4.0",
|
|
41
|
-
"
|
|
61
|
+
"vitest": "^4.1.5"
|
|
62
|
+
},
|
|
63
|
+
"resolutions": {
|
|
64
|
+
"vite": "^8.0.0"
|
|
42
65
|
}
|
|
43
66
|
}
|