@hardimpactdev/craft-ui 0.0.5 → 0.0.6

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.
@@ -0,0 +1,4 @@
1
+ export declare function craft(): {
2
+ name: string;
3
+ enforce: string;
4
+ }[];
@@ -1,26 +1,21 @@
1
- import { UserConfig } from 'vite';
2
- interface CraftConfigOptions {
1
+ interface viteConfigOptions {
3
2
  laravel?: {
4
- input: string | string[];
5
- publicDirectory?: string;
6
- buildDirectory?: string;
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;
3
+ input?: string[];
4
+ refresh?: boolean;
5
+ };
6
+ plugins?: any[];
7
+ aliases?: any[];
8
+ ui?: {
9
+ localPath?: string;
14
10
  };
15
11
  }
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(): {
12
+ export declare function defineCraftConfig(options?: viteConfigOptions): import('vite').UserConfigFnObject;
13
+ export declare function pluginConfig(options: viteConfigOptions): ({
21
14
  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;
15
+ enforce: string;
16
+ }[] | import('vite').PluginOption)[];
17
+ export declare function aliasConfig(aliases?: any[], ui?: any): {
18
+ dedupe: string[];
19
+ alias: any[];
25
20
  };
26
21
  export {};
@@ -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,166 @@
1
+ import { defineConfig, loadEnv } from "vite";
2
+ import vue from "@vitejs/plugin-vue";
3
+ import path from "path";
4
+ import laravel from "laravel-vite-plugin";
5
+ import { run } from "vite-plugin-run";
6
+ import { craft } from "./craftPlugin.js";
7
+ import tailwindcss from "@tailwindcss/vite";
8
+ import i18n from "laravel-vue-i18n/vite";
9
+ import vueDevTools from "vite-plugin-vue-devtools";
1
10
  function defineCraftConfig(options = {}) {
2
- const laravelConfig = options.laravel ?? {
3
- input: ["resources/js/app.ts"]
4
- };
5
- return {
6
- plugins: [
7
- // These will be dynamically imported by the consuming app
8
- // We provide the config structure, they provide the actual plugins
9
- ],
10
- optimizeDeps: {
11
- exclude: [
12
- "@hardimpactdev/craft-ui",
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
- };
11
+ return defineConfig(({ mode }) => {
12
+ const serverConfig = getServerConfig(mode);
13
+ return {
14
+ plugins: [
15
+ ...pluginConfig(options),
16
+ ...options.plugins || [],
17
+ // Plugin to enforce allowedHosts after laravel-vite-plugin
18
+ // This bypasses Vite's DNS rebinding protection which conflicts with reverse proxy setups
19
+ {
20
+ name: "craft-allowed-hosts",
21
+ enforce: "post",
22
+ config() {
23
+ return {
24
+ server: {
25
+ allowedHosts: true
26
+ }
27
+ };
28
+ }
29
+ }
30
+ ],
31
+ resolve: {
32
+ ...aliasConfig(options.aliases, options.ui)
33
+ },
34
+ server: serverConfig
35
+ };
36
+ });
22
37
  }
23
- function craft() {
24
- return {
25
- name: "craft",
26
- enforce: "pre",
27
- resolveId(id) {
28
- if (id === "virtual:craft") {
29
- return id;
38
+ function getServerConfig(mode) {
39
+ const env = loadEnv(mode, process.cwd());
40
+ const appUrl = env.VITE_APP_URL;
41
+ if (!appUrl) {
42
+ return { host: "0.0.0.0" };
43
+ }
44
+ try {
45
+ const url = new URL(appUrl);
46
+ const isHttps = url.protocol === "https:";
47
+ return {
48
+ host: "0.0.0.0",
49
+ origin: appUrl,
50
+ allowedHosts: true,
51
+ hmr: {
52
+ host: url.hostname,
53
+ protocol: isHttps ? "wss" : "ws",
54
+ clientPort: isHttps ? 443 : parseInt(url.port) || 80
30
55
  }
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
- }`;
56
+ };
57
+ } catch {
58
+ return { host: "0.0.0.0" };
59
+ }
60
+ }
61
+ function pluginConfig(options) {
62
+ return [
63
+ craft(),
64
+ laravel({
65
+ input: options.laravel?.input || ["resources/js/app.ts"],
66
+ refresh: options.laravel?.refresh || true
67
+ }),
68
+ tailwindcss(),
69
+ i18n(),
70
+ vue({
71
+ template: {
72
+ transformAssetUrls: {
73
+ base: null,
74
+ includeAbsolute: false
75
+ },
76
+ compilerOptions: {
77
+ isCustomElement: (tag) => tag === "trix-editor"
78
+ }
108
79
  }
80
+ }),
81
+ runConfiguration(),
82
+ vueDevTools({
83
+ appendTo: "virtual:craft",
84
+ launchEditor: import.meta.env?.VITE_EDITOR || "cursor"
85
+ })
86
+ ].filter(Boolean);
87
+ }
88
+ function runConfiguration() {
89
+ if (false) {
90
+ return null;
91
+ }
92
+ return run([
93
+ {
94
+ name: "waymaker",
95
+ run: ["php", "artisan", "waymaker:generate"],
96
+ pattern: ["app/**/Http/**/*.php"]
97
+ },
98
+ {
99
+ name: "wayfinder",
100
+ run: ["php", "artisan", "wayfinder:generate"],
101
+ pattern: ["routes/*.php", "app/**/Http/**/*.php"]
102
+ },
103
+ {
104
+ name: "typescript",
105
+ run: ["php", "artisan", "typescript:transform"],
106
+ pattern: ["app/{Data,Enums}/**/*.php"]
109
107
  }
108
+ ]);
109
+ }
110
+ function aliasConfig(aliases = [], ui = {}) {
111
+ if (!process.argv.includes("build") && ui.localPath) {
112
+ const absoluteLibraryPath = path.resolve(process.cwd(), ui.localPath);
113
+ const localAliases = [
114
+ // The following alias will make the @ alias work correctly depending on the importer path.
115
+ {
116
+ regex: /^@\//,
117
+ replacement: "@/",
118
+ libraryPath: absoluteLibraryPath,
119
+ aliasLocalBasePath: "./resources/js",
120
+ aliasExternalBasePath: path.join(ui.localPath, "src")
121
+ },
122
+ // The following alias config will change all package references like import { Dialog } from '@hardimpactdev/craft-ui';
123
+ // to the library's index.ts file instead.
124
+ {
125
+ regex: /^@hardimpactdev\/craft-ui/,
126
+ replacement: path.join(ui.localPath, "index.ts")
127
+ }
128
+ ];
129
+ aliases.push(...aliasLocalPackage(localAliases));
130
+ }
131
+ return {
132
+ dedupe: ["@inertiajs/vue3", "@tailwindcss/vite"],
133
+ alias: aliases
110
134
  };
111
135
  }
136
+ function aliasLocalPackage(aliases) {
137
+ return aliases.map((alias) => {
138
+ if (!alias.libraryPath && !alias.aliasLocalBasePath && !alias.aliasExternalBasePath) {
139
+ return {
140
+ find: alias.regex,
141
+ replacement: path.resolve(process.cwd(), alias.replacement)
142
+ };
143
+ }
144
+ return {
145
+ find: alias.regex,
146
+ replacement: alias.replacement,
147
+ async customResolver(source, importer) {
148
+ let resolvedPath = "";
149
+ resolvedPath = path.resolve(
150
+ // get the directory name of the importer
151
+ process.cwd(),
152
+ // if the importer string includes the folder name, use the external path, otherwise use the local path
153
+ importer?.includes(alias.libraryPath) ? alias.aliasExternalBasePath : alias.aliasLocalBasePath,
154
+ // remove the alias replacement from the source path
155
+ source.replace(alias.replacement, "")
156
+ );
157
+ return (await this.resolve(resolvedPath))?.id;
158
+ }
159
+ };
160
+ });
161
+ }
112
162
  export {
113
- craft,
114
- defineCraftConfig
163
+ aliasConfig,
164
+ defineCraftConfig,
165
+ pluginConfig
115
166
  };
@@ -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,213 @@
1
- import type { UserConfig } from 'vite'
2
-
3
- interface CraftConfigOptions {
4
- laravel?: {
5
- input: string | string[]
6
- publicDirectory?: string
7
- buildDirectory?: string
8
- ssr?: string | string[]
9
- ssrOutputDirectory?: string
10
- refresh?: boolean | string | string[]
11
- hotFile?: string
12
- detectTls?: string | boolean
13
- valetTls?: string | boolean
14
- transformOnServe?: (code: string, url: string) => string
15
- }
1
+ import { defineConfig, loadEnv } from 'vite';
2
+ import vue from '@vitejs/plugin-vue';
3
+ import path from 'path';
4
+ import laravel from 'laravel-vite-plugin';
5
+ import { run } from 'vite-plugin-run';
6
+ import { craft } from './craftPlugin.js';
7
+ import tailwindcss from '@tailwindcss/vite';
8
+ import i18n from 'laravel-vue-i18n/vite';
9
+ import vueDevTools from 'vite-plugin-vue-devtools';
10
+
11
+ interface viteConfigOptions {
12
+ laravel?: {
13
+ input?: string[];
14
+ refresh?: boolean;
15
+ };
16
+ plugins?: any[];
17
+ aliases?: any[];
18
+ ui?: {
19
+ localPath?: string;
20
+ };
16
21
  }
17
22
 
18
- export function defineCraftConfig(options: CraftConfigOptions = {}): UserConfig {
19
- const laravelConfig = options.laravel ?? {
20
- input: ['resources/js/app.ts'],
21
- }
22
-
23
- return {
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)
23
+ export function defineCraftConfig(options: viteConfigOptions = {}) {
24
+ return defineConfig(({ mode }) => {
25
+ const serverConfig = getServerConfig(mode);
26
+ return {
27
+ plugins: [
28
+ ...pluginConfig(options),
29
+ ...(options.plugins || []),
30
+ // Plugin to enforce allowedHosts after laravel-vite-plugin
31
+ // This bypasses Vite's DNS rebinding protection which conflicts with reverse proxy setups
32
+ {
33
+ name: 'craft-allowed-hosts',
34
+ enforce: 'post' as const,
35
+ config() {
36
+ return {
37
+ server: {
38
+ allowedHosts: true as const,
39
+ },
40
+ };
41
+ },
42
+ },
43
+ ],
44
+ resolve: {
45
+ ...aliasConfig(options.aliases, options.ui),
46
+ },
47
+ server: serverConfig,
48
+ };
49
+ });
50
+ }
51
+
52
+ function getServerConfig(mode: string) {
53
+ // Only load VITE_* prefixed env vars (Vite's default secure behavior)
54
+ const env = loadEnv(mode, process.cwd());
55
+ const appUrl = env.VITE_APP_URL;
56
+
57
+ if (!appUrl) {
58
+ return { host: '0.0.0.0' };
59
+ }
60
+
61
+ try {
62
+ const url = new URL(appUrl);
63
+ const isHttps = url.protocol === 'https:';
64
+
65
+ return {
66
+ host: '0.0.0.0',
67
+ origin: appUrl,
68
+ allowedHosts: true as const,
69
+ hmr: {
70
+ host: url.hostname,
71
+ protocol: isHttps ? 'wss' : 'ws',
72
+ clientPort: isHttps ? 443 : (parseInt(url.port) || 80),
73
+ },
74
+ };
75
+ } catch {
76
+ return { host: '0.0.0.0' };
77
+ }
78
+ }
79
+
80
+ export function pluginConfig(options: viteConfigOptions) {
81
+ return [
82
+ craft(),
83
+ laravel({
84
+ input: options.laravel?.input || ['resources/js/app.ts'],
85
+ refresh: options.laravel?.refresh || true,
86
+ }),
87
+ tailwindcss(),
88
+ i18n(),
89
+ vue({
90
+ template: {
91
+ transformAssetUrls: {
92
+ base: null,
93
+ includeAbsolute: false,
94
+ },
95
+ compilerOptions: {
96
+ isCustomElement: (tag) => tag === 'trix-editor',
97
+ },
98
+ },
99
+ }),
100
+ runConfiguration(),
101
+ vueDevTools({
102
+ appendTo: 'virtual:craft',
103
+ launchEditor: import.meta.env?.VITE_EDITOR || 'cursor',
104
+ }),
105
+ ].filter(Boolean);
106
+ }
107
+
108
+ function runConfiguration() {
109
+ if (process.env.NODE_ENV !== 'development') {
110
+ return null;
38
111
  }
39
- }
112
+
113
+ return run([
114
+ {
115
+ name: 'waymaker',
116
+ run: ['php', 'artisan', 'waymaker:generate'],
117
+ pattern: ['app/**/Http/**/*.php'],
118
+ },
119
+ {
120
+ name: 'wayfinder',
121
+ run: ['php', 'artisan', 'wayfinder:generate'],
122
+ pattern: ['routes/*.php', 'app/**/Http/**/*.php'],
123
+ },
124
+ {
125
+ name: 'typescript',
126
+ run: ['php', 'artisan', 'typescript:transform'],
127
+ pattern: ['app/{Data,Enums}/**/*.php'],
128
+ },
129
+ ]);
130
+ }
131
+
132
+ interface LocalAlias {
133
+ regex: RegExp;
134
+ replacement: string;
135
+ libraryPath?: string;
136
+ aliasLocalBasePath?: string;
137
+ aliasExternalBasePath?: string;
138
+ }
139
+
140
+ export function aliasConfig(aliases: any[] = [], ui: any = {}) {
141
+ if (!process.argv.includes('build') && ui.localPath) {
142
+
143
+ const absoluteLibraryPath = path.resolve(process.cwd(), ui.localPath);
144
+
145
+ const localAliases = [
146
+ // The following alias will make the @ alias work correctly depending on the importer path.
147
+ {
148
+ regex: /^@\//,
149
+ replacement: "@/",
150
+ libraryPath: absoluteLibraryPath,
151
+ aliasLocalBasePath: "./resources/js",
152
+ aliasExternalBasePath: path.join(ui.localPath, "src"),
153
+ },
154
+ // The following alias config will change all package references like import { Dialog } from '@hardimpactdev/craft-ui';
155
+ // to the library's index.ts file instead.
156
+ {
157
+ regex: /^@hardimpactdev\/craft-ui/,
158
+ replacement: path.join(ui.localPath, "index.ts"),
159
+ },
160
+ ];
161
+
162
+ aliases.push(...aliasLocalPackage(localAliases as LocalAlias[]));
163
+ }
164
+
165
+ return {
166
+ dedupe: ['@inertiajs/vue3', '@tailwindcss/vite'],
167
+ alias: aliases,
168
+ };
169
+ }
170
+
171
+ interface CustomResolverContext {
172
+ resolve: (id: string, importer?: string) => Promise<{ id: string } | null>;
40
173
  }
41
174
 
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
- }
175
+ function aliasLocalPackage(aliases: Array<LocalAlias>) {
176
+
177
+ return aliases.map((alias) => {
178
+
179
+ // if the alias has no external path, folder name, or local path, use the replacement as the path
180
+ // For example library references like import { Dialog } from '@hardimpactdev/craft-ui'; will be resolved to the library's index.ts file instead.
181
+ // This will make HMR work correctly. Allowing to update the library and see the changes in the target project without hard page reloads.
182
+ if (!alias.libraryPath && !alias.aliasLocalBasePath && !alias.aliasExternalBasePath) {
183
+ return {
184
+ find: alias.regex,
185
+ replacement: path.resolve(process.cwd(), alias.replacement),
186
+ };
187
+ }
188
+
189
+ // When using an alias both in a library and the target project, we need to resolve the alias correctly based on the importer path.
190
+ // This allows for example to use the @ alias both in the library and the target project.
191
+ return {
192
+ find: alias.regex,
193
+ replacement: alias.replacement,
194
+ async customResolver(this: CustomResolverContext, source: any, importer: any) {
195
+ let resolvedPath = '';
196
+
197
+ resolvedPath = path.resolve(
198
+ // get the directory name of the importer
199
+ process.cwd(),
200
+
201
+ // if the importer string includes the folder name, use the external path, otherwise use the local path
202
+ importer?.includes(alias.libraryPath) ? alias.aliasExternalBasePath! : alias.aliasLocalBasePath!,
203
+
204
+ // remove the alias replacement from the source path
205
+ source.replace(alias.replacement, ''),
206
+ );
207
+
208
+ // use Vite's (in fact, rollup's) resolution function
209
+ return (await this.resolve(resolvedPath))?.id;
210
+ },
211
+ };
212
+ });
135
213
  }
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.5",
59
+ "version": "0.0.6",
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.1",
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
  }