@brightspot/ui-builder 2.0.1 → 5.0.4-pre.20260624
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 +5 -1
- package/dist/commands/_load-config.d.ts +0 -2
- package/dist/commands/_load-config.js +0 -23
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +0 -11
- package/dist/commands/dev.d.ts +0 -6
- package/dist/commands/dev.js +0 -38
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.js +0 -71
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -26
- package/dist/lib/logger.d.ts +0 -6
- package/dist/lib/logger.js +0 -15
- package/dist/lib/resolve-config.d.ts +0 -23
- package/dist/lib/resolve-config.js +0 -128
- package/dist/lib/resolve-target.d.ts +0 -1
- package/dist/lib/resolve-target.js +0 -19
- package/dist/vite/auto-plugins.d.ts +0 -2
- package/dist/vite/auto-plugins.js +0 -41
- package/dist/vite/plugin-html-inject.d.ts +0 -16
- package/dist/vite/plugin-html-inject.js +0 -31
- package/dist/vite/proxy-html-rewrite.d.ts +0 -19
- package/dist/vite/proxy-html-rewrite.js +0 -125
- package/dist/vite/vite-config.d.ts +0 -12
- package/dist/vite/vite-config.js +0 -135
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspot/ui-builder",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.4-pre.20260624",
|
|
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"
|
|
@@ -1,23 +0,0 @@
|
|
|
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.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function build(): Promise<void>;
|
package/dist/commands/build.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { build as viteBuild } from 'vite';
|
|
2
|
-
import { log } from '../lib/logger.js';
|
|
3
|
-
import { createBuildConfig } from '../vite/vite-config.js';
|
|
4
|
-
import { loadOrExit } from './_load-config.js';
|
|
5
|
-
export async function build() {
|
|
6
|
-
const builder = loadOrExit();
|
|
7
|
-
log.info(`Building ${builder.format.toUpperCase()} bundle...`);
|
|
8
|
-
const config = await createBuildConfig({ builder });
|
|
9
|
-
await viteBuild(config);
|
|
10
|
-
log.success(`Build complete — output in ${builder.output.dir}/`);
|
|
11
|
-
}
|
package/dist/commands/dev.d.ts
DELETED
package/dist/commands/dev.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { createServer } from 'vite';
|
|
5
|
-
import { log } from '../lib/logger.js';
|
|
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';
|
|
10
|
-
export async function dev(opts) {
|
|
11
|
-
const builder = loadOrExit();
|
|
12
|
-
const target = opts.url ?? readLocalTarget();
|
|
13
|
-
const config = await createDevConfig({ builder, target, port: opts.port });
|
|
14
|
-
const server = await createServer(config);
|
|
15
|
-
await server.listen();
|
|
16
|
-
// Proxy root rarely renders useful HTML; land cmd-click on the app path.
|
|
17
|
-
const openPath = builder.dev?.openPath ?? '/cms/';
|
|
18
|
-
if (server.resolvedUrls && openPath !== '/') {
|
|
19
|
-
const append = (u) => new URL(openPath, u).href;
|
|
20
|
-
server.resolvedUrls.local = server.resolvedUrls.local.map(append);
|
|
21
|
-
server.resolvedUrls.network = server.resolvedUrls.network.map(append);
|
|
22
|
-
}
|
|
23
|
-
server.printUrls();
|
|
24
|
-
log.success('Dev server running — proxying to ' + target);
|
|
25
|
-
}
|
|
26
|
-
function readLocalTarget() {
|
|
27
|
-
try {
|
|
28
|
-
const url = fs.readFileSync(LOCAL_URL_PATH, 'utf8').trim();
|
|
29
|
-
if (url) {
|
|
30
|
-
log.info(`Proxy target: ${url}`);
|
|
31
|
-
return url;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
log.warn(`${LOCAL_URL_PATH} not found — falling back to ${DEFAULT_TARGET}`);
|
|
36
|
-
}
|
|
37
|
-
return DEFAULT_TARGET;
|
|
38
|
-
}
|
package/dist/commands/init.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function init(): Promise<void>;
|
package/dist/commands/init.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { log } from '../lib/logger.js';
|
|
4
|
-
const cwd = process.cwd();
|
|
5
|
-
export async function init() {
|
|
6
|
-
log.info('Initializing Brightspot UI project...');
|
|
7
|
-
writeTsConfig();
|
|
8
|
-
writeEslintConfig();
|
|
9
|
-
patchPackageJson();
|
|
10
|
-
scaffoldEntryPoint();
|
|
11
|
-
log.success('Project initialized. Run `brightspot-ui dev` to start developing.');
|
|
12
|
-
}
|
|
13
|
-
function writeTsConfig() {
|
|
14
|
-
const tsConfigPath = path.join(cwd, 'tsconfig.json');
|
|
15
|
-
if (fs.existsSync(tsConfigPath)) {
|
|
16
|
-
log.warn('tsconfig.json already exists — skipping');
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const config = {
|
|
20
|
-
extends: '@brightspot/ui-builder/configs/tsconfig.base.json',
|
|
21
|
-
};
|
|
22
|
-
fs.writeFileSync(tsConfigPath, JSON.stringify(config, null, 2) + '\n');
|
|
23
|
-
log.success('Created tsconfig.json');
|
|
24
|
-
}
|
|
25
|
-
function writeEslintConfig() {
|
|
26
|
-
const eslintConfigPath = path.join(cwd, 'eslint.config.mjs');
|
|
27
|
-
if (fs.existsSync(eslintConfigPath)) {
|
|
28
|
-
log.warn('eslint.config.mjs already exists — skipping');
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const content = `import config from '@brightspot/ui-builder/configs/eslint.config.mjs'
|
|
32
|
-
|
|
33
|
-
export default [...config]
|
|
34
|
-
`;
|
|
35
|
-
fs.writeFileSync(eslintConfigPath, content);
|
|
36
|
-
log.success('Created eslint.config.mjs');
|
|
37
|
-
}
|
|
38
|
-
function patchPackageJson() {
|
|
39
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
40
|
-
if (!fs.existsSync(pkgPath)) {
|
|
41
|
-
log.warn('No package.json found — skipping script injection');
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
45
|
-
pkg.scripts = pkg.scripts ?? {};
|
|
46
|
-
pkg.scripts.dev = 'brightspot-ui dev';
|
|
47
|
-
pkg.scripts.build = 'brightspot-ui build';
|
|
48
|
-
pkg.prettier = '@brightspot/ui-builder/configs/prettier.config.mjs';
|
|
49
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
50
|
-
log.success('Patched package.json (scripts + prettier)');
|
|
51
|
-
}
|
|
52
|
-
function scaffoldEntryPoint() {
|
|
53
|
-
const srcDir = path.join(cwd, 'src');
|
|
54
|
-
const entryPath = path.join(srcDir, 'index.ts');
|
|
55
|
-
if (fs.existsSync(entryPath)) {
|
|
56
|
-
log.warn('src/index.ts already exists — skipping');
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (!fs.existsSync(srcDir)) {
|
|
60
|
-
fs.mkdirSync(srcDir, { recursive: true });
|
|
61
|
-
}
|
|
62
|
-
const template = `// Entry point for Brightspot CMS bundle
|
|
63
|
-
// Import your styles:
|
|
64
|
-
// import './styles/main.css'
|
|
65
|
-
|
|
66
|
-
// Import your modules:
|
|
67
|
-
// import './tour'
|
|
68
|
-
`;
|
|
69
|
-
fs.writeFileSync(entryPath, template);
|
|
70
|
-
log.success('Created src/index.ts');
|
|
71
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { program } from 'commander';
|
|
3
|
-
program.name('brightspot-ui').description('Zero-config build toolkit for Brightspot CMS front-end development.');
|
|
4
|
-
program
|
|
5
|
-
.command('init')
|
|
6
|
-
.description('Scaffold config files for a new Brightspot front-end project')
|
|
7
|
-
.action(async () => {
|
|
8
|
-
const { init } = await import('./commands/init.js');
|
|
9
|
-
await init();
|
|
10
|
-
});
|
|
11
|
-
program
|
|
12
|
-
.command('dev [url]')
|
|
13
|
-
.description('Start Vite dev server with HTTPS proxy to Brightspot')
|
|
14
|
-
.option('--port <port>', 'Dev server port', parseInt)
|
|
15
|
-
.action(async (url, opts) => {
|
|
16
|
-
const { dev } = await import('./commands/dev.js');
|
|
17
|
-
await dev({ ...opts, url });
|
|
18
|
-
});
|
|
19
|
-
program
|
|
20
|
-
.command('build')
|
|
21
|
-
.description('Build production bundle')
|
|
22
|
-
.action(async () => {
|
|
23
|
-
const { build } = await import('./commands/build.js');
|
|
24
|
-
await build();
|
|
25
|
-
});
|
|
26
|
-
program.parse();
|
package/dist/lib/logger.d.ts
DELETED
package/dist/lib/logger.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import pc from 'picocolors';
|
|
2
|
-
export const log = {
|
|
3
|
-
info(msg) {
|
|
4
|
-
console.log(pc.cyan('ℹ'), msg);
|
|
5
|
-
},
|
|
6
|
-
success(msg) {
|
|
7
|
-
console.log(pc.green('✔'), msg);
|
|
8
|
-
},
|
|
9
|
-
warn(msg) {
|
|
10
|
-
console.log(pc.yellow('⚠'), msg);
|
|
11
|
-
},
|
|
12
|
-
error(msg) {
|
|
13
|
-
console.error(pc.red('✖'), msg);
|
|
14
|
-
},
|
|
15
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface BuilderConfig {
|
|
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
|
-
};
|
|
11
|
-
dev?: {
|
|
12
|
-
/** Path appended to printed dev URLs. "/" disables. */
|
|
13
|
-
openPath: string;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export interface ResolveResult {
|
|
17
|
-
config: BuilderConfig;
|
|
18
|
-
warnings: string[];
|
|
19
|
-
}
|
|
20
|
-
export declare class ConfigError extends Error {
|
|
21
|
-
constructor(message: string);
|
|
22
|
-
}
|
|
23
|
-
export declare function resolveConfig(): ResolveResult;
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
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
|
-
dev: {
|
|
18
|
-
openPath: '/cms/',
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
export function resolveConfig() {
|
|
22
|
-
const pkgPath = path.resolve(process.cwd(), 'package.json');
|
|
23
|
-
let pkg;
|
|
24
|
-
try {
|
|
25
|
-
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
throw new ConfigError(`Could not read ${pkgPath}.`);
|
|
29
|
-
}
|
|
30
|
-
const raw = pkg['brightspot-ui'] ?? {};
|
|
31
|
-
const warnings = [];
|
|
32
|
-
const pkgName = pkg.name;
|
|
33
|
-
const basePath = resolveBasePath(raw.basePath, pkgName, warnings);
|
|
34
|
-
// Normalize leading `./` so downstream consumers (the proxy regex,
|
|
35
|
-
// the dev-entry bootstrap import URL) get a canonical form.
|
|
36
|
-
const entry = (raw.entry ?? DEFAULTS.entry).replace(/^\.\//, '');
|
|
37
|
-
const format = resolveFormat(raw.format);
|
|
38
|
-
const name = resolveIifeName(raw.name, format, pkgName, warnings);
|
|
39
|
-
const output = {
|
|
40
|
-
dir: raw.output?.dir ?? DEFAULTS.output.dir,
|
|
41
|
-
jsName: raw.output?.jsName ?? DEFAULTS.output.jsName,
|
|
42
|
-
cssName: raw.output?.cssName ?? DEFAULTS.output.cssName,
|
|
43
|
-
};
|
|
44
|
-
const openPath = resolveOpenPath(raw.dev?.openPath);
|
|
45
|
-
return { config: { basePath, entry, format, name, output, dev: { openPath } }, warnings };
|
|
46
|
-
}
|
|
47
|
-
function resolveOpenPath(explicit) {
|
|
48
|
-
const value = explicit ?? DEFAULTS.dev.openPath;
|
|
49
|
-
// Reject protocol-relative ('//host/...') so a misconfigured openPath
|
|
50
|
-
// can't redirect the printed dev URL off-site.
|
|
51
|
-
if (!value.startsWith('/') || value.startsWith('//')) {
|
|
52
|
-
throw new ConfigError(`dev.openPath "${value}" must start with a single "/".`);
|
|
53
|
-
}
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
function resolveBasePath(explicit, pkgName, warnings) {
|
|
57
|
-
if (explicit) {
|
|
58
|
-
if (!explicit.startsWith('/') || !explicit.endsWith('/')) {
|
|
59
|
-
throw new ConfigError(`basePath "${explicit}" must start and end with "/".`);
|
|
60
|
-
}
|
|
61
|
-
return explicit;
|
|
62
|
-
}
|
|
63
|
-
if (!pkgName) {
|
|
64
|
-
// BREAKING (2.0): pre-2.0 builds silently fell back to /_resource/dist/
|
|
65
|
-
// when both knobs were absent. We now exit so misconfigured monorepo
|
|
66
|
-
// packages don't ship to the wrong URL. Migration paths below.
|
|
67
|
-
throw new ConfigError('No basePath set in package.json#brightspot-ui.basePath, and no package.json#name to derive one from. ' +
|
|
68
|
-
'To migrate from @brightspot/ui-builder 1.x: ' +
|
|
69
|
-
'either set "name": "<slug>" in package.json (basePath will be inferred as /_resource/<slug>/cms/dist/), ' +
|
|
70
|
-
'or set "brightspot-ui": { "basePath": "/_resource/<slug>/cms/dist/" } explicitly.');
|
|
71
|
-
}
|
|
72
|
-
const inferred = `/_resource/${stripScope(pkgName)}/cms/dist/`;
|
|
73
|
-
warnings.push(`basePath was not set — inferred "${inferred}" from package.json#name "${pkgName}". ` +
|
|
74
|
-
`Set "brightspot-ui": { "basePath": "..." } in package.json to silence this warning.`);
|
|
75
|
-
return inferred;
|
|
76
|
-
}
|
|
77
|
-
function resolveFormat(value) {
|
|
78
|
-
if (value === undefined)
|
|
79
|
-
return DEFAULTS.format;
|
|
80
|
-
if (value !== 'es' && value !== 'iife') {
|
|
81
|
-
throw new ConfigError(`format "${value}" must be "es" or "iife".`);
|
|
82
|
-
}
|
|
83
|
-
return value;
|
|
84
|
-
}
|
|
85
|
-
// Vite library mode requires `name` whenever formats include 'iife' or
|
|
86
|
-
// 'umd' — it's a config-time check, not a function-of-whether-the-entry-
|
|
87
|
-
// has-exports check. So when the consumer chooses iife and hasn't set
|
|
88
|
-
// name, we derive a PascalCase identifier from package.json#name
|
|
89
|
-
// (@brightspot/platform-tours -> PlatformTours) and warn. Returns
|
|
90
|
-
// undefined when format is es (name is irrelevant). Throws when iife
|
|
91
|
-
// is chosen but no valid identifier can be derived.
|
|
92
|
-
function resolveIifeName(explicit, format, pkgName, warnings) {
|
|
93
|
-
if (explicit !== undefined) {
|
|
94
|
-
if (!isValidJsIdentifier(explicit)) {
|
|
95
|
-
throw new ConfigError(`name "${explicit}" is not a valid JavaScript identifier — Vite library mode emits ` +
|
|
96
|
-
`\`var ${explicit} = ...\` for iife output, which would produce a syntax error. ` +
|
|
97
|
-
`Set "brightspot-ui": { "name": "..." } to a PascalCase identifier (e.g. "PlatformTours").`);
|
|
98
|
-
}
|
|
99
|
-
return explicit;
|
|
100
|
-
}
|
|
101
|
-
if (format !== 'iife')
|
|
102
|
-
return undefined;
|
|
103
|
-
const derived = deriveIifeName(pkgName);
|
|
104
|
-
if (derived === null) {
|
|
105
|
-
throw new ConfigError(`format "iife" requires a "name" — could not derive a valid JavaScript identifier from package.json#name "${pkgName ?? '<unset>'}". ` +
|
|
106
|
-
`Set "brightspot-ui": { "name": "..." } in package.json (e.g. "PlatformTours").`);
|
|
107
|
-
}
|
|
108
|
-
warnings.push(`name was not set — derived "${derived}" from package.json#name "${pkgName}". ` +
|
|
109
|
-
`Set "brightspot-ui": { "name": "..." } in package.json to silence this warning.`);
|
|
110
|
-
return derived;
|
|
111
|
-
}
|
|
112
|
-
function deriveIifeName(pkgName) {
|
|
113
|
-
if (!pkgName)
|
|
114
|
-
return null;
|
|
115
|
-
const parts = stripScope(pkgName).split(/[-_.]/).filter(Boolean);
|
|
116
|
-
if (parts.length === 0)
|
|
117
|
-
return null;
|
|
118
|
-
const pascal = parts.map(p => p[0].toUpperCase() + p.slice(1)).join('');
|
|
119
|
-
return isValidJsIdentifier(pascal) ? pascal : null;
|
|
120
|
-
}
|
|
121
|
-
// ASCII-only by design: matches what minifiers and bundlers handle
|
|
122
|
-
// without surprises. ECMAScript permits a wider Unicode range.
|
|
123
|
-
function isValidJsIdentifier(s) {
|
|
124
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
|
|
125
|
-
}
|
|
126
|
-
function stripScope(pkgName) {
|
|
127
|
-
return pkgName.replace(/^@[^/]+\//, '');
|
|
128
|
-
}
|
|
@@ -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,41 +0,0 @@
|
|
|
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,16 +0,0 @@
|
|
|
1
|
-
import type { Plugin } from 'vite';
|
|
2
|
-
export interface DevEntryOptions {
|
|
3
|
-
basePath: string;
|
|
4
|
-
entry: string;
|
|
5
|
-
jsName: string;
|
|
6
|
-
cssName: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Serves a dev bootstrap script at the bundle path so that Vite's
|
|
10
|
-
* HMR client and the real entry point are loaded as ES modules.
|
|
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.
|
|
15
|
-
*/
|
|
16
|
-
export declare function devEntryPlugin({ basePath, entry, jsName, cssName }: DevEntryOptions): Plugin;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Serves a dev bootstrap script at the bundle path so that Vite's
|
|
3
|
-
* HMR client and the real entry point are loaded as ES modules.
|
|
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.
|
|
8
|
-
*/
|
|
9
|
-
export function devEntryPlugin({ basePath, entry, jsName, cssName }) {
|
|
10
|
-
const bundlePath = `${basePath}${jsName}`;
|
|
11
|
-
const cssPath = `${basePath}${cssName}`;
|
|
12
|
-
return {
|
|
13
|
-
name: 'brightspot-dev-entry',
|
|
14
|
-
configureServer(server) {
|
|
15
|
-
server.middlewares.use((req, res, next) => {
|
|
16
|
-
const pathname = req.url?.split('?')[0];
|
|
17
|
-
if (pathname === bundlePath) {
|
|
18
|
-
res.setHeader('Content-Type', 'application/javascript');
|
|
19
|
-
res.end(`import('/@vite/client');import('/${entry}');`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (pathname === cssPath) {
|
|
23
|
-
res.setHeader('Content-Type', 'text/css');
|
|
24
|
-
res.end('');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
next();
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -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
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { type InlineConfig } from 'vite';
|
|
2
|
-
import type { BuilderConfig } from '../lib/resolve-config.js';
|
|
3
|
-
export interface DevConfigOptions {
|
|
4
|
-
builder: BuilderConfig;
|
|
5
|
-
target: string;
|
|
6
|
-
port?: number;
|
|
7
|
-
}
|
|
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
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
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';
|
|
5
|
-
import { devEntryPlugin } from './plugin-html-inject.js';
|
|
6
|
-
// Mask define.amd around the IIFE so UMD-wrapped deps (e.g. loglevel) take
|
|
7
|
-
// the global branch — host-page RequireJS rejects anonymous define() calls.
|
|
8
|
-
// Namespaced var name reduces collision risk with bundled UMD code.
|
|
9
|
-
const AMD_GUARD_INTRO = 'var __bsp_amd=typeof define!=="undefined"&&define&&define.amd;if(__bsp_amd)define.amd=void 0;try{';
|
|
10
|
-
const AMD_GUARD_OUTRO = '}finally{if(__bsp_amd)define.amd=__bsp_amd;}';
|
|
11
|
-
export async function createDevConfig({ builder, target, port }) {
|
|
12
|
-
const cwd = process.cwd();
|
|
13
|
-
const { basePath, entry, output: { jsName, cssName }, } = builder;
|
|
14
|
-
const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
15
|
-
const escaped = escapeRegex(basePath.replace(/^\//, ''));
|
|
16
|
-
// entry.split('/')[0] yields the file itself (e.g. 'index.ts') for a
|
|
17
|
-
// root-level entry, which never matches a URL — skip the segment in
|
|
18
|
-
// that case so /index.ts isn't proxied to the upstream application.
|
|
19
|
-
const entryParts = entry.split('/');
|
|
20
|
-
const entryDirAlt = entryParts.length > 1 ? `|${escapeRegex(entryParts[0])}/` : '';
|
|
21
|
-
// x-forwarded-proto must match the upstream's scheme — otherwise an
|
|
22
|
-
// http CMS canonicalises to its toolUrlPrefix and autoRewrite loops
|
|
23
|
-
// the 302 back through the dev origin.
|
|
24
|
-
const targetScheme = new URL(target).protocol === 'https:' ? 'https' : 'http';
|
|
25
|
-
const [mkcert, autoPlugins] = await Promise.all([loadMkcertPlugin(), loadAutoPlugins(cwd)]);
|
|
26
|
-
const base = {
|
|
27
|
-
root: cwd,
|
|
28
|
-
base: '/',
|
|
29
|
-
plugins: [mkcert, devEntryPlugin({ basePath, entry, jsName, cssName }), ...autoPlugins],
|
|
30
|
-
resolve: {
|
|
31
|
-
preserveSymlinks: true,
|
|
32
|
-
// Vite 8 native: replaces the deprecated vite-tsconfig-paths plugin.
|
|
33
|
-
// Follows tsconfig `extends` chains and applies to all importers
|
|
34
|
-
// (including .svelte / .vue) without the plugin's loose-mode dance.
|
|
35
|
-
tsconfigPaths: true,
|
|
36
|
-
},
|
|
37
|
-
server: {
|
|
38
|
-
port,
|
|
39
|
-
// Default HMR path '/' falls through the proxy to the upstream CMS
|
|
40
|
-
// and 404s; '/@vite/*' is in the regex exclusion list below.
|
|
41
|
-
hmr: { path: '/@vite/hmr' },
|
|
42
|
-
proxy: {
|
|
43
|
-
[`^/(?!(${escaped}${entryDirAlt}|@vite|@fs|node_modules))`]: {
|
|
44
|
-
target,
|
|
45
|
-
// Local CMS dev servers use self-signed certs.
|
|
46
|
-
secure: false,
|
|
47
|
-
// protocolRewrite: Vite is mkcert-https only; http redirects would 404.
|
|
48
|
-
autoRewrite: true,
|
|
49
|
-
protocolRewrite: 'https',
|
|
50
|
-
changeOrigin: true,
|
|
51
|
-
configure: proxy => {
|
|
52
|
-
proxy.on('proxyReq', proxyReq => {
|
|
53
|
-
proxyReq.setHeader('x-forwarded-proto', targetScheme);
|
|
54
|
-
proxyReq.setHeader('x-brightspot-dev', '1');
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
return applyUserConfig(base, cwd, 'serve');
|
|
62
|
-
}
|
|
63
|
-
export async function createBuildConfig({ builder }) {
|
|
64
|
-
const cwd = process.cwd();
|
|
65
|
-
const { basePath, entry, format, name, output } = builder;
|
|
66
|
-
const autoPlugins = await loadAutoPlugins(cwd);
|
|
67
|
-
const base = {
|
|
68
|
-
root: cwd,
|
|
69
|
-
plugins: [...autoPlugins],
|
|
70
|
-
resolve: {
|
|
71
|
-
preserveSymlinks: true,
|
|
72
|
-
// Vite 8 native: replaces the deprecated vite-tsconfig-paths plugin.
|
|
73
|
-
// Follows tsconfig `extends` chains and applies to all importers
|
|
74
|
-
// (including .svelte / .vue) without the plugin's loose-mode dance.
|
|
75
|
-
tsconfigPaths: true,
|
|
76
|
-
},
|
|
77
|
-
build: {
|
|
78
|
-
cssCodeSplit: false,
|
|
79
|
-
// Vite 7's `build.lib` implied a single bundle. Vite 8 dropped that
|
|
80
|
-
// default, so any consumer with `await import(...)` in its graph
|
|
81
|
-
// emits sidecar `*-<hash>.mjs` chunks that the toolkit's promised
|
|
82
|
-
// two-file output (bundle.js + style.css) doesn't account for —
|
|
83
|
-
// and that the gradle processResources task doesn't pattern-match
|
|
84
|
-
// against, so chunks 404 in production. Default-off here restores
|
|
85
|
-
// the contract; consumers who want code-splitting can opt back in
|
|
86
|
-
// via Layer 3 (vite.config.ts → build.rollupOptions.output.
|
|
87
|
-
// codeSplitting = true). Note: Rolldown deprecated the equivalent
|
|
88
|
-
// `inlineDynamicImports: true` in favor of `codeSplitting: false`.
|
|
89
|
-
rollupOptions: {
|
|
90
|
-
output: {
|
|
91
|
-
codeSplitting: false,
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
};
|
|
96
|
-
const knobs = {
|
|
97
|
-
base: basePath,
|
|
98
|
-
build: {
|
|
99
|
-
lib: {
|
|
100
|
-
entry: path.resolve(cwd, entry),
|
|
101
|
-
formats: [format],
|
|
102
|
-
fileName: () => output.jsName,
|
|
103
|
-
// Vite library mode requires `name` whenever format is 'iife'.
|
|
104
|
-
// resolveConfig guarantees this is set when format is iife (explicit
|
|
105
|
-
// or derived from package.json#name), so this guard is defensive
|
|
106
|
-
// against a manually-constructed BuilderConfig that violates the
|
|
107
|
-
// invariant — we propagate undefined to Vite rather than inject a
|
|
108
|
-
// silent default; Vite then surfaces its own clear error.
|
|
109
|
-
...(format === 'iife' && name ? { name } : {}),
|
|
110
|
-
},
|
|
111
|
-
outDir: output.dir,
|
|
112
|
-
rollupOptions: {
|
|
113
|
-
output: {
|
|
114
|
-
assetFileNames: () => output.cssName,
|
|
115
|
-
...(format === 'iife' ? { intro: AMD_GUARD_INTRO, outro: AMD_GUARD_OUTRO } : {}),
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
return applyUserConfig(mergeConfig(base, knobs), cwd, 'build');
|
|
121
|
-
}
|
|
122
|
-
async function applyUserConfig(merged, cwd, command) {
|
|
123
|
-
const loaded = await loadConfigFromFile({ command, mode: command === 'serve' ? 'development' : 'production' }, undefined, cwd);
|
|
124
|
-
if (loaded)
|
|
125
|
-
log.info(`Merging consumer Vite config from ${path.relative(cwd, loaded.path)}.`);
|
|
126
|
-
const base = loaded ? mergeConfig(merged, loaded.config) : merged;
|
|
127
|
-
// configFile: false prevents Vite from re-resolving + double-merging
|
|
128
|
-
// the consumer's vite.config when the InlineConfig is passed to
|
|
129
|
-
// viteBuild / createServer.
|
|
130
|
-
return { ...base, configFile: false };
|
|
131
|
-
}
|
|
132
|
-
async function loadMkcertPlugin() {
|
|
133
|
-
const mkcert = await import('vite-plugin-mkcert');
|
|
134
|
-
return mkcert.default();
|
|
135
|
-
}
|