vite-mix 0.7.3
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/LICENSE +15 -0
- package/README.md +202 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +342 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hugo Rodrigues
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# vite-mix
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/vite-mix)
|
|
4
|
+
[](https://www.npmjs.com/package/vite-mix)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/hugoboss17/vite-mix/actions/workflows/ci.yml)
|
|
7
|
+
[](https://codecov.io/gh/hugoboss17/vite-mix)
|
|
8
|
+
[](https://scorecard.dev/viewer/?uri=github.com/hugoboss17/vite-mix)
|
|
9
|
+
[](https://www.bestpractices.dev/projects/12098)
|
|
10
|
+
[](https://slsa.dev)
|
|
11
|
+
[](https://socket.dev/npm/package/vite-mix)
|
|
12
|
+
|
|
13
|
+
Use Vite with a Laravel Mix-like API.
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
|
|
17
|
+
Laravel Mix is no longer maintained. This package helps migrate legacy Mix-style build definitions to Vite with minimal frontend structure changes.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install vite-mix
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Peer dependencies (install only what you need):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev vite # required
|
|
29
|
+
npm install --save-dev sass # if using .scss/.sass
|
|
30
|
+
npm install --save-dev @vitejs/plugin-vue # if using Vue 3
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
Replace your `webpack.mix.js` with `vite.config.js` with a Mix-style definition:
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// vite.config.js
|
|
39
|
+
import { defineConfig } from "vite";
|
|
40
|
+
import { mix, viteConfigFromGraph } from "vite-mix";
|
|
41
|
+
|
|
42
|
+
const graph = mix()
|
|
43
|
+
.setPublicPath("public")
|
|
44
|
+
.js("resources/assets/js/app.js", "public/js")
|
|
45
|
+
.vue()
|
|
46
|
+
.sass("resources/assets/sass/app.scss", "public/css")
|
|
47
|
+
.css("resources/assets/css/simple.css", "public/css")
|
|
48
|
+
.copy("resources/assets/images/logo.png", "public/images/logo.png")
|
|
49
|
+
.copyDirectory("resources/assets/fonts", "public/fonts")
|
|
50
|
+
.autoload({ jquery: ["$", "jQuery", "window.jQuery"] })
|
|
51
|
+
.toGraph();
|
|
52
|
+
|
|
53
|
+
export default defineConfig(
|
|
54
|
+
async ({ mode }) => await viteConfigFromGraph(graph, mode),
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If you need to set the mode manually (e.g. outside Vite):
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
const mode =
|
|
62
|
+
process.env.NODE_ENV === "production" ? "production" : "development";
|
|
63
|
+
await viteConfigFromGraph(graph, mode);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Add scripts to your `package.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"scripts": {
|
|
71
|
+
"dev": "vite",
|
|
72
|
+
"build": "vite build",
|
|
73
|
+
"watch": "vite build --watch"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then run:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run build # production
|
|
82
|
+
npm run watch # watch mode
|
|
83
|
+
npm run dev # dev server
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Supported API
|
|
87
|
+
|
|
88
|
+
| Method | Description |
|
|
89
|
+
| ---------------------------------- | ----------------------------------------------------------------- |
|
|
90
|
+
| `mix()` | Create a new Mix instance |
|
|
91
|
+
| `.setPublicPath(path)` | Output directory (default: `"public"`) |
|
|
92
|
+
| `.js(src, dest)` | JavaScript/TypeScript entry point |
|
|
93
|
+
| `.vue()` | Enable Vue 3 |
|
|
94
|
+
| `.sass(src, dest)` | Sass/SCSS entry point |
|
|
95
|
+
| `.css(src, dest)` | Plain CSS entry point |
|
|
96
|
+
| `.copy(src, dest)` | Copy a file to the output directory |
|
|
97
|
+
| `.copyDirectory(src, dest)` | Copy a directory to the output directory |
|
|
98
|
+
| `.autoload(map)` | Inject globals (e.g. jQuery, Lodash) |
|
|
99
|
+
| `.toGraph()` | Returns the build graph (pass to `viteConfigFromGraph`) |
|
|
100
|
+
| `viteConfigFromGraph(graph, mode)` | Returns a Vite `InlineConfig` (`mode` from Vite's `defineConfig`) |
|
|
101
|
+
|
|
102
|
+
## Examples
|
|
103
|
+
|
|
104
|
+
### Basic JavaScript
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
import { defineConfig } from "vite";
|
|
108
|
+
import { mix, viteConfigFromGraph } from "vite-mix";
|
|
109
|
+
|
|
110
|
+
const graph = mix()
|
|
111
|
+
.setPublicPath("public")
|
|
112
|
+
.js("resources/assets/js/app.js", "public/js")
|
|
113
|
+
.toGraph();
|
|
114
|
+
|
|
115
|
+
export default defineConfig(
|
|
116
|
+
async ({ mode }) => await viteConfigFromGraph(graph, mode),
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Sass / CSS
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
mix()
|
|
124
|
+
.setPublicPath("public")
|
|
125
|
+
.sass("resources/assets/sass/app.scss", "public/css")
|
|
126
|
+
.css("resources/assets/css/vendor.css", "public/css")
|
|
127
|
+
.toGraph();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Vue 3
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
mix()
|
|
134
|
+
.setPublicPath("public")
|
|
135
|
+
.js("resources/assets/js/app.js", "public/js")
|
|
136
|
+
.js("resources/assets/js/admin.js", "public/js")
|
|
137
|
+
.vue()
|
|
138
|
+
.toGraph();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
only the second JS import uses Vue
|
|
142
|
+
|
|
143
|
+
### Autoloading
|
|
144
|
+
|
|
145
|
+
Works with any library, not just jQuery:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
mix()
|
|
149
|
+
.setPublicPath("public")
|
|
150
|
+
.autoload({
|
|
151
|
+
jquery: ["$", "jQuery", "window.jQuery"],
|
|
152
|
+
lodash: ["_"],
|
|
153
|
+
})
|
|
154
|
+
.toGraph();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Static Assets
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
mix()
|
|
161
|
+
.setPublicPath("public")
|
|
162
|
+
.copy("resources/assets/images/logo.png", "public/images/logo.png")
|
|
163
|
+
.copyDirectory("resources/assets/fonts", "public/fonts")
|
|
164
|
+
.toGraph();
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Full Example
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
// vite.config.js
|
|
171
|
+
import { defineConfig } from "vite";
|
|
172
|
+
import { mix, viteConfigFromGraph } from "vite-mix";
|
|
173
|
+
|
|
174
|
+
const graph = mix()
|
|
175
|
+
.setPublicPath("public")
|
|
176
|
+
.js("resources/assets/js/app.js", "public/js")
|
|
177
|
+
.vue()
|
|
178
|
+
.sass("resources/assets/sass/app.scss", "public/css")
|
|
179
|
+
.css("resources/assets/css/vendor.css", "public/css")
|
|
180
|
+
.autoload({
|
|
181
|
+
jquery: ["$", "jQuery", "window.jQuery"],
|
|
182
|
+
lodash: ["_"],
|
|
183
|
+
})
|
|
184
|
+
.copy("resources/assets/images/logo.png", "public/images/logo.png")
|
|
185
|
+
.copyDirectory("resources/assets/fonts", "public/fonts")
|
|
186
|
+
.toGraph();
|
|
187
|
+
|
|
188
|
+
export default defineConfig(
|
|
189
|
+
async ({ mode }) => await viteConfigFromGraph(graph, mode),
|
|
190
|
+
);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Webpack compatibility handled
|
|
194
|
+
|
|
195
|
+
- Sass `~` imports — `@import "~bootstrap-sass/assets/stylesheets/bootstrap"`
|
|
196
|
+
- Extensionless Vue imports — `import Component from "../Component"`
|
|
197
|
+
- Directory Vue imports resolving to `index.vue`
|
|
198
|
+
- Legacy bare asset paths in templates resolving from `resources/assets`
|
|
199
|
+
|
|
200
|
+
## Contributing
|
|
201
|
+
|
|
202
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { InlineConfig } from 'vite';
|
|
2
|
+
|
|
3
|
+
declare function viteConfigFromGraph(graph: MixGraph, mode: string): Promise<InlineConfig>;
|
|
4
|
+
|
|
5
|
+
type CopyTarget = {
|
|
6
|
+
src: string;
|
|
7
|
+
dest: string;
|
|
8
|
+
rename?: string;
|
|
9
|
+
};
|
|
10
|
+
type CopyDirTarget = {
|
|
11
|
+
src: string;
|
|
12
|
+
dest: string;
|
|
13
|
+
};
|
|
14
|
+
type CssEntry = {
|
|
15
|
+
src: string;
|
|
16
|
+
destDir: string;
|
|
17
|
+
};
|
|
18
|
+
type JsEntry = {
|
|
19
|
+
src: string;
|
|
20
|
+
destDirOrFile: string;
|
|
21
|
+
};
|
|
22
|
+
type MixGraph = {
|
|
23
|
+
publicPath: string;
|
|
24
|
+
js: JsEntry[];
|
|
25
|
+
css: CssEntry[];
|
|
26
|
+
sass: Array<{
|
|
27
|
+
src: string;
|
|
28
|
+
destDir: string;
|
|
29
|
+
}>;
|
|
30
|
+
copies: CopyTarget[];
|
|
31
|
+
copyDirs: CopyDirTarget[];
|
|
32
|
+
autoload: Record<string, string[]>;
|
|
33
|
+
vue: boolean;
|
|
34
|
+
};
|
|
35
|
+
declare class Mix {
|
|
36
|
+
private graph;
|
|
37
|
+
constructor();
|
|
38
|
+
setPublicPath(p: string): this;
|
|
39
|
+
js(src: string, dest: string): this;
|
|
40
|
+
vue(): this;
|
|
41
|
+
sass(src: string, dest: string): this;
|
|
42
|
+
css(src: string, destDir: string): this;
|
|
43
|
+
copy(src: string, dest: string): this;
|
|
44
|
+
copyDirectory(src: string, dest: string): this;
|
|
45
|
+
autoload(map: Record<string, string[]>): this;
|
|
46
|
+
toGraph(): MixGraph;
|
|
47
|
+
}
|
|
48
|
+
declare function mix(): Mix;
|
|
49
|
+
|
|
50
|
+
export { type CopyDirTarget, type CopyTarget, type CssEntry, type JsEntry, Mix, type MixGraph, mix, viteConfigFromGraph };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import path2 from "path";
|
|
3
|
+
|
|
4
|
+
// src/driver-vite.ts
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import vue from "@vitejs/plugin-vue";
|
|
8
|
+
function relKeyFromResources(file) {
|
|
9
|
+
return file.replace(/^resources\/assets\/(js|css|sass)\//, "").replace(/\.(js|css|scss|sass)$/, "");
|
|
10
|
+
}
|
|
11
|
+
function ensurePosix(p) {
|
|
12
|
+
return p.split(path.sep).join("/");
|
|
13
|
+
}
|
|
14
|
+
function escapeRegExp(s) {
|
|
15
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16
|
+
}
|
|
17
|
+
var VALID_IDENT = /^[a-zA-Z_$][\w$]*(\.[a-zA-Z_$][\w$]*)*$/;
|
|
18
|
+
var VALID_MODULE = /^[@a-zA-Z][\w./@-]*$/;
|
|
19
|
+
function hasFile(p) {
|
|
20
|
+
try {
|
|
21
|
+
return fs.statSync(p).isFile();
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function hasDir(p) {
|
|
27
|
+
try {
|
|
28
|
+
return fs.statSync(p).isDirectory();
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function isBareAssetImport(source) {
|
|
34
|
+
if (source.startsWith(".") || source.startsWith("/") || source.startsWith("@")) return false;
|
|
35
|
+
if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(source)) return false;
|
|
36
|
+
const sourcePath = source.split("?")[0].split("#")[0];
|
|
37
|
+
if (!sourcePath.includes("/")) return false;
|
|
38
|
+
return /\.(png|jpe?g|gif|svg|webp|avif|ico|bmp|tiff|woff2?|eot|ttf|otf)$/i.test(sourcePath);
|
|
39
|
+
}
|
|
40
|
+
function webpackCompatResolvePlugin() {
|
|
41
|
+
return {
|
|
42
|
+
name: "mix-webpack-compat-resolve",
|
|
43
|
+
enforce: "pre",
|
|
44
|
+
resolveId(source, importer) {
|
|
45
|
+
if (!importer) return null;
|
|
46
|
+
const isRelative = source.startsWith(".");
|
|
47
|
+
if (isRelative && !path.extname(source)) {
|
|
48
|
+
const importerPath = importer.split("?")[0];
|
|
49
|
+
const baseDir = path.dirname(importerPath);
|
|
50
|
+
const abs = path.resolve(baseDir, source);
|
|
51
|
+
const vueSibling = `${abs}.vue`;
|
|
52
|
+
if (hasFile(vueSibling)) return vueSibling;
|
|
53
|
+
if (hasDir(abs)) {
|
|
54
|
+
const vueIndex = path.join(abs, "index.vue");
|
|
55
|
+
if (hasFile(vueIndex)) return vueIndex;
|
|
56
|
+
}
|
|
57
|
+
const parentDir = path.dirname(abs);
|
|
58
|
+
if (path.basename(abs) === path.basename(parentDir)) {
|
|
59
|
+
const parentIndexVue = path.join(parentDir, "index.vue");
|
|
60
|
+
if (hasFile(parentIndexVue)) return parentIndexVue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (isBareAssetImport(source)) {
|
|
64
|
+
const sourcePath = source.split("?")[0].split("#")[0];
|
|
65
|
+
const candidates = [path.resolve(process.cwd(), "resources/assets", sourcePath), path.resolve(process.cwd(), "resources", sourcePath)];
|
|
66
|
+
for (const candidate of candidates) {
|
|
67
|
+
if (hasFile(candidate)) return candidate;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function autoloadPlugin(identMap) {
|
|
75
|
+
const patterns = Object.entries(identMap).map(([ident, module]) => {
|
|
76
|
+
if (!VALID_IDENT.test(ident)) throw new Error(`Invalid autoload identifier: ${ident}`);
|
|
77
|
+
if (!VALID_MODULE.test(module)) throw new Error(`Invalid autoload module: ${module}`);
|
|
78
|
+
return {
|
|
79
|
+
ident,
|
|
80
|
+
module,
|
|
81
|
+
localName: ident.startsWith("window.") ? ident.slice(7) : ident,
|
|
82
|
+
isWindow: ident.startsWith("window."),
|
|
83
|
+
regex: ident.startsWith("window.") ? null : new RegExp(`(?<![.\\w$])${escapeRegExp(ident)}(?![\\w$])`)
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
name: "mix-inject",
|
|
88
|
+
enforce: "post",
|
|
89
|
+
transform(code, id) {
|
|
90
|
+
if (id.includes("node_modules")) return;
|
|
91
|
+
if (!/\.[jt]sx?$/.test(id.split("?")[0])) return;
|
|
92
|
+
const toAdd = [];
|
|
93
|
+
const seen = /* @__PURE__ */ new Set();
|
|
94
|
+
for (const { ident, module, localName, isWindow, regex } of patterns) {
|
|
95
|
+
if (seen.has(localName)) continue;
|
|
96
|
+
if (code.includes(`from '${module}'`) || code.includes(`from "${module}"`) || code.includes(`require('${module}')`) || code.includes(`require("${module}")`))
|
|
97
|
+
continue;
|
|
98
|
+
const used = isWindow ? code.includes(ident) : regex.test(code);
|
|
99
|
+
if (used) {
|
|
100
|
+
toAdd.push(`import ${localName} from '${module}';`);
|
|
101
|
+
seen.add(localName);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!toAdd.length) return;
|
|
105
|
+
return { code: toAdd.join("\n") + "\n" + code, map: null };
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function collectFiles(dir) {
|
|
110
|
+
const entries = await fs.promises.readdir(dir, { recursive: true, withFileTypes: true });
|
|
111
|
+
return entries.filter((e) => e.isFile()).map((e) => {
|
|
112
|
+
const abs = path.join(e.parentPath, e.name);
|
|
113
|
+
return { abs, rel: path.relative(dir, abs) };
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function safePath(base, untrusted) {
|
|
117
|
+
const resolved = path.resolve(base, untrusted);
|
|
118
|
+
return resolved.startsWith(base + path.sep) ? resolved : null;
|
|
119
|
+
}
|
|
120
|
+
function outputPath(dest, filename) {
|
|
121
|
+
return dest && dest !== "." ? `${dest}/${filename}` : filename;
|
|
122
|
+
}
|
|
123
|
+
function staticCopyPlugin(targets) {
|
|
124
|
+
return {
|
|
125
|
+
name: "mix-static-copy",
|
|
126
|
+
async buildStart() {
|
|
127
|
+
for (const { src, dest, rename } of targets) {
|
|
128
|
+
try {
|
|
129
|
+
if (src.endsWith("/**/*")) {
|
|
130
|
+
const srcDir = src.slice(0, -5);
|
|
131
|
+
for (const { abs, rel } of await collectFiles(srcDir)) {
|
|
132
|
+
this.emitFile({ type: "asset", fileName: outputPath(dest, rel), source: await fs.promises.readFile(abs) });
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
const filename = rename ?? path.basename(src);
|
|
136
|
+
this.emitFile({ type: "asset", fileName: outputPath(dest, filename), source: await fs.promises.readFile(src) });
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (err.code !== "ENOENT") throw err;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
configureServer(server) {
|
|
144
|
+
server.middlewares.use((req, res, next) => {
|
|
145
|
+
const url = decodeURIComponent(req.url?.split("?")[0] ?? "");
|
|
146
|
+
for (const { src, dest, rename } of targets) {
|
|
147
|
+
if (src.endsWith("/**/*")) {
|
|
148
|
+
const prefix = `/${dest}/`;
|
|
149
|
+
if (url.startsWith(prefix)) {
|
|
150
|
+
const srcDir = path.resolve(src.slice(0, -5));
|
|
151
|
+
const file = safePath(srcDir, url.slice(prefix.length));
|
|
152
|
+
if (!file) {
|
|
153
|
+
next();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
fs.promises.readFile(file).then((content) => res.end(content)).catch(() => next());
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const filename = rename ?? path.basename(src);
|
|
161
|
+
if (url === `/${outputPath(dest, filename)}`) {
|
|
162
|
+
fs.promises.readFile(src).then((content) => res.end(content)).catch(() => next());
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
next();
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async function viteConfigFromGraph(graph, mode) {
|
|
173
|
+
const isProd = mode === "production";
|
|
174
|
+
const input = {};
|
|
175
|
+
for (const e of graph.js) {
|
|
176
|
+
input[relKeyFromResources(e.src)] = path.resolve(e.src);
|
|
177
|
+
}
|
|
178
|
+
for (const e of graph.sass) {
|
|
179
|
+
input[relKeyFromResources(e.src)] = path.resolve(e.src);
|
|
180
|
+
}
|
|
181
|
+
for (const e of graph.css) {
|
|
182
|
+
input[relKeyFromResources(e.src)] = path.resolve(e.src);
|
|
183
|
+
}
|
|
184
|
+
const staticTargets = [];
|
|
185
|
+
const resolvedPublic = path.resolve(graph.publicPath);
|
|
186
|
+
const publicPrefixRe = new RegExp(`^${escapeRegExp(ensurePosix(graph.publicPath))}/?`);
|
|
187
|
+
function guardDest(dest) {
|
|
188
|
+
const resolved = path.resolve(graph.publicPath, dest);
|
|
189
|
+
if (resolved !== resolvedPublic && !resolved.startsWith(resolvedPublic + path.sep)) {
|
|
190
|
+
throw new Error(`copy destination escapes publicPath: ${dest}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const c of graph.copies) {
|
|
194
|
+
const normalizedDest = ensurePosix(c.dest).replace(publicPrefixRe, "");
|
|
195
|
+
guardDest(normalizedDest);
|
|
196
|
+
const destDir = path.posix.dirname(normalizedDest);
|
|
197
|
+
const base = path.posix.basename(normalizedDest);
|
|
198
|
+
const hasExt = /\.[a-z0-9]+$/i.test(base);
|
|
199
|
+
staticTargets.push({
|
|
200
|
+
src: ensurePosix(c.src),
|
|
201
|
+
dest: hasExt ? destDir : normalizedDest,
|
|
202
|
+
...hasExt ? { rename: base } : {}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
for (const cd of graph.copyDirs) {
|
|
206
|
+
const normalizedDest = ensurePosix(cd.dest).replace(publicPrefixRe, "");
|
|
207
|
+
guardDest(normalizedDest);
|
|
208
|
+
staticTargets.push({
|
|
209
|
+
src: ensurePosix(cd.src) + "/**/*",
|
|
210
|
+
dest: normalizedDest
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const autoloadMap = {};
|
|
214
|
+
for (const [module, identifiers] of Object.entries(graph.autoload)) {
|
|
215
|
+
for (const ident of identifiers) {
|
|
216
|
+
autoloadMap[ident] = module;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const plugins = [webpackCompatResolvePlugin()];
|
|
220
|
+
if (graph.vue) {
|
|
221
|
+
plugins.push(vue());
|
|
222
|
+
}
|
|
223
|
+
if (Object.keys(autoloadMap).length) {
|
|
224
|
+
plugins.push(autoloadPlugin(autoloadMap));
|
|
225
|
+
}
|
|
226
|
+
if (staticTargets.length) {
|
|
227
|
+
plugins.push(staticCopyPlugin(staticTargets));
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
resolve: {
|
|
231
|
+
extensions: [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx", ".json", ".vue"],
|
|
232
|
+
alias: [{ find: /^~(.*)$/, replacement: "$1" }]
|
|
233
|
+
},
|
|
234
|
+
plugins,
|
|
235
|
+
build: {
|
|
236
|
+
manifest: true,
|
|
237
|
+
outDir: graph.publicPath,
|
|
238
|
+
emptyOutDir: false,
|
|
239
|
+
assetsDir: "",
|
|
240
|
+
rollupOptions: {
|
|
241
|
+
input,
|
|
242
|
+
output: isProd ? {
|
|
243
|
+
entryFileNames: `js/[name]-[hash].js`,
|
|
244
|
+
assetFileNames: (assetInfo) => {
|
|
245
|
+
const name = assetInfo.names[0] || "";
|
|
246
|
+
if (name.endsWith(".css")) return `css/[name]-[hash][extname]`;
|
|
247
|
+
return `assets/[name]-[hash][extname]`;
|
|
248
|
+
}
|
|
249
|
+
} : {
|
|
250
|
+
entryFileNames: `js/[name].js`,
|
|
251
|
+
assetFileNames: (assetInfo) => {
|
|
252
|
+
const name = assetInfo.names[0] || "";
|
|
253
|
+
if (name.endsWith(".css")) return `css/${name}`;
|
|
254
|
+
return `assets/${name}`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
server: {
|
|
260
|
+
strictPort: true
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/index.ts
|
|
266
|
+
function normalizePublicPath(p) {
|
|
267
|
+
let end = p.length;
|
|
268
|
+
while (end > 0 && p[end - 1] === "/") end--;
|
|
269
|
+
return p.slice(0, end);
|
|
270
|
+
}
|
|
271
|
+
var Mix = class {
|
|
272
|
+
graph;
|
|
273
|
+
constructor() {
|
|
274
|
+
this.graph = {
|
|
275
|
+
publicPath: "public",
|
|
276
|
+
js: [],
|
|
277
|
+
css: [],
|
|
278
|
+
sass: [],
|
|
279
|
+
copies: [],
|
|
280
|
+
copyDirs: [],
|
|
281
|
+
autoload: {},
|
|
282
|
+
vue: false
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
setPublicPath(p) {
|
|
286
|
+
const normalized = normalizePublicPath(p);
|
|
287
|
+
const resolved = path2.resolve(process.cwd(), normalized);
|
|
288
|
+
if (resolved !== process.cwd() && !resolved.startsWith(process.cwd() + path2.sep)) {
|
|
289
|
+
throw new Error(`publicPath must be within the project directory: ${p}`);
|
|
290
|
+
}
|
|
291
|
+
this.graph.publicPath = normalized;
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
js(src, dest) {
|
|
295
|
+
this.graph.js.push({ src, destDirOrFile: dest });
|
|
296
|
+
return this;
|
|
297
|
+
}
|
|
298
|
+
vue() {
|
|
299
|
+
this.graph.vue = true;
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
sass(src, dest) {
|
|
303
|
+
this.graph.sass.push({ src, destDir: dest });
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
css(src, destDir) {
|
|
307
|
+
this.graph.css.push({ src, destDir });
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
copy(src, dest) {
|
|
311
|
+
this.graph.copies.push({ src, dest });
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
314
|
+
copyDirectory(src, dest) {
|
|
315
|
+
this.graph.copyDirs.push({ src, dest });
|
|
316
|
+
return this;
|
|
317
|
+
}
|
|
318
|
+
autoload(map) {
|
|
319
|
+
this.graph.autoload = { ...this.graph.autoload, ...map };
|
|
320
|
+
return this;
|
|
321
|
+
}
|
|
322
|
+
toGraph() {
|
|
323
|
+
return {
|
|
324
|
+
publicPath: this.graph.publicPath,
|
|
325
|
+
js: this.graph.js.map((entry) => ({ ...entry })),
|
|
326
|
+
css: this.graph.css.map((entry) => ({ ...entry })),
|
|
327
|
+
sass: this.graph.sass.map((entry) => ({ ...entry })),
|
|
328
|
+
copies: this.graph.copies.map((entry) => ({ ...entry })),
|
|
329
|
+
copyDirs: this.graph.copyDirs.map((entry) => ({ ...entry })),
|
|
330
|
+
autoload: Object.fromEntries(Object.entries(this.graph.autoload).map(([key, values]) => [key, [...values]])),
|
|
331
|
+
vue: this.graph.vue
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
function mix() {
|
|
336
|
+
return new Mix();
|
|
337
|
+
}
|
|
338
|
+
export {
|
|
339
|
+
Mix,
|
|
340
|
+
mix,
|
|
341
|
+
viteConfigFromGraph
|
|
342
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-mix",
|
|
3
|
+
"version": "0.7.3",
|
|
4
|
+
"description": "Use Vite as Laravel Mix",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"vite",
|
|
7
|
+
"laravel",
|
|
8
|
+
"mix",
|
|
9
|
+
"laravel-mix",
|
|
10
|
+
"webpack",
|
|
11
|
+
"build-tool"
|
|
12
|
+
],
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Hugo Rodrigues",
|
|
15
|
+
"url": "https://github.com/hugoboss17"
|
|
16
|
+
},
|
|
17
|
+
"license": "ISC",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/hugoboss17/vite-mix.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/hugoboss17/vite-mix/issues"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=24"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"verify:pack": "node scripts/verify-pack.ts",
|
|
51
|
+
"check:lockfile": "node scripts/check-lockfile-change.ts",
|
|
52
|
+
"prepack": "npm run build && npm run verify:pack",
|
|
53
|
+
"lint": "node -e \"console.log('add eslint if you want')\"",
|
|
54
|
+
"prepare": "pre-commit install || true"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
59
|
+
"sass": "^1.0.0",
|
|
60
|
+
"vite": "^7.0.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "25.3.5",
|
|
64
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
65
|
+
"tsup": "8.5.1",
|
|
66
|
+
"typescript": "5.9.3",
|
|
67
|
+
"vite": "7.3.1",
|
|
68
|
+
"vitest": "4.0.18"
|
|
69
|
+
}
|
|
70
|
+
}
|