rails-vite-plugin 0.2.1 → 0.2.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/dist/jsbundling.js +21 -7
- package/dist/shared/entries.js +49 -1
- package/package.json +1 -1
package/dist/jsbundling.js
CHANGED
|
@@ -56,6 +56,9 @@ export default function jsbundling(options = {}) {
|
|
|
56
56
|
outDir: userConfig.build?.outDir ?? ssrConfig.outDir,
|
|
57
57
|
[bundlerOptionsKey]: {
|
|
58
58
|
input: userBundlerInput ?? ssrConfig.entry,
|
|
59
|
+
output: {
|
|
60
|
+
assetFileNames: '[name][extname]',
|
|
61
|
+
},
|
|
59
62
|
},
|
|
60
63
|
},
|
|
61
64
|
} : {}),
|
|
@@ -80,7 +83,7 @@ export default function jsbundling(options = {}) {
|
|
|
80
83
|
if (chunkInfo.facadeModuleId && cssExtensions.test(chunkInfo.facadeModuleId)) {
|
|
81
84
|
return `${CSS_FACADE_PREFIX}[name].js`;
|
|
82
85
|
}
|
|
83
|
-
return '[name].js';
|
|
86
|
+
return '_[name]-[hash].js';
|
|
84
87
|
},
|
|
85
88
|
chunkFileNames: '[name]-[hash].js',
|
|
86
89
|
assetFileNames: '[name][extname]',
|
|
@@ -120,19 +123,30 @@ export default function jsbundling(options = {}) {
|
|
|
120
123
|
// SSR bundles are Node.js server code — not served to browsers.
|
|
121
124
|
if (resolvedConfig.build.ssr)
|
|
122
125
|
return;
|
|
123
|
-
// Copy entry files (JS + CSS) to the asset pipeline directory
|
|
124
|
-
// so Propshaft/Sprockets can serve them via Rails helpers.
|
|
125
|
-
// Chunks stay in outputDir and are served directly by the web server.
|
|
126
126
|
fs.mkdirSync(assetPipelineDir, { recursive: true });
|
|
127
127
|
const outDir = resolvedConfig.build.outDir;
|
|
128
|
-
// Only copy CSS files that correspond to entries (entry CSS or CSS extracted
|
|
129
|
-
// from JS entries). Shared chunk CSS stays in outputDir.
|
|
130
128
|
const entryNames = new Set(entries.map(e => e.name));
|
|
131
129
|
for (const [fileName, chunk] of Object.entries(bundle)) {
|
|
132
130
|
const isEntryJs = chunk.type === 'chunk' && chunk.isEntry;
|
|
133
131
|
const isEntryCss = chunk.type === 'asset' && cssExtensions.test(fileName)
|
|
134
132
|
&& entryNames.has(fileName.replace(/\.[^.]+$/, ''));
|
|
135
|
-
if (isEntryJs
|
|
133
|
+
if (isEntryJs) {
|
|
134
|
+
// JS entries are built as _[name]-[hash].js (content-hashed).
|
|
135
|
+
// Write a thin shim as [name].js for the asset pipeline so that
|
|
136
|
+
// Propshaft/Sprockets can serve it via javascript_include_tag.
|
|
137
|
+
//
|
|
138
|
+
// Why: Propshaft digests entry filenames (inertia.js → inertia-abc123.js)
|
|
139
|
+
// but cannot rewrite import paths inside Vite's chunks. Without the shim,
|
|
140
|
+
// the <script> tag and chunk imports resolve to different ES module
|
|
141
|
+
// instances — duplicating React, Inertia, and other stateful singletons.
|
|
142
|
+
// The shim ensures both paths chain to the same _[name]-[hash].js module.
|
|
143
|
+
const shimName = chunk.name + '.js';
|
|
144
|
+
const shim = `import "./${fileName}";export * from "./${fileName}";\n`;
|
|
145
|
+
fs.writeFileSync(path.join(outDir, shimName), shim);
|
|
146
|
+
fs.writeFileSync(path.join(assetPipelineDir, shimName), shim);
|
|
147
|
+
}
|
|
148
|
+
else if (isEntryCss) {
|
|
149
|
+
// Copy entry CSS to the asset pipeline. Shared chunk CSS stays in outputDir.
|
|
136
150
|
const src = path.join(outDir, fileName);
|
|
137
151
|
const dest = path.join(assetPipelineDir, fileName);
|
|
138
152
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
package/dist/shared/entries.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import picomatch from 'picomatch';
|
|
3
4
|
import { cssExtensionList } from './css.js';
|
|
4
5
|
const jsExtensions = ['mjs', 'js', 'mts', 'ts', 'jsx', 'tsx'];
|
|
5
6
|
const entrypointExtensions = new RegExp(`\\.(${[...jsExtensions, ...cssExtensionList].join('|')})$`);
|
|
@@ -13,7 +14,8 @@ export function resolveEntries(input, sourceDir) {
|
|
|
13
14
|
sourcePath: prefixWithSourceDir(value, sourceDir),
|
|
14
15
|
}));
|
|
15
16
|
}
|
|
16
|
-
const
|
|
17
|
+
const rawInputs = Array.isArray(input) ? input : [input];
|
|
18
|
+
const inputs = rawInputs.flatMap(entry => expandGlob(entry, sourceDir));
|
|
17
19
|
const sourcePaths = inputs.map(entry => prefixWithSourceDir(entry, sourceDir));
|
|
18
20
|
const commonPrefix = detectCommonEntryPrefix(sourcePaths, sourceDir);
|
|
19
21
|
return sourcePaths.map(sourcePath => ({
|
|
@@ -65,6 +67,52 @@ export function detectEntrypoint(sourceDir) {
|
|
|
65
67
|
}
|
|
66
68
|
return 'application.js';
|
|
67
69
|
}
|
|
70
|
+
function isGlob(pattern) {
|
|
71
|
+
return /[*?{]/.test(pattern);
|
|
72
|
+
}
|
|
73
|
+
function expandGlob(entry, sourceDir) {
|
|
74
|
+
if (!isGlob(entry))
|
|
75
|
+
return [entry];
|
|
76
|
+
const matcher = picomatch(entry);
|
|
77
|
+
if (entry.includes('**')) {
|
|
78
|
+
// Recursive: find the static prefix directory, then walk it
|
|
79
|
+
const parts = entry.split('/');
|
|
80
|
+
const staticParts = [];
|
|
81
|
+
for (const part of parts) {
|
|
82
|
+
if (isGlob(part))
|
|
83
|
+
break;
|
|
84
|
+
staticParts.push(part);
|
|
85
|
+
}
|
|
86
|
+
const baseDir = staticParts.length > 0
|
|
87
|
+
? path.join(sourceDir, ...staticParts)
|
|
88
|
+
: sourceDir;
|
|
89
|
+
if (!fs.existsSync(baseDir))
|
|
90
|
+
return [];
|
|
91
|
+
return walkDir(baseDir, sourceDir).filter(f => matcher(f));
|
|
92
|
+
}
|
|
93
|
+
// Non-recursive: single directory match
|
|
94
|
+
const dir = path.join(sourceDir, path.dirname(entry));
|
|
95
|
+
const pattern = path.basename(entry);
|
|
96
|
+
const fileMatcher = picomatch(pattern);
|
|
97
|
+
if (!fs.existsSync(dir))
|
|
98
|
+
return [];
|
|
99
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
100
|
+
.filter(f => f.isFile() && fileMatcher(f.name))
|
|
101
|
+
.map(f => path.relative(sourceDir, path.join(dir, f.name)));
|
|
102
|
+
}
|
|
103
|
+
function walkDir(dir, baseDir) {
|
|
104
|
+
const results = [];
|
|
105
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
106
|
+
const full = path.join(dir, entry.name);
|
|
107
|
+
if (entry.isDirectory()) {
|
|
108
|
+
results.push(...walkDir(full, baseDir));
|
|
109
|
+
}
|
|
110
|
+
else if (entry.isFile()) {
|
|
111
|
+
results.push(path.relative(baseDir, full));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
68
116
|
function discoverEntrypoints(dir, base = dir) {
|
|
69
117
|
const entries = [];
|
|
70
118
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|