@hardimpactdev/craft-ui 0.0.5 → 0.0.7
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/dist/src/vite/aliases.d.ts +13 -0
- package/dist/src/vite/craftPlugin.d.ts +4 -0
- package/dist/src/vite/defineCraftConfig.d.ts +6 -26
- package/dist/src/vite/plugins.d.ts +17 -0
- package/dist/src/vite/server.d.ts +10 -0
- package/dist/src/vite/types.d.ts +64 -0
- package/dist/vite/aliases.js +62 -0
- package/dist/vite/craftPlugin.js +105 -0
- package/dist/vite/defineCraftConfig.js +17 -109
- package/dist/vite/plugins.js +59 -0
- package/dist/vite/server.js +30 -0
- package/dist/vite/ts/aliases.ts +132 -0
- package/dist/vite/ts/craftPlugin.ts +106 -0
- package/dist/vite/ts/defineCraftConfig.ts +23 -134
- package/dist/vite/ts/plugins.ts +86 -0
- package/dist/vite/ts/server.ts +42 -0
- package/dist/vite/ts/types.ts +73 -0
- package/dist/vite/types.js +0 -0
- package/package.json +11 -4
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Alias } from 'vite';
|
|
2
|
+
import { CraftConfigOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Configure Vite resolve options for the Craft stack
|
|
5
|
+
*
|
|
6
|
+
* Handles two scenarios:
|
|
7
|
+
* 1. Normal: Just returns dedupe config and any custom aliases
|
|
8
|
+
* 2. Local development: Adds aliases for symlinked craft-ui to enable HMR
|
|
9
|
+
*/
|
|
10
|
+
export declare function getResolveConfig(options: CraftConfigOptions): {
|
|
11
|
+
dedupe: string[];
|
|
12
|
+
alias: Alias[];
|
|
13
|
+
};
|
|
@@ -1,26 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
ssr?: string | string[];
|
|
8
|
-
ssrOutputDirectory?: string;
|
|
9
|
-
refresh?: boolean | string | string[];
|
|
10
|
-
hotFile?: string;
|
|
11
|
-
detectTls?: string | boolean;
|
|
12
|
-
valetTls?: string | boolean;
|
|
13
|
-
transformOnServe?: (code: string, url: string) => string;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export declare function defineCraftConfig(options?: CraftConfigOptions): UserConfig;
|
|
17
|
-
/**
|
|
18
|
-
* Craft Vite plugin that provides virtual module for app initialization
|
|
19
|
-
*/
|
|
20
|
-
export declare function craft(): {
|
|
21
|
-
name: string;
|
|
22
|
-
enforce: "pre";
|
|
23
|
-
resolveId(id: string): "virtual:craft" | undefined;
|
|
24
|
-
load(id: string): "\n import { createInertiaApp } from \"@inertiajs/vue3\";\n import { resolvePageComponent } from \"laravel-vite-plugin/inertia-helpers\";\n import { createApp, h } from \"vue\";\n import { i18n } from \"@hardimpactdev/craft-ui\";\n import { TooltipProvider } from \"reka-ui\";\n\n const appName = import.meta.env.VITE_APP_NAME || \"Laravel\";\n\n export function initializeCraft(options) {\n const { enhanceVue, layouts } = options || {};\n\n return createInertiaApp({\n title: (title) => `${title} - ${appName}`,\n resolve: (name) => {\n const pages = import.meta.glob('/resources/js/pages/**/*.vue', {\n eager: true,\n });\n\n const page = pages[`/resources/js/pages/${name}.vue`];\n\n let defaultLayout = undefined;\n\n if (layouts) {\n Object.entries(layouts).forEach(([key, value]) => {\n if(name.startsWith(key)) {\n defaultLayout = value;\n }\n\n if(!defaultLayout && layouts.default) {\n defaultLayout = layouts.default;\n }\n });\n }\n\n if(!page) {\n const errorMessage = `Page not found: ${name}.vue`;\n\n console.error(`[Inertia] ${errorMessage}`);\n }\n\n page.default.layout = defaultLayout;\n return page;\n },\n setup({ el, App, props, plugin }) {\n // Get the language files and transform the paths\n const langGlob = import.meta.glob(\"/lang/*.json\", { eager: true });\n\n // Transform absolute paths to relative paths expected by i18n\n const transformedLangs = {};\n Object.entries(langGlob).forEach(([absolutePath, module]) => {\n // Convert \"/lang/en.json\" to \"../../lang/en.json\"\n const relativePath = absolutePath.replace('/lang/', '../../lang/');\n transformedLangs[relativePath] = module;\n });\n\n let app = createApp({ render: () => h(TooltipProvider, null, () => h(App, props)) })\n .use(plugin)\n .use(i18n, {\n langs: transformedLangs,\n changeLanguageRoute: '/change-language',\n });\n\n if (enhanceVue) {\n app = enhanceVue(app);\n }\n\n app.mount(el);\n },\n progress: {\n includeCSS: false,\n },\n });\n }" | undefined;
|
|
25
|
-
};
|
|
26
|
-
export {};
|
|
1
|
+
import { CraftConfigOptions } from './types.js';
|
|
2
|
+
export type { CraftConfigOptions } from './types.js';
|
|
3
|
+
export { getPlugins } from './plugins.js';
|
|
4
|
+
export { getServerConfig } from './server.js';
|
|
5
|
+
export { getResolveConfig } from './aliases.js';
|
|
6
|
+
export declare function defineCraftConfig(options?: CraftConfigOptions): import('vite').UserConfigFnObject;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { CraftConfigOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configure all Vite plugins for the Craft stack
|
|
4
|
+
*
|
|
5
|
+
* Includes:
|
|
6
|
+
* - craft: Virtual module for Inertia app initialization
|
|
7
|
+
* - laravel: Laravel Vite integration
|
|
8
|
+
* - tailwindcss: Tailwind CSS v4
|
|
9
|
+
* - i18n: Laravel Vue i18n
|
|
10
|
+
* - vue: Vue 3 SFC support
|
|
11
|
+
* - vueDevTools: Vue DevTools integration
|
|
12
|
+
* - run: Auto-run artisan commands on file changes (dev only)
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPlugins(options: CraftConfigOptions): (false | {
|
|
15
|
+
name: string;
|
|
16
|
+
enforce: string;
|
|
17
|
+
}[] | import('vite').Plugin<any> | import('vite').PluginOption[] | Promise<import('vite').Plugin<any> | (false | null | undefined) | import('vite').PluginOption[]> | null | undefined)[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ServerOptions } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Configure Vite dev server for Laravel apps behind a reverse proxy (Caddy/nginx)
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - HMR WebSocket connection through the proxy (wss://app.test:443)
|
|
7
|
+
* - Correct origin for CORS and asset URLs
|
|
8
|
+
* - Binding to all interfaces for proxy access
|
|
9
|
+
*/
|
|
10
|
+
export declare function getServerConfig(mode: string): ServerOptions;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Alias } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for defineCraftConfig
|
|
4
|
+
*/
|
|
5
|
+
export interface CraftConfigOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Laravel Vite plugin options
|
|
8
|
+
*/
|
|
9
|
+
laravel?: {
|
|
10
|
+
/** Entry points for the application */
|
|
11
|
+
input?: string[];
|
|
12
|
+
/** Enable file refresh on changes */
|
|
13
|
+
refresh?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Additional Vite plugins to include
|
|
17
|
+
*/
|
|
18
|
+
plugins?: any[];
|
|
19
|
+
/**
|
|
20
|
+
* Additional Vite aliases
|
|
21
|
+
*/
|
|
22
|
+
aliases?: Alias[];
|
|
23
|
+
/**
|
|
24
|
+
* Local UI library configuration for development with symlinked packages
|
|
25
|
+
*/
|
|
26
|
+
ui?: LocalUiOptions;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for local UI library development
|
|
30
|
+
*
|
|
31
|
+
* When developing craft-ui alongside an app, this enables HMR by:
|
|
32
|
+
* 1. Redirecting package imports to source files instead of dist/
|
|
33
|
+
* 2. Resolving conflicting aliases (like @/) based on importer location
|
|
34
|
+
*/
|
|
35
|
+
export interface LocalUiOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Relative path from app root to the symlinked craft-ui directory
|
|
38
|
+
* @example '../craft-ui'
|
|
39
|
+
*/
|
|
40
|
+
localPath?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Internal alias configuration for local package resolution
|
|
44
|
+
*/
|
|
45
|
+
export interface LocalAliasConfig {
|
|
46
|
+
/** Regex pattern to match imports */
|
|
47
|
+
pattern: RegExp;
|
|
48
|
+
/** The replacement path or alias */
|
|
49
|
+
replacement: string;
|
|
50
|
+
/**
|
|
51
|
+
* Absolute path to the library (for importer detection)
|
|
52
|
+
* If set along with basePaths, enables context-aware resolution
|
|
53
|
+
*/
|
|
54
|
+
libraryPath?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Base paths for context-aware resolution
|
|
57
|
+
*/
|
|
58
|
+
basePaths?: {
|
|
59
|
+
/** Path to use when import is from the app */
|
|
60
|
+
app: string;
|
|
61
|
+
/** Path to use when import is from the library */
|
|
62
|
+
library: string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
function getResolveConfig(options) {
|
|
3
|
+
const aliases = [...options.aliases || []];
|
|
4
|
+
if (isDevMode() && options.ui?.localPath) {
|
|
5
|
+
aliases.push(...createLocalDevAliases(options.ui.localPath));
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
// Prevent duplicate instances of packages that must be singletons
|
|
9
|
+
dedupe: ["@inertiajs/vue3", "@tailwindcss/vite"],
|
|
10
|
+
alias: aliases
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function createLocalDevAliases(localPath) {
|
|
14
|
+
const absoluteLibraryPath = path.resolve(process.cwd(), localPath);
|
|
15
|
+
const configs = [
|
|
16
|
+
// Handle @/ alias - resolve based on importer location
|
|
17
|
+
{
|
|
18
|
+
pattern: /^@\//,
|
|
19
|
+
replacement: "@/",
|
|
20
|
+
libraryPath: absoluteLibraryPath,
|
|
21
|
+
basePaths: {
|
|
22
|
+
app: "./resources/js",
|
|
23
|
+
library: path.join(localPath, "src")
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
// Redirect package imports to source
|
|
27
|
+
{
|
|
28
|
+
pattern: /^@hardimpactdev\/craft-ui/,
|
|
29
|
+
replacement: path.join(localPath, "index.ts")
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
return configs.map(createAliasFromConfig);
|
|
33
|
+
}
|
|
34
|
+
function createAliasFromConfig(config) {
|
|
35
|
+
if (!config.libraryPath || !config.basePaths) {
|
|
36
|
+
return {
|
|
37
|
+
find: config.pattern,
|
|
38
|
+
replacement: path.resolve(process.cwd(), config.replacement)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
find: config.pattern,
|
|
43
|
+
replacement: config.replacement,
|
|
44
|
+
customResolver: createContextAwareResolver(config)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createContextAwareResolver(config) {
|
|
48
|
+
return async function customResolver(source, importer) {
|
|
49
|
+
const isFromLibrary = importer?.includes(config.libraryPath);
|
|
50
|
+
const basePath = isFromLibrary ? config.basePaths.library : config.basePaths.app;
|
|
51
|
+
const importPath = source.replace(config.replacement, "");
|
|
52
|
+
const resolvedPath = path.resolve(process.cwd(), basePath, importPath);
|
|
53
|
+
const result = await this.resolve(resolvedPath);
|
|
54
|
+
return result?.id;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function isDevMode() {
|
|
58
|
+
return !process.argv.includes("build");
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
getResolveConfig
|
|
62
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
function craft() {
|
|
2
|
+
const plugins = [];
|
|
3
|
+
plugins.push({
|
|
4
|
+
name: "craft",
|
|
5
|
+
enforce: "pre",
|
|
6
|
+
config() {
|
|
7
|
+
return {
|
|
8
|
+
optimizeDeps: {
|
|
9
|
+
exclude: [
|
|
10
|
+
"@hardimpactdev/craft-ui",
|
|
11
|
+
"@tailwindcss/vite",
|
|
12
|
+
"laravel-vue-i18n/vite"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
resolveId(id) {
|
|
18
|
+
if (id === "virtual:craft") {
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
load(id) {
|
|
23
|
+
if (id === "virtual:craft") {
|
|
24
|
+
return `
|
|
25
|
+
import { createInertiaApp } from "@inertiajs/vue3";
|
|
26
|
+
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
|
|
27
|
+
import { createApp, h } from "vue";
|
|
28
|
+
import { i18n } from "@hardimpactdev/craft-ui";
|
|
29
|
+
import { TooltipProvider } from "reka-ui";
|
|
30
|
+
|
|
31
|
+
const appName = import.meta.env.VITE_APP_NAME || "Laravel";
|
|
32
|
+
|
|
33
|
+
export function initializeCraft(options) {
|
|
34
|
+
const { enhanceVue, layouts } = options || {};
|
|
35
|
+
|
|
36
|
+
return createInertiaApp({
|
|
37
|
+
title: (title) => \`\${title} - \${appName}\`,
|
|
38
|
+
resolve: (name) => {
|
|
39
|
+
const pages = import.meta.glob('/resources/js/pages/**/*.vue', {
|
|
40
|
+
eager: true,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const page = pages[\`/resources/js/pages/\${name}.vue\`];
|
|
44
|
+
|
|
45
|
+
let defaultLayout = undefined;
|
|
46
|
+
|
|
47
|
+
if (layouts) {
|
|
48
|
+
Object.entries(layouts).forEach(([key, value]) => {
|
|
49
|
+
if(name.startsWith(key)) {
|
|
50
|
+
defaultLayout = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if(!defaultLayout && layouts.default) {
|
|
54
|
+
defaultLayout = layouts.default;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if(!page) {
|
|
60
|
+
const errorMessage = \`Page not found: \${name}.vue\`;
|
|
61
|
+
|
|
62
|
+
console.error(\`[Inertia] \${errorMessage}\`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
page.default.layout = defaultLayout;
|
|
66
|
+
return page;
|
|
67
|
+
},
|
|
68
|
+
setup({ el, App, props, plugin }) {
|
|
69
|
+
// Get the language files and transform the paths
|
|
70
|
+
const langGlob = import.meta.glob("/lang/*.json", { eager: true });
|
|
71
|
+
|
|
72
|
+
// Transform absolute paths to relative paths expected by i18n
|
|
73
|
+
const transformedLangs = {};
|
|
74
|
+
Object.entries(langGlob).forEach(([absolutePath, module]) => {
|
|
75
|
+
// Convert "/lang/en.json" to "../../lang/en.json"
|
|
76
|
+
const relativePath = absolutePath.replace('/lang/', '../../lang/');
|
|
77
|
+
transformedLangs[relativePath] = module;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
let app = createApp({ render: () => h(TooltipProvider, null, () => h(App, props)) })
|
|
81
|
+
.use(plugin)
|
|
82
|
+
.use(i18n, {
|
|
83
|
+
langs: transformedLangs,
|
|
84
|
+
changeLanguageRoute: '/change-language',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (enhanceVue) {
|
|
88
|
+
app = enhanceVue(app);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
app.mount(el);
|
|
92
|
+
},
|
|
93
|
+
progress: {
|
|
94
|
+
includeCSS: false,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return plugins;
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
craft
|
|
105
|
+
};
|
|
@@ -1,115 +1,23 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { getPlugins } from "./plugins.js";
|
|
3
|
+
import { getServerConfig } from "./server.js";
|
|
4
|
+
import { getResolveConfig } from "./aliases.js";
|
|
5
|
+
import { getPlugins as getPlugins2 } from "./plugins.js";
|
|
6
|
+
import { getServerConfig as getServerConfig2 } from "./server.js";
|
|
7
|
+
import { getResolveConfig as getResolveConfig2 } from "./aliases.js";
|
|
1
8
|
function defineCraftConfig(options = {}) {
|
|
2
|
-
|
|
3
|
-
input: ["resources/js/app.ts"]
|
|
4
|
-
};
|
|
5
|
-
return {
|
|
9
|
+
return defineConfig(({ mode }) => ({
|
|
6
10
|
plugins: [
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
...getPlugins(options),
|
|
12
|
+
...options.plugins || []
|
|
9
13
|
],
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"@tailwindcss/vite",
|
|
14
|
-
"laravel-vue-i18n/vite"
|
|
15
|
-
]
|
|
16
|
-
},
|
|
17
|
-
// Store laravel config for the consuming app to use
|
|
18
|
-
define: {
|
|
19
|
-
__CRAFT_LARAVEL_CONFIG__: JSON.stringify(laravelConfig)
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function craft() {
|
|
24
|
-
return {
|
|
25
|
-
name: "craft",
|
|
26
|
-
enforce: "pre",
|
|
27
|
-
resolveId(id) {
|
|
28
|
-
if (id === "virtual:craft") {
|
|
29
|
-
return id;
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
load(id) {
|
|
33
|
-
if (id === "virtual:craft") {
|
|
34
|
-
return `
|
|
35
|
-
import { createInertiaApp } from "@inertiajs/vue3";
|
|
36
|
-
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
|
|
37
|
-
import { createApp, h } from "vue";
|
|
38
|
-
import { i18n } from "@hardimpactdev/craft-ui";
|
|
39
|
-
import { TooltipProvider } from "reka-ui";
|
|
40
|
-
|
|
41
|
-
const appName = import.meta.env.VITE_APP_NAME || "Laravel";
|
|
42
|
-
|
|
43
|
-
export function initializeCraft(options) {
|
|
44
|
-
const { enhanceVue, layouts } = options || {};
|
|
45
|
-
|
|
46
|
-
return createInertiaApp({
|
|
47
|
-
title: (title) => \`\${title} - \${appName}\`,
|
|
48
|
-
resolve: (name) => {
|
|
49
|
-
const pages = import.meta.glob('/resources/js/pages/**/*.vue', {
|
|
50
|
-
eager: true,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const page = pages[\`/resources/js/pages/\${name}.vue\`];
|
|
54
|
-
|
|
55
|
-
let defaultLayout = undefined;
|
|
56
|
-
|
|
57
|
-
if (layouts) {
|
|
58
|
-
Object.entries(layouts).forEach(([key, value]) => {
|
|
59
|
-
if(name.startsWith(key)) {
|
|
60
|
-
defaultLayout = value;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if(!defaultLayout && layouts.default) {
|
|
64
|
-
defaultLayout = layouts.default;
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if(!page) {
|
|
70
|
-
const errorMessage = \`Page not found: \${name}.vue\`;
|
|
71
|
-
|
|
72
|
-
console.error(\`[Inertia] \${errorMessage}\`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
page.default.layout = defaultLayout;
|
|
76
|
-
return page;
|
|
77
|
-
},
|
|
78
|
-
setup({ el, App, props, plugin }) {
|
|
79
|
-
// Get the language files and transform the paths
|
|
80
|
-
const langGlob = import.meta.glob("/lang/*.json", { eager: true });
|
|
81
|
-
|
|
82
|
-
// Transform absolute paths to relative paths expected by i18n
|
|
83
|
-
const transformedLangs = {};
|
|
84
|
-
Object.entries(langGlob).forEach(([absolutePath, module]) => {
|
|
85
|
-
// Convert "/lang/en.json" to "../../lang/en.json"
|
|
86
|
-
const relativePath = absolutePath.replace('/lang/', '../../lang/');
|
|
87
|
-
transformedLangs[relativePath] = module;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
let app = createApp({ render: () => h(TooltipProvider, null, () => h(App, props)) })
|
|
91
|
-
.use(plugin)
|
|
92
|
-
.use(i18n, {
|
|
93
|
-
langs: transformedLangs,
|
|
94
|
-
changeLanguageRoute: '/change-language',
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (enhanceVue) {
|
|
98
|
-
app = enhanceVue(app);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
app.mount(el);
|
|
102
|
-
},
|
|
103
|
-
progress: {
|
|
104
|
-
includeCSS: false,
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
}`;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
};
|
|
14
|
+
resolve: getResolveConfig(options),
|
|
15
|
+
server: getServerConfig(mode)
|
|
16
|
+
}));
|
|
111
17
|
}
|
|
112
18
|
export {
|
|
113
|
-
|
|
114
|
-
|
|
19
|
+
defineCraftConfig,
|
|
20
|
+
getPlugins2 as getPlugins,
|
|
21
|
+
getResolveConfig2 as getResolveConfig,
|
|
22
|
+
getServerConfig2 as getServerConfig
|
|
115
23
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import vue from "@vitejs/plugin-vue";
|
|
2
|
+
import laravel from "laravel-vite-plugin";
|
|
3
|
+
import { run } from "vite-plugin-run";
|
|
4
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
5
|
+
import i18n from "laravel-vue-i18n/vite";
|
|
6
|
+
import vueDevTools from "vite-plugin-vue-devtools";
|
|
7
|
+
import { craft } from "./craftPlugin.js";
|
|
8
|
+
function getPlugins(options) {
|
|
9
|
+
return [
|
|
10
|
+
craft(),
|
|
11
|
+
laravel({
|
|
12
|
+
input: options.laravel?.input || ["resources/js/app.ts"],
|
|
13
|
+
refresh: options.laravel?.refresh ?? true
|
|
14
|
+
}),
|
|
15
|
+
tailwindcss(),
|
|
16
|
+
i18n(),
|
|
17
|
+
vue({
|
|
18
|
+
template: {
|
|
19
|
+
transformAssetUrls: {
|
|
20
|
+
base: null,
|
|
21
|
+
includeAbsolute: false
|
|
22
|
+
},
|
|
23
|
+
compilerOptions: {
|
|
24
|
+
isCustomElement: (tag) => tag === "trix-editor"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}),
|
|
28
|
+
getArtisanRunners(),
|
|
29
|
+
vueDevTools({
|
|
30
|
+
appendTo: "virtual:craft",
|
|
31
|
+
launchEditor: import.meta.env?.VITE_EDITOR || "cursor"
|
|
32
|
+
})
|
|
33
|
+
].filter(Boolean);
|
|
34
|
+
}
|
|
35
|
+
function getArtisanRunners() {
|
|
36
|
+
if (false) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return run([
|
|
40
|
+
{
|
|
41
|
+
name: "waymaker",
|
|
42
|
+
run: ["php", "artisan", "waymaker:generate"],
|
|
43
|
+
pattern: ["app/**/Http/**/*.php"]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "wayfinder",
|
|
47
|
+
run: ["php", "artisan", "wayfinder:generate"],
|
|
48
|
+
pattern: ["routes/*.php", "app/**/Http/**/*.php"]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "typescript",
|
|
52
|
+
run: ["php", "artisan", "typescript:transform"],
|
|
53
|
+
pattern: ["app/{Data,Enums}/**/*.php"]
|
|
54
|
+
}
|
|
55
|
+
]);
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
getPlugins
|
|
59
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { loadEnv } from "vite";
|
|
2
|
+
function getServerConfig(mode) {
|
|
3
|
+
const env = loadEnv(mode, process.cwd());
|
|
4
|
+
const appUrl = env.VITE_APP_URL;
|
|
5
|
+
if (!appUrl) {
|
|
6
|
+
return { host: "0.0.0.0" };
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(appUrl);
|
|
10
|
+
const isHttps = url.protocol === "https:";
|
|
11
|
+
return {
|
|
12
|
+
// Accept connections from reverse proxy
|
|
13
|
+
host: "0.0.0.0",
|
|
14
|
+
// Tell Vite the public origin for asset URLs
|
|
15
|
+
origin: appUrl,
|
|
16
|
+
// Configure HMR to connect through the reverse proxy
|
|
17
|
+
// Without this, browser would try localhost:5173 directly
|
|
18
|
+
hmr: {
|
|
19
|
+
host: url.hostname,
|
|
20
|
+
protocol: isHttps ? "wss" : "ws",
|
|
21
|
+
clientPort: isHttps ? 443 : parseInt(url.port) || 80
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
} catch {
|
|
25
|
+
return { host: "0.0.0.0" };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
getServerConfig
|
|
30
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import type { Alias } from 'vite';
|
|
3
|
+
import type { CraftConfigOptions, LocalAliasConfig } from './types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configure Vite resolve options for the Craft stack
|
|
7
|
+
*
|
|
8
|
+
* Handles two scenarios:
|
|
9
|
+
* 1. Normal: Just returns dedupe config and any custom aliases
|
|
10
|
+
* 2. Local development: Adds aliases for symlinked craft-ui to enable HMR
|
|
11
|
+
*/
|
|
12
|
+
export function getResolveConfig(options: CraftConfigOptions) {
|
|
13
|
+
const aliases: Alias[] = [...(options.aliases || [])];
|
|
14
|
+
|
|
15
|
+
// Add local development aliases when craft-ui is symlinked
|
|
16
|
+
if (isDevMode() && options.ui?.localPath) {
|
|
17
|
+
aliases.push(...createLocalDevAliases(options.ui.localPath));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
// Prevent duplicate instances of packages that must be singletons
|
|
22
|
+
dedupe: ['@inertiajs/vue3', '@tailwindcss/vite'],
|
|
23
|
+
alias: aliases,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create aliases for local craft-ui development
|
|
29
|
+
*
|
|
30
|
+
* When craft-ui is symlinked (e.g., for development), we need to:
|
|
31
|
+
*
|
|
32
|
+
* 1. Redirect package imports to source files
|
|
33
|
+
* `import { Button } from '@hardimpactdev/craft-ui'`
|
|
34
|
+
* → resolves to `../craft-ui/index.ts` instead of `dist/`
|
|
35
|
+
* This enables HMR (dist files can't hot-reload)
|
|
36
|
+
*
|
|
37
|
+
* 2. Handle the @/ alias conflict
|
|
38
|
+
* Both the app and craft-ui use @/ as an alias:
|
|
39
|
+
* - App: @/ → ./resources/js
|
|
40
|
+
* - craft-ui: @/ → ./src
|
|
41
|
+
*
|
|
42
|
+
* When a file in craft-ui imports `@/lib/utils`, we need to resolve
|
|
43
|
+
* it to craft-ui/src/lib/utils, not app/resources/js/lib/utils.
|
|
44
|
+
* The resolver checks WHERE the import comes from to decide.
|
|
45
|
+
*/
|
|
46
|
+
function createLocalDevAliases(localPath: string): Alias[] {
|
|
47
|
+
const absoluteLibraryPath = path.resolve(process.cwd(), localPath);
|
|
48
|
+
|
|
49
|
+
const configs: LocalAliasConfig[] = [
|
|
50
|
+
// Handle @/ alias - resolve based on importer location
|
|
51
|
+
{
|
|
52
|
+
pattern: /^@\//,
|
|
53
|
+
replacement: '@/',
|
|
54
|
+
libraryPath: absoluteLibraryPath,
|
|
55
|
+
basePaths: {
|
|
56
|
+
app: './resources/js',
|
|
57
|
+
library: path.join(localPath, 'src'),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// Redirect package imports to source
|
|
62
|
+
{
|
|
63
|
+
pattern: /^@hardimpactdev\/craft-ui/,
|
|
64
|
+
replacement: path.join(localPath, 'index.ts'),
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
return configs.map(createAliasFromConfig);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a Vite alias from our config format
|
|
73
|
+
*/
|
|
74
|
+
function createAliasFromConfig(config: LocalAliasConfig): Alias {
|
|
75
|
+
// Simple redirect (no context-aware resolution needed)
|
|
76
|
+
if (!config.libraryPath || !config.basePaths) {
|
|
77
|
+
return {
|
|
78
|
+
find: config.pattern,
|
|
79
|
+
replacement: path.resolve(process.cwd(), config.replacement),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Context-aware resolution: choose base path based on importer location
|
|
84
|
+
return {
|
|
85
|
+
find: config.pattern,
|
|
86
|
+
replacement: config.replacement,
|
|
87
|
+
customResolver: createContextAwareResolver(config),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create a resolver that picks the right base path based on where the import comes from
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* // Import from app file → use app base path
|
|
96
|
+
* // resources/js/pages/Home.vue imports @/lib/utils
|
|
97
|
+
* // → resolves to resources/js/lib/utils
|
|
98
|
+
*
|
|
99
|
+
* // Import from library file → use library base path
|
|
100
|
+
* // ../craft-ui/src/components/Button.vue imports @/lib/utils
|
|
101
|
+
* // → resolves to ../craft-ui/src/lib/utils
|
|
102
|
+
*/
|
|
103
|
+
function createContextAwareResolver(config: LocalAliasConfig) {
|
|
104
|
+
return async function customResolver(
|
|
105
|
+
this: { resolve: (id: string, importer?: string) => Promise<{ id: string } | null> },
|
|
106
|
+
source: string,
|
|
107
|
+
importer: string | undefined
|
|
108
|
+
): Promise<string | undefined> {
|
|
109
|
+
// Determine if the import is coming from the library or the app
|
|
110
|
+
const isFromLibrary = importer?.includes(config.libraryPath!);
|
|
111
|
+
const basePath = isFromLibrary ? config.basePaths!.library : config.basePaths!.app;
|
|
112
|
+
|
|
113
|
+
// Build the full path by combining:
|
|
114
|
+
// 1. Current working directory
|
|
115
|
+
// 2. The appropriate base path (app or library)
|
|
116
|
+
// 3. The import path (minus the alias prefix)
|
|
117
|
+
const importPath = source.replace(config.replacement, '');
|
|
118
|
+
const resolvedPath = path.resolve(process.cwd(), basePath, importPath);
|
|
119
|
+
|
|
120
|
+
// Use Vite's resolver to get the final path (handles extensions, index files, etc.)
|
|
121
|
+
const result = await this.resolve(resolvedPath);
|
|
122
|
+
return result?.id;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if we're in development mode
|
|
128
|
+
* Used to skip local dev aliases during production builds
|
|
129
|
+
*/
|
|
130
|
+
function isDevMode(): boolean {
|
|
131
|
+
return !process.argv.includes('build');
|
|
132
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export function craft() {
|
|
2
|
+
const plugins = [];
|
|
3
|
+
|
|
4
|
+
// Core plugin
|
|
5
|
+
plugins.push({
|
|
6
|
+
name: 'craft',
|
|
7
|
+
enforce: 'pre',
|
|
8
|
+
config() {
|
|
9
|
+
return {
|
|
10
|
+
optimizeDeps: {
|
|
11
|
+
exclude: [
|
|
12
|
+
'@hardimpactdev/craft-ui',
|
|
13
|
+
'@tailwindcss/vite',
|
|
14
|
+
'laravel-vue-i18n/vite'
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
resolveId(id: string) {
|
|
21
|
+
if (id === 'virtual:craft') {
|
|
22
|
+
return id;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
load(id: string) {
|
|
26
|
+
if (id === 'virtual:craft') {
|
|
27
|
+
return `
|
|
28
|
+
import { createInertiaApp } from "@inertiajs/vue3";
|
|
29
|
+
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
|
|
30
|
+
import { createApp, h } from "vue";
|
|
31
|
+
import { i18n } from "@hardimpactdev/craft-ui";
|
|
32
|
+
import { TooltipProvider } from "reka-ui";
|
|
33
|
+
|
|
34
|
+
const appName = import.meta.env.VITE_APP_NAME || "Laravel";
|
|
35
|
+
|
|
36
|
+
export function initializeCraft(options) {
|
|
37
|
+
const { enhanceVue, layouts } = options || {};
|
|
38
|
+
|
|
39
|
+
return createInertiaApp({
|
|
40
|
+
title: (title) => \`\${title} - \${appName}\`,
|
|
41
|
+
resolve: (name) => {
|
|
42
|
+
const pages = import.meta.glob('/resources/js/pages/**/*.vue', {
|
|
43
|
+
eager: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const page = pages[\`/resources/js/pages/\${name}.vue\`];
|
|
47
|
+
|
|
48
|
+
let defaultLayout = undefined;
|
|
49
|
+
|
|
50
|
+
if (layouts) {
|
|
51
|
+
Object.entries(layouts).forEach(([key, value]) => {
|
|
52
|
+
if(name.startsWith(key)) {
|
|
53
|
+
defaultLayout = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if(!defaultLayout && layouts.default) {
|
|
57
|
+
defaultLayout = layouts.default;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if(!page) {
|
|
63
|
+
const errorMessage = \`Page not found: \${name}.vue\`;
|
|
64
|
+
|
|
65
|
+
console.error(\`[Inertia] \${errorMessage}\`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
page.default.layout = defaultLayout;
|
|
69
|
+
return page;
|
|
70
|
+
},
|
|
71
|
+
setup({ el, App, props, plugin }) {
|
|
72
|
+
// Get the language files and transform the paths
|
|
73
|
+
const langGlob = import.meta.glob("/lang/*.json", { eager: true });
|
|
74
|
+
|
|
75
|
+
// Transform absolute paths to relative paths expected by i18n
|
|
76
|
+
const transformedLangs = {};
|
|
77
|
+
Object.entries(langGlob).forEach(([absolutePath, module]) => {
|
|
78
|
+
// Convert "/lang/en.json" to "../../lang/en.json"
|
|
79
|
+
const relativePath = absolutePath.replace('/lang/', '../../lang/');
|
|
80
|
+
transformedLangs[relativePath] = module;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let app = createApp({ render: () => h(TooltipProvider, null, () => h(App, props)) })
|
|
84
|
+
.use(plugin)
|
|
85
|
+
.use(i18n, {
|
|
86
|
+
langs: transformedLangs,
|
|
87
|
+
changeLanguageRoute: '/change-language',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (enhanceVue) {
|
|
91
|
+
app = enhanceVue(app);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
app.mount(el);
|
|
95
|
+
},
|
|
96
|
+
progress: {
|
|
97
|
+
includeCSS: false,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}`;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return plugins;
|
|
106
|
+
}
|
|
@@ -1,135 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
plugins: [
|
|
25
|
-
// These will be dynamically imported by the consuming app
|
|
26
|
-
// We provide the config structure, they provide the actual plugins
|
|
27
|
-
],
|
|
28
|
-
optimizeDeps: {
|
|
29
|
-
exclude: [
|
|
30
|
-
'@hardimpactdev/craft-ui',
|
|
31
|
-
'@tailwindcss/vite',
|
|
32
|
-
'laravel-vue-i18n/vite'
|
|
33
|
-
]
|
|
34
|
-
},
|
|
35
|
-
// Store laravel config for the consuming app to use
|
|
36
|
-
define: {
|
|
37
|
-
__CRAFT_LARAVEL_CONFIG__: JSON.stringify(laravelConfig)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Craft Vite plugin that provides virtual module for app initialization
|
|
44
|
-
*/
|
|
45
|
-
export function craft() {
|
|
46
|
-
return {
|
|
47
|
-
name: 'craft',
|
|
48
|
-
enforce: 'pre' as const,
|
|
49
|
-
|
|
50
|
-
resolveId(id: string) {
|
|
51
|
-
if (id === 'virtual:craft') {
|
|
52
|
-
return id
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
load(id: string) {
|
|
57
|
-
if (id === 'virtual:craft') {
|
|
58
|
-
return `
|
|
59
|
-
import { createInertiaApp } from "@inertiajs/vue3";
|
|
60
|
-
import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers";
|
|
61
|
-
import { createApp, h } from "vue";
|
|
62
|
-
import { i18n } from "@hardimpactdev/craft-ui";
|
|
63
|
-
import { TooltipProvider } from "reka-ui";
|
|
64
|
-
|
|
65
|
-
const appName = import.meta.env.VITE_APP_NAME || "Laravel";
|
|
66
|
-
|
|
67
|
-
export function initializeCraft(options) {
|
|
68
|
-
const { enhanceVue, layouts } = options || {};
|
|
69
|
-
|
|
70
|
-
return createInertiaApp({
|
|
71
|
-
title: (title) => \`\${title} - \${appName}\`,
|
|
72
|
-
resolve: (name) => {
|
|
73
|
-
const pages = import.meta.glob('/resources/js/pages/**/*.vue', {
|
|
74
|
-
eager: true,
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const page = pages[\`/resources/js/pages/\${name}.vue\`];
|
|
78
|
-
|
|
79
|
-
let defaultLayout = undefined;
|
|
80
|
-
|
|
81
|
-
if (layouts) {
|
|
82
|
-
Object.entries(layouts).forEach(([key, value]) => {
|
|
83
|
-
if(name.startsWith(key)) {
|
|
84
|
-
defaultLayout = value;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if(!defaultLayout && layouts.default) {
|
|
88
|
-
defaultLayout = layouts.default;
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if(!page) {
|
|
94
|
-
const errorMessage = \`Page not found: \${name}.vue\`;
|
|
95
|
-
|
|
96
|
-
console.error(\`[Inertia] \${errorMessage}\`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
page.default.layout = defaultLayout;
|
|
100
|
-
return page;
|
|
101
|
-
},
|
|
102
|
-
setup({ el, App, props, plugin }) {
|
|
103
|
-
// Get the language files and transform the paths
|
|
104
|
-
const langGlob = import.meta.glob("/lang/*.json", { eager: true });
|
|
105
|
-
|
|
106
|
-
// Transform absolute paths to relative paths expected by i18n
|
|
107
|
-
const transformedLangs = {};
|
|
108
|
-
Object.entries(langGlob).forEach(([absolutePath, module]) => {
|
|
109
|
-
// Convert "/lang/en.json" to "../../lang/en.json"
|
|
110
|
-
const relativePath = absolutePath.replace('/lang/', '../../lang/');
|
|
111
|
-
transformedLangs[relativePath] = module;
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
let app = createApp({ render: () => h(TooltipProvider, null, () => h(App, props)) })
|
|
115
|
-
.use(plugin)
|
|
116
|
-
.use(i18n, {
|
|
117
|
-
langs: transformedLangs,
|
|
118
|
-
changeLanguageRoute: '/change-language',
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (enhanceVue) {
|
|
122
|
-
app = enhanceVue(app);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
app.mount(el);
|
|
126
|
-
},
|
|
127
|
-
progress: {
|
|
128
|
-
includeCSS: false,
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
}`;
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
}
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import { getPlugins } from './plugins.js';
|
|
3
|
+
import { getServerConfig } from './server.js';
|
|
4
|
+
import { getResolveConfig } from './aliases.js';
|
|
5
|
+
import type { CraftConfigOptions } from './types.js';
|
|
6
|
+
|
|
7
|
+
// Re-export types for consumers
|
|
8
|
+
export type { CraftConfigOptions } from './types.js';
|
|
9
|
+
|
|
10
|
+
// Re-export individual modules for advanced customization
|
|
11
|
+
export { getPlugins } from './plugins.js';
|
|
12
|
+
export { getServerConfig } from './server.js';
|
|
13
|
+
export { getResolveConfig } from './aliases.js';
|
|
14
|
+
|
|
15
|
+
export function defineCraftConfig(options: CraftConfigOptions = {}) {
|
|
16
|
+
return defineConfig(({ mode }) => ({
|
|
17
|
+
plugins: [
|
|
18
|
+
...getPlugins(options),
|
|
19
|
+
...(options.plugins || []),
|
|
20
|
+
],
|
|
21
|
+
resolve: getResolveConfig(options),
|
|
22
|
+
server: getServerConfig(mode),
|
|
23
|
+
}));
|
|
135
24
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import vue from '@vitejs/plugin-vue';
|
|
2
|
+
import laravel from 'laravel-vite-plugin';
|
|
3
|
+
import { run } from 'vite-plugin-run';
|
|
4
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
5
|
+
import i18n from 'laravel-vue-i18n/vite';
|
|
6
|
+
import vueDevTools from 'vite-plugin-vue-devtools';
|
|
7
|
+
import { craft } from './craftPlugin.js';
|
|
8
|
+
import type { CraftConfigOptions } from './types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configure all Vite plugins for the Craft stack
|
|
12
|
+
*
|
|
13
|
+
* Includes:
|
|
14
|
+
* - craft: Virtual module for Inertia app initialization
|
|
15
|
+
* - laravel: Laravel Vite integration
|
|
16
|
+
* - tailwindcss: Tailwind CSS v4
|
|
17
|
+
* - i18n: Laravel Vue i18n
|
|
18
|
+
* - vue: Vue 3 SFC support
|
|
19
|
+
* - vueDevTools: Vue DevTools integration
|
|
20
|
+
* - run: Auto-run artisan commands on file changes (dev only)
|
|
21
|
+
*/
|
|
22
|
+
export function getPlugins(options: CraftConfigOptions) {
|
|
23
|
+
return [
|
|
24
|
+
craft(),
|
|
25
|
+
|
|
26
|
+
laravel({
|
|
27
|
+
input: options.laravel?.input || ['resources/js/app.ts'],
|
|
28
|
+
refresh: options.laravel?.refresh ?? true,
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
tailwindcss(),
|
|
32
|
+
|
|
33
|
+
i18n(),
|
|
34
|
+
|
|
35
|
+
vue({
|
|
36
|
+
template: {
|
|
37
|
+
transformAssetUrls: {
|
|
38
|
+
base: null,
|
|
39
|
+
includeAbsolute: false,
|
|
40
|
+
},
|
|
41
|
+
compilerOptions: {
|
|
42
|
+
isCustomElement: (tag) => tag === 'trix-editor',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
|
|
47
|
+
getArtisanRunners(),
|
|
48
|
+
|
|
49
|
+
vueDevTools({
|
|
50
|
+
appendTo: 'virtual:craft',
|
|
51
|
+
launchEditor: import.meta.env?.VITE_EDITOR || 'cursor',
|
|
52
|
+
}),
|
|
53
|
+
].filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Configure artisan command runners for development
|
|
58
|
+
*
|
|
59
|
+
* Auto-runs:
|
|
60
|
+
* - waymaker:generate on controller changes
|
|
61
|
+
* - wayfinder:generate on route/controller changes
|
|
62
|
+
* - typescript:transform on DTO/Enum changes
|
|
63
|
+
*/
|
|
64
|
+
function getArtisanRunners() {
|
|
65
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return run([
|
|
70
|
+
{
|
|
71
|
+
name: 'waymaker',
|
|
72
|
+
run: ['php', 'artisan', 'waymaker:generate'],
|
|
73
|
+
pattern: ['app/**/Http/**/*.php'],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'wayfinder',
|
|
77
|
+
run: ['php', 'artisan', 'wayfinder:generate'],
|
|
78
|
+
pattern: ['routes/*.php', 'app/**/Http/**/*.php'],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'typescript',
|
|
82
|
+
run: ['php', 'artisan', 'typescript:transform'],
|
|
83
|
+
pattern: ['app/{Data,Enums}/**/*.php'],
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { loadEnv } from 'vite';
|
|
2
|
+
import type { ServerOptions } from 'vite';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Configure Vite dev server for Laravel apps behind a reverse proxy (Caddy/nginx)
|
|
6
|
+
*
|
|
7
|
+
* Handles:
|
|
8
|
+
* - HMR WebSocket connection through the proxy (wss://app.test:443)
|
|
9
|
+
* - Correct origin for CORS and asset URLs
|
|
10
|
+
* - Binding to all interfaces for proxy access
|
|
11
|
+
*/
|
|
12
|
+
export function getServerConfig(mode: string): ServerOptions {
|
|
13
|
+
const env = loadEnv(mode, process.cwd());
|
|
14
|
+
const appUrl = env.VITE_APP_URL;
|
|
15
|
+
|
|
16
|
+
if (!appUrl) {
|
|
17
|
+
return { host: '0.0.0.0' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(appUrl);
|
|
22
|
+
const isHttps = url.protocol === 'https:';
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
// Accept connections from reverse proxy
|
|
26
|
+
host: '0.0.0.0',
|
|
27
|
+
|
|
28
|
+
// Tell Vite the public origin for asset URLs
|
|
29
|
+
origin: appUrl,
|
|
30
|
+
|
|
31
|
+
// Configure HMR to connect through the reverse proxy
|
|
32
|
+
// Without this, browser would try localhost:5173 directly
|
|
33
|
+
hmr: {
|
|
34
|
+
host: url.hostname,
|
|
35
|
+
protocol: isHttps ? 'wss' : 'ws',
|
|
36
|
+
clientPort: isHttps ? 443 : (parseInt(url.port) || 80),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
} catch {
|
|
40
|
+
return { host: '0.0.0.0' };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Alias } from 'vite';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for defineCraftConfig
|
|
5
|
+
*/
|
|
6
|
+
export interface CraftConfigOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Laravel Vite plugin options
|
|
9
|
+
*/
|
|
10
|
+
laravel?: {
|
|
11
|
+
/** Entry points for the application */
|
|
12
|
+
input?: string[];
|
|
13
|
+
/** Enable file refresh on changes */
|
|
14
|
+
refresh?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Additional Vite plugins to include
|
|
19
|
+
*/
|
|
20
|
+
plugins?: any[];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Additional Vite aliases
|
|
24
|
+
*/
|
|
25
|
+
aliases?: Alias[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Local UI library configuration for development with symlinked packages
|
|
29
|
+
*/
|
|
30
|
+
ui?: LocalUiOptions;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for local UI library development
|
|
35
|
+
*
|
|
36
|
+
* When developing craft-ui alongside an app, this enables HMR by:
|
|
37
|
+
* 1. Redirecting package imports to source files instead of dist/
|
|
38
|
+
* 2. Resolving conflicting aliases (like @/) based on importer location
|
|
39
|
+
*/
|
|
40
|
+
export interface LocalUiOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Relative path from app root to the symlinked craft-ui directory
|
|
43
|
+
* @example '../craft-ui'
|
|
44
|
+
*/
|
|
45
|
+
localPath?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Internal alias configuration for local package resolution
|
|
50
|
+
*/
|
|
51
|
+
export interface LocalAliasConfig {
|
|
52
|
+
/** Regex pattern to match imports */
|
|
53
|
+
pattern: RegExp;
|
|
54
|
+
|
|
55
|
+
/** The replacement path or alias */
|
|
56
|
+
replacement: string;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Absolute path to the library (for importer detection)
|
|
60
|
+
* If set along with basePaths, enables context-aware resolution
|
|
61
|
+
*/
|
|
62
|
+
libraryPath?: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Base paths for context-aware resolution
|
|
66
|
+
*/
|
|
67
|
+
basePaths?: {
|
|
68
|
+
/** Path to use when import is from the app */
|
|
69
|
+
app: string;
|
|
70
|
+
/** Path to use when import is from the library */
|
|
71
|
+
library: string;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -46,12 +46,17 @@
|
|
|
46
46
|
"import": "./dist/craft-ui.css",
|
|
47
47
|
"default": "./dist/craft-ui.css"
|
|
48
48
|
},
|
|
49
|
+
"./dist/craft-ui.css": {
|
|
50
|
+
"style": "./dist/craft-ui.css",
|
|
51
|
+
"import": "./dist/craft-ui.css",
|
|
52
|
+
"default": "./dist/craft-ui.css"
|
|
53
|
+
},
|
|
49
54
|
"./vite": {
|
|
50
55
|
"import": "./dist/vite/defineCraftConfig.js",
|
|
51
56
|
"types": "./dist/src/vite/defineCraftConfig.d.ts"
|
|
52
57
|
}
|
|
53
58
|
},
|
|
54
|
-
"version": "0.0.
|
|
59
|
+
"version": "0.0.7",
|
|
55
60
|
"type": "module",
|
|
56
61
|
"scripts": {
|
|
57
62
|
"dev": "vite",
|
|
@@ -71,6 +76,7 @@
|
|
|
71
76
|
"@inertiajs/core": "^2.3.6",
|
|
72
77
|
"@inertiajs/vue3": "^2.0.0",
|
|
73
78
|
"@tailwindcss/vite": "^4.0.0",
|
|
79
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
74
80
|
"@tanstack/vue-table": "^8.21.3",
|
|
75
81
|
"@types/node": "^25.0.3",
|
|
76
82
|
"@unovis/ts": "^1.6.2",
|
|
@@ -91,6 +97,7 @@
|
|
|
91
97
|
"tw-animate-css": "^1.4.0",
|
|
92
98
|
"vaul-vue": "^0.4.1",
|
|
93
99
|
"vee-validate": "^4.15.1",
|
|
100
|
+
"vite-plugin-run": "^0.6.1",
|
|
94
101
|
"vite-plugin-vue-devtools": "^8.0.5",
|
|
95
102
|
"vue": "^3.5.0",
|
|
96
103
|
"vue-chartjs": "^5.3.3",
|
|
@@ -104,7 +111,6 @@
|
|
|
104
111
|
"@storybook/addon-themes": "^10.1.11",
|
|
105
112
|
"@storybook/addon-vitest": "10.1.11",
|
|
106
113
|
"@storybook/vue3-vite": "^10.1.11",
|
|
107
|
-
"@vitejs/plugin-vue": "^6.0.3",
|
|
108
114
|
"@vitest/browser": "^4.0.16",
|
|
109
115
|
"@vitest/browser-playwright": "^4.0.16",
|
|
110
116
|
"@vitest/coverage-v8": "^4.0.16",
|
|
@@ -116,7 +122,6 @@
|
|
|
116
122
|
"typescript": "~5.9.3",
|
|
117
123
|
"vite": "^7.3.0",
|
|
118
124
|
"vite-plugin-dts": "^4.5.4",
|
|
119
|
-
"vite-plugin-run": "^0.6.1",
|
|
120
125
|
"vitest": "^4.0.16",
|
|
121
126
|
"vue-draggable-plus": "^0.6.0",
|
|
122
127
|
"vue-router": "^4.6.4",
|
|
@@ -125,9 +130,11 @@
|
|
|
125
130
|
"peerDependencies": {
|
|
126
131
|
"@inertiajs/vue3": "^2.0.0",
|
|
127
132
|
"@tailwindcss/vite": "^4.0.0",
|
|
133
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
128
134
|
"laravel-vite-plugin": "^2.0.1",
|
|
129
135
|
"laravel-vue-i18n": "^2.8.0",
|
|
130
|
-
"vite-plugin-run": "^0.6.
|
|
136
|
+
"vite-plugin-run": "^0.6.0",
|
|
137
|
+
"vite-plugin-vue-devtools": "^8.0.0",
|
|
131
138
|
"vue": "^3.5.0"
|
|
132
139
|
}
|
|
133
140
|
}
|