@vinktarhq/cli 0.1.0
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/CHANGELOG.md +61 -0
- package/LICENSE +21 -0
- package/README.md +378 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +28 -0
- package/dist/bin.js.map +1 -0
- package/dist/bundler/core.d.ts +140 -0
- package/dist/bundler/core.js +320 -0
- package/dist/bundler/core.js.map +1 -0
- package/dist/bundler/esbuild.d.ts +44 -0
- package/dist/bundler/esbuild.js +85 -0
- package/dist/bundler/esbuild.js.map +1 -0
- package/dist/bundler/rollup.d.ts +84 -0
- package/dist/bundler/rollup.js +221 -0
- package/dist/bundler/rollup.js.map +1 -0
- package/dist/bundler/rspack.d.ts +8 -0
- package/dist/bundler/rspack.js +8 -0
- package/dist/bundler/rspack.js.map +1 -0
- package/dist/bundler/webpack.d.ts +83 -0
- package/dist/bundler/webpack.js +104 -0
- package/dist/bundler/webpack.js.map +1 -0
- package/dist/cjs/bundler/core.d.ts +140 -0
- package/dist/cjs/bundler/core.js +331 -0
- package/dist/cjs/bundler/esbuild.d.ts +44 -0
- package/dist/cjs/bundler/esbuild.js +87 -0
- package/dist/cjs/bundler/rollup.d.ts +84 -0
- package/dist/cjs/bundler/rollup.js +226 -0
- package/dist/cjs/bundler/rspack.d.ts +8 -0
- package/dist/cjs/bundler/rspack.js +12 -0
- package/dist/cjs/bundler/webpack.d.ts +83 -0
- package/dist/cjs/bundler/webpack.js +107 -0
- package/dist/cjs/cli.d.ts +17 -0
- package/dist/cjs/cli.js +358 -0
- package/dist/cjs/commands/doctor.d.ts +15 -0
- package/dist/cjs/commands/doctor.js +134 -0
- package/dist/cjs/commands/inject.d.ts +70 -0
- package/dist/cjs/commands/inject.js +123 -0
- package/dist/cjs/commands/resolve.d.ts +32 -0
- package/dist/cjs/commands/resolve.js +130 -0
- package/dist/cjs/commands/upload.d.ts +74 -0
- package/dist/cjs/commands/upload.js +254 -0
- package/dist/cjs/config.d.ts +51 -0
- package/dist/cjs/config.js +152 -0
- package/dist/cjs/debug-id.d.ts +176 -0
- package/dist/cjs/debug-id.js +323 -0
- package/dist/cjs/discover.d.ts +50 -0
- package/dist/cjs/discover.js +235 -0
- package/dist/cjs/env.d.ts +22 -0
- package/dist/cjs/env.js +52 -0
- package/dist/cjs/glob.d.ts +34 -0
- package/dist/cjs/glob.js +112 -0
- package/dist/cjs/http.d.ts +68 -0
- package/dist/cjs/http.js +211 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +53 -0
- package/dist/cjs/limits.d.ts +35 -0
- package/dist/cjs/limits.js +38 -0
- package/dist/cjs/multipart.d.ts +28 -0
- package/dist/cjs/multipart.js +44 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/upload.d.ts +178 -0
- package/dist/cjs/upload.js +394 -0
- package/dist/cjs/url.d.ts +25 -0
- package/dist/cjs/url.js +43 -0
- package/dist/cjs/version.d.ts +10 -0
- package/dist/cjs/version.js +13 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +354 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/doctor.js +132 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/inject.d.ts +70 -0
- package/dist/commands/inject.js +120 -0
- package/dist/commands/inject.js.map +1 -0
- package/dist/commands/resolve.d.ts +32 -0
- package/dist/commands/resolve.js +128 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/upload.d.ts +74 -0
- package/dist/commands/upload.js +252 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config.d.ts +51 -0
- package/dist/config.js +150 -0
- package/dist/config.js.map +1 -0
- package/dist/debug-id.d.ts +176 -0
- package/dist/debug-id.js +307 -0
- package/dist/debug-id.js.map +1 -0
- package/dist/discover.d.ts +50 -0
- package/dist/discover.js +229 -0
- package/dist/discover.js.map +1 -0
- package/dist/env.d.ts +22 -0
- package/dist/env.js +50 -0
- package/dist/env.js.map +1 -0
- package/dist/glob.d.ts +34 -0
- package/dist/glob.js +109 -0
- package/dist/glob.js.map +1 -0
- package/dist/http.d.ts +68 -0
- package/dist/http.js +203 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/limits.d.ts +35 -0
- package/dist/limits.js +36 -0
- package/dist/limits.js.map +1 -0
- package/dist/multipart.d.ts +28 -0
- package/dist/multipart.js +41 -0
- package/dist/multipart.js.map +1 -0
- package/dist/upload.d.ts +178 -0
- package/dist/upload.js +383 -0
- package/dist/upload.js.map +1 -0
- package/dist/url.d.ts +25 -0
- package/dist/url.js +40 -0
- package/dist/url.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/vite.d.ts +74 -0
- package/dist/vite.js +166 -0
- package/dist/vite.js.map +1 -0
- package/package.json +141 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stampChunk = stampChunk;
|
|
4
|
+
exports.repairChunks = repairChunks;
|
|
5
|
+
exports.writeMaps = writeMaps;
|
|
6
|
+
exports.vinktarRollup = vinktarRollup;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const debug_id_js_1 = require("../debug-id.js");
|
|
10
|
+
const discover_js_1 = require("../discover.js");
|
|
11
|
+
const core_js_1 = require("./core.js");
|
|
12
|
+
/**
|
|
13
|
+
* Whether the snippet can go at the TOP of this chunk without corrupting its map.
|
|
14
|
+
*
|
|
15
|
+
* Under Rollup it can: nothing re-derives the map after `renderChunk`, so the inserted line is
|
|
16
|
+
* accounted for exactly, in `repairChunks`.
|
|
17
|
+
*
|
|
18
|
+
* Under Rolldown it cannot, and this is the difference between a working map and a meaningless
|
|
19
|
+
* one. Rolldown's Oxc minifier runs AFTER every `renderChunk` hook and rebuilds the chunk's map
|
|
20
|
+
* from the code we hand it, composed against a base that knows nothing about our extra line — so
|
|
21
|
+
* every mapping in the file lands inside the snippet. A build proved it: the first segment of a
|
|
22
|
+
* Vite 8 chunk pointed at generated column 1, which is the middle of the injected IIFE.
|
|
23
|
+
*
|
|
24
|
+
* There is no hook between those two steps, and expressing the insertion as a real transform map
|
|
25
|
+
* would mean emitting a segment per token for every chunk — which is what both incumbents do, and
|
|
26
|
+
* what their magic-string dependency is for. Appending moves nothing, so it needs no map at all;
|
|
27
|
+
* the cost is that a chunk which throws while initialising does not register its id there.
|
|
28
|
+
*
|
|
29
|
+
* `this.meta.rolldownVersion` is Rolldown's own marker, absent under Rollup.
|
|
30
|
+
*/
|
|
31
|
+
function prependable(context) {
|
|
32
|
+
return context?.meta?.rolldownVersion === undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The name a chunk answers to in every hook.
|
|
36
|
+
*
|
|
37
|
+
* `fileName` still holds a hash PLACEHOLDER during `renderChunk` — the whole point of the hook is
|
|
38
|
+
* that the hash has not been computed yet — so keying on it there and looking it up by the final
|
|
39
|
+
* name later matched nothing at all. `preliminaryFileName` is the one that is stable across both.
|
|
40
|
+
*/
|
|
41
|
+
function keyOf(chunk) {
|
|
42
|
+
return chunk.preliminaryFileName ?? chunk.fileName ?? '';
|
|
43
|
+
}
|
|
44
|
+
/** Stamp one chunk and remember what its map will owe. Shared with the Vite plugin. */
|
|
45
|
+
function stampChunk(code, rawChunk, tracked, context) {
|
|
46
|
+
const chunk = (rawChunk ?? {});
|
|
47
|
+
const stamped = (0, core_js_1.stamp)(code, { prepend: prependable(context) });
|
|
48
|
+
tracked.set(keyOf(chunk), { debugId: stamped.debugId, line: stamped.line });
|
|
49
|
+
/**
|
|
50
|
+
* `map: null`, and it must be exactly that — not an omitted key.
|
|
51
|
+
*
|
|
52
|
+
* Rollup reads it as a strict null check: `null` keeps this transform OUT of the chunk's
|
|
53
|
+
* sourcemap chain and leaves the existing map alone, while `undefined` (an omitted key) enters
|
|
54
|
+
* the chain as a MISSING link, which makes Rollup log `SOURCEMAP_BROKEN` and collapse the whole
|
|
55
|
+
* chain to empty mappings. The line this inserted is accounted for in {@link repairChunks}
|
|
56
|
+
* instead, exactly, rather than through a generated transform map.
|
|
57
|
+
*/
|
|
58
|
+
return stamped.changed ? { code: stamped.code, map: null } : null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Square the chunk's map up with the snippet, and put back the comment that ran after us.
|
|
62
|
+
*
|
|
63
|
+
* Both halves belong in `generateBundle` with `order: 'pre'`, and both are load-bearing:
|
|
64
|
+
*
|
|
65
|
+
* - **The comment.** Vite 8 is Rolldown, and its Oxc minifier runs after `renderChunk`: it strips
|
|
66
|
+
* the `//# debugId=` line and leaves the snippet, so the uploader read no id, derived a fresh
|
|
67
|
+
* one from the minified bytes and filed the map under an id no stack frame reports. The
|
|
68
|
+
* embedded marker survives minification and is what this package reads back, but the comment is
|
|
69
|
+
* what every OTHER tool reads.
|
|
70
|
+
* - **The mappings.** Inserting a line above the code shifts every generated line below it by
|
|
71
|
+
* one, and splicing one empty group into `mappings` is the exact repair. It has to happen HERE,
|
|
72
|
+
* before any other plugin's `generateBundle`, because Vite's own
|
|
73
|
+
* `vite:build-import-analysis` rewrites the entry chunk and then does
|
|
74
|
+
* `combineSourcemaps(fileName, [itsMap, chunk.map])`. Its map is measured against the code
|
|
75
|
+
* INCLUDING our line; combining it with a `chunk.map` that is one line short does not merely
|
|
76
|
+
* lose precision, it collapses to empty mappings — the entry chunk of every Vite build with a
|
|
77
|
+
* dynamic import, silently resolving nothing.
|
|
78
|
+
*
|
|
79
|
+
* `order: 'pre'` is therefore not politeness; it is the difference between a working map and an
|
|
80
|
+
* empty one.
|
|
81
|
+
*/
|
|
82
|
+
function repairChunks(bundle, tracked) {
|
|
83
|
+
for (const asset of Object.values(bundle)) {
|
|
84
|
+
const chunk = asset;
|
|
85
|
+
if (chunk.type !== 'chunk' || typeof chunk.code !== 'string')
|
|
86
|
+
continue;
|
|
87
|
+
const found = tracked.get(keyOf(chunk));
|
|
88
|
+
if (found === undefined)
|
|
89
|
+
continue;
|
|
90
|
+
// Placed the way `inject` places it — before a trailing `sourceMappingURL`, never after.
|
|
91
|
+
// Devtools stop reading at the mapping comment, so a debugId line below it would make the map
|
|
92
|
+
// unreachable, which is a strange way to fix a source-map tool.
|
|
93
|
+
chunk.code = (0, debug_id_js_1.setDebugIdComment)(chunk.code, found.debugId);
|
|
94
|
+
const map = chunk.map;
|
|
95
|
+
if (map === null || map === undefined || typeof map.mappings !== 'string')
|
|
96
|
+
continue;
|
|
97
|
+
if (found.line !== null) {
|
|
98
|
+
map.mappings = String((0, debug_id_js_1.shiftMappings)({ mappings: map.mappings }, found.line)['mappings']);
|
|
99
|
+
}
|
|
100
|
+
// Vite carries `debugId` across its own re-combination by name, and nothing else, so this is
|
|
101
|
+
// the one field worth setting here rather than only on disk.
|
|
102
|
+
map['debugId'] = found.debugId;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Write each chunk's map to disk, because Rollup will not write ours.
|
|
107
|
+
*
|
|
108
|
+
* Rollup emits a chunk's map from the reference it captured while rendering, so an edit made in
|
|
109
|
+
* `generateBundle` reaches the bundle object every other plugin sees and never reaches the file.
|
|
110
|
+
* It survived for chunks a later Vite plugin happened to rebuild and vanished for the rest, which
|
|
111
|
+
* is the worst shape a bug of this kind can take: half the maps in a build one line out, and no
|
|
112
|
+
* warning from anything.
|
|
113
|
+
*
|
|
114
|
+
* Writing the file afterwards is safe in a way that rewriting a chunk would not be. Nothing hashes
|
|
115
|
+
* a source map: its name comes from the chunk's, no integrity attribute covers it, and no importer
|
|
116
|
+
* reads its contents.
|
|
117
|
+
*/
|
|
118
|
+
async function writeMaps(dir, bundle, tracked, done) {
|
|
119
|
+
const chunks = Object.values(bundle).filter((asset) => {
|
|
120
|
+
const chunk = asset;
|
|
121
|
+
return chunk.type === 'chunk' && tracked.has(keyOf(chunk));
|
|
122
|
+
});
|
|
123
|
+
if (process.env['VINKTAR_DEBUG_TRACK'])
|
|
124
|
+
console.log('WRITEMAPS', dir, chunks.length, [...tracked.keys()], Object.keys(bundle));
|
|
125
|
+
if (chunks.length === 0)
|
|
126
|
+
return;
|
|
127
|
+
// One walk of the output, reusing the chunk-to-map resolution the uploader uses — which asks
|
|
128
|
+
// the chunk what its map is called before assuming a `<file>.map` sibling, and so copes with
|
|
129
|
+
// `output.sourcemapFileNames`.
|
|
130
|
+
const { artifacts } = await (0, discover_js_1.discover)(dir).catch(() => ({ artifacts: [] }));
|
|
131
|
+
const maps = new Map(artifacts.map((artifact) => [artifact.file, artifact.map]));
|
|
132
|
+
for (const chunk of chunks) {
|
|
133
|
+
const found = tracked.get(keyOf(chunk));
|
|
134
|
+
const map = chunk.map;
|
|
135
|
+
if (map === null || map === undefined || typeof map.mappings !== 'string')
|
|
136
|
+
continue;
|
|
137
|
+
const path = maps.get((0, node_path_1.join)(dir, chunk.fileName ?? ''));
|
|
138
|
+
if (process.env['VINKTAR_DEBUG_TRACK'])
|
|
139
|
+
console.log('WM', chunk.fileName, path, found.line, typeof map.mappings);
|
|
140
|
+
// Idempotent per file per build: `writeBundle` fires once per output, and two outputs writing
|
|
141
|
+
// into one directory must not each rewrite the other's maps.
|
|
142
|
+
if (path === undefined || path === null || done.has(path))
|
|
143
|
+
continue;
|
|
144
|
+
done.add(path);
|
|
145
|
+
try {
|
|
146
|
+
await (0, promises_1.writeFile)(path, JSON.stringify({ ...map, debugId: found.debugId, debug_id: found.debugId }), 'utf8');
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// A read-only volume, or a map the bundler did not actually write. The chunk still carries
|
|
150
|
+
// its id, and the uploader will stamp whatever map it does find.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function vinktarRollup(options = {}) {
|
|
155
|
+
if ((0, core_js_1.disabled)(options))
|
|
156
|
+
return inert();
|
|
157
|
+
const tracked = new Map();
|
|
158
|
+
const repaired = new Set();
|
|
159
|
+
const stamping = options.injectDebugIds !== false;
|
|
160
|
+
const run = (0, core_js_1.session)(options);
|
|
161
|
+
return {
|
|
162
|
+
name: 'vinktar',
|
|
163
|
+
buildStart() {
|
|
164
|
+
// A watch rebuild rewrites every file, so last build's bookkeeping would suppress this
|
|
165
|
+
// build's repairs.
|
|
166
|
+
tracked.clear();
|
|
167
|
+
repaired.clear();
|
|
168
|
+
},
|
|
169
|
+
renderChunk: {
|
|
170
|
+
order: 'post',
|
|
171
|
+
handler(code, chunk) {
|
|
172
|
+
return stamping ? stampChunk(code, chunk, tracked, this) : null;
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
generateBundle: {
|
|
176
|
+
order: 'pre',
|
|
177
|
+
handler(_options, bundle) {
|
|
178
|
+
repairChunks(bundle, tracked);
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* Every output, not the first one.
|
|
183
|
+
*
|
|
184
|
+
* The previous version latched on the output directory and returned early for the rest, so a
|
|
185
|
+
* build with a client output and an SSR output — SvelteKit, Nuxt, Remix, or plain
|
|
186
|
+
* `output: [a, b]` — uploaded the first and silently dropped the second. Re-uploading costs
|
|
187
|
+
* nothing: the content hashes are checked against the server before any bytes are sent.
|
|
188
|
+
*/
|
|
189
|
+
async writeBundle(output, bundle) {
|
|
190
|
+
const dir = outputDir(output);
|
|
191
|
+
if (dir === '')
|
|
192
|
+
return;
|
|
193
|
+
await writeMaps(dir, bundle, tracked, repaired);
|
|
194
|
+
await run.upload(dir);
|
|
195
|
+
// Deleting here rather than only in `closeBundle`, because `closeBundle` fires only when the
|
|
196
|
+
// caller closes the bundle — Vite and the Rollup CLI do, `rollup.rollup().write()` does not.
|
|
197
|
+
// Leaving the maps in a public directory on that path is the exact leak this deletes.
|
|
198
|
+
await run.cleanup();
|
|
199
|
+
},
|
|
200
|
+
async closeBundle() {
|
|
201
|
+
await run.cleanup();
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* The plugin that does nothing, for `disable: true`.
|
|
207
|
+
*
|
|
208
|
+
* A whole plugin rather than a flag threaded through every hook, so that a config reused by
|
|
209
|
+
* vitest or Storybook costs a function call and not a directory walk.
|
|
210
|
+
*/
|
|
211
|
+
function inert() {
|
|
212
|
+
return {
|
|
213
|
+
name: 'vinktar',
|
|
214
|
+
buildStart() { },
|
|
215
|
+
renderChunk: { order: 'post', handler: () => null },
|
|
216
|
+
generateBundle: { order: 'pre', handler: () => { } },
|
|
217
|
+
writeBundle: async () => { },
|
|
218
|
+
closeBundle: async () => { },
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function outputDir(output) {
|
|
222
|
+
const dir = output.dir ?? (output.file === undefined ? '' : (0, node_path_1.join)(output.file, '..'));
|
|
223
|
+
if (dir === '')
|
|
224
|
+
return '';
|
|
225
|
+
return (0, node_path_1.isAbsolute)(dir) ? dir : (0, node_path_1.join)(process.cwd(), dir);
|
|
226
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rspack reimplements webpack's plugin API — `thisCompilation`, `processAssets`, the stage
|
|
3
|
+
* constants and `compilation.updateAsset` all behave the same way — so the plugin is the same
|
|
4
|
+
* plugin. This module exists so an rspack config can say `vinktarRspack` and read correctly,
|
|
5
|
+
* rather than importing something called "webpack" into a build that is not one.
|
|
6
|
+
*/
|
|
7
|
+
export { VinktarWebpackPlugin as VinktarRspackPlugin, vinktarWebpack as vinktarRspack } from './webpack.js';
|
|
8
|
+
export type { BundlerOptions } from './core.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.vinktarRspack = exports.VinktarRspackPlugin = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* rspack reimplements webpack's plugin API — `thisCompilation`, `processAssets`, the stage
|
|
6
|
+
* constants and `compilation.updateAsset` all behave the same way — so the plugin is the same
|
|
7
|
+
* plugin. This module exists so an rspack config can say `vinktarRspack` and read correctly,
|
|
8
|
+
* rather than importing something called "webpack" into a build that is not one.
|
|
9
|
+
*/
|
|
10
|
+
var webpack_js_1 = require("./webpack.js");
|
|
11
|
+
Object.defineProperty(exports, "VinktarRspackPlugin", { enumerable: true, get: function () { return webpack_js_1.VinktarWebpackPlugin; } });
|
|
12
|
+
Object.defineProperty(exports, "vinktarRspack", { enumerable: true, get: function () { return webpack_js_1.vinktarWebpack; } });
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type BundlerOptions } from './core.js';
|
|
2
|
+
/**
|
|
3
|
+
* The webpack plugin, which is also the rspack plugin.
|
|
4
|
+
*
|
|
5
|
+
* ```js
|
|
6
|
+
* const { vinktarWebpack } = require('@vinktarhq/cli/webpack');
|
|
7
|
+
* module.exports = { plugins: [new vinktarWebpack({ urlPrefix: '/static/' })] };
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* rspack reimplements webpack's plugin API, including `processAssets` and its stage constants, so
|
|
11
|
+
* one class serves both. `@vinktarhq/cli/rspack` re-exports this under its own name because that is
|
|
12
|
+
* what a reader of an rspack config expects to see.
|
|
13
|
+
*
|
|
14
|
+
* ## The stage matters
|
|
15
|
+
*
|
|
16
|
+
* `PROCESS_ASSETS_STAGE_OPTIMIZE_HASH` is the documented point for "changing the asset in a way
|
|
17
|
+
* that affects its hash", and it is where the real content hashes are computed. Doing this any
|
|
18
|
+
* later — in `afterEmit`, say — means the file on disk no longer matches the hash in its own name,
|
|
19
|
+
* and everything downstream that recorded that hash (a PWA precache manifest, an integrity
|
|
20
|
+
* attribute, a precompressed copy) is pointing at bytes that are gone.
|
|
21
|
+
*
|
|
22
|
+
* One BEFORE it, though, not at it. Taps sharing a stage run in registration order, and rspack's
|
|
23
|
+
* real-content-hash pass is native and registered before any user plugin — so at the stage itself
|
|
24
|
+
* the hash was computed from the pre-injection bytes and the filename described a file that never
|
|
25
|
+
* existed. A build proved it: with the plugin and without it, rspack emitted the same
|
|
26
|
+
* `main.<hash>.js` for different content. webpack is unaffected either way, so both take the
|
|
27
|
+
* earlier slot.
|
|
28
|
+
*
|
|
29
|
+
* It is also after `PROCESS_ASSETS_STAGE_DEV_TOOLING`, which is where `SourceMapDevToolPlugin`
|
|
30
|
+
* emits the `.map` assets and appends the `sourceMappingURL` comment. That ordering is what lets
|
|
31
|
+
* the map be corrected in the same pass as the chunk: the injected line shifts every mapping
|
|
32
|
+
* below it, and a map fixed in a later hook would already have been hashed and written.
|
|
33
|
+
*/
|
|
34
|
+
/** Just enough of webpack's shape to compile without depending on it. */
|
|
35
|
+
interface Compiler {
|
|
36
|
+
hooks: {
|
|
37
|
+
thisCompilation: {
|
|
38
|
+
tap(name: string, fn: (compilation: Compilation) => void): void;
|
|
39
|
+
};
|
|
40
|
+
afterEmit: {
|
|
41
|
+
tapPromise(name: string, fn: (compilation: Compilation) => Promise<void>): void;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
options: {
|
|
45
|
+
output?: {
|
|
46
|
+
path?: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
webpack?: {
|
|
50
|
+
Compilation?: {
|
|
51
|
+
PROCESS_ASSETS_STAGE_OPTIMIZE_HASH?: number;
|
|
52
|
+
};
|
|
53
|
+
sources?: SourceLibrary;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
interface SourceLibrary {
|
|
57
|
+
RawSource: new (value: string) => unknown;
|
|
58
|
+
}
|
|
59
|
+
interface Compilation {
|
|
60
|
+
hooks: {
|
|
61
|
+
processAssets: {
|
|
62
|
+
tap(options: {
|
|
63
|
+
name: string;
|
|
64
|
+
stage: number;
|
|
65
|
+
}, fn: (assets: Record<string, Asset>) => void): void;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
updateAsset(name: string, source: unknown): void;
|
|
69
|
+
getAsset(name: string): {
|
|
70
|
+
source: Asset;
|
|
71
|
+
} | undefined;
|
|
72
|
+
}
|
|
73
|
+
interface Asset {
|
|
74
|
+
source(): string | Buffer;
|
|
75
|
+
}
|
|
76
|
+
export declare class VinktarWebpackPlugin {
|
|
77
|
+
private readonly options;
|
|
78
|
+
constructor(options?: BundlerOptions);
|
|
79
|
+
apply(compiler: Compiler): void;
|
|
80
|
+
}
|
|
81
|
+
/** Named for how a config reads: `new vinktarWebpack({ … })`. */
|
|
82
|
+
export declare const vinktarWebpack: typeof VinktarWebpackPlugin;
|
|
83
|
+
export type { BundlerOptions } from './core.js';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.vinktarWebpack = exports.VinktarWebpackPlugin = void 0;
|
|
4
|
+
const core_js_1 = require("./core.js");
|
|
5
|
+
const NAME = 'vinktar';
|
|
6
|
+
/** webpack's own value for this stage, when the compiler does not expose the constant. */
|
|
7
|
+
const STAGE_OPTIMIZE_HASH = 2500;
|
|
8
|
+
class VinktarWebpackPlugin {
|
|
9
|
+
options;
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
apply(compiler) {
|
|
14
|
+
if ((0, core_js_1.disabled)(this.options))
|
|
15
|
+
return;
|
|
16
|
+
const stamping = this.options.injectDebugIds !== false;
|
|
17
|
+
const RawSource = compiler.webpack?.sources?.RawSource;
|
|
18
|
+
const stage = (compiler.webpack?.Compilation?.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH ?? STAGE_OPTIMIZE_HASH) - 1;
|
|
19
|
+
const run = (0, core_js_1.session)(this.options);
|
|
20
|
+
compiler.hooks.thisCompilation.tap(NAME, (compilation) => {
|
|
21
|
+
compilation.hooks.processAssets.tap({ name: NAME, stage }, (assets) => {
|
|
22
|
+
if (!stamping)
|
|
23
|
+
return;
|
|
24
|
+
const write = (name, value) => {
|
|
25
|
+
// `RawSource` is reached through the compiler rather than imported, so this file has no
|
|
26
|
+
// build-time dependency on webpack at all — and so rspack's own sources are used under
|
|
27
|
+
// rspack rather than webpack's.
|
|
28
|
+
compilation.updateAsset(name, RawSource === undefined ? value : new RawSource(value));
|
|
29
|
+
};
|
|
30
|
+
for (const name of Object.keys(assets)) {
|
|
31
|
+
if (!(0, core_js_1.isScript)(name))
|
|
32
|
+
continue;
|
|
33
|
+
const asset = compilation.getAsset(name);
|
|
34
|
+
if (asset === undefined)
|
|
35
|
+
continue;
|
|
36
|
+
const code = String(asset.source.source());
|
|
37
|
+
const stamped = (0, core_js_1.stamp)(code);
|
|
38
|
+
if (!stamped.changed)
|
|
39
|
+
continue;
|
|
40
|
+
write(name, stamped.code);
|
|
41
|
+
// The map is corrected in the same stage, from the same bytes. Skipping it leaves every
|
|
42
|
+
// line one out of step, which is worse than no map: the frame resolves, to the wrong
|
|
43
|
+
// line, and nobody doubts a source map that answered.
|
|
44
|
+
const mapName = mapAssetOf(name, code, assets);
|
|
45
|
+
if (mapName === null)
|
|
46
|
+
continue;
|
|
47
|
+
const map = compilation.getAsset(mapName);
|
|
48
|
+
if (map === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
try {
|
|
51
|
+
write(mapName, (0, core_js_1.injectIntoMap)(String(map.source.source()), stamped.debugId, stamped.line));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// A map asset that is not JSON is not ours to rewrite.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
compiler.hooks.afterEmit.tapPromise(NAME, async () => {
|
|
60
|
+
const dir = compiler.options.output?.path ?? '';
|
|
61
|
+
if (dir === '')
|
|
62
|
+
return;
|
|
63
|
+
await run.upload(dir);
|
|
64
|
+
await run.cleanup();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.VinktarWebpackPlugin = VinktarWebpackPlugin;
|
|
69
|
+
const SOURCE_MAPPING_COMMENT = /^\/\/[#@] sourceMappingURL=(.*)$/gm;
|
|
70
|
+
/**
|
|
71
|
+
* The map asset belonging to a chunk asset.
|
|
72
|
+
*
|
|
73
|
+
* `output.sourceMapFilename` is configurable — `[file].map` is only the default, and a build that
|
|
74
|
+
* puts maps under `sourcemaps/[name].map` has no `<file>.map` asset at all. The chunk names its
|
|
75
|
+
* own map in the comment `SourceMapDevToolPlugin` just appended, so that is asked first and the
|
|
76
|
+
* sibling is the fallback.
|
|
77
|
+
*/
|
|
78
|
+
function mapAssetOf(name, code, assets) {
|
|
79
|
+
let last = null;
|
|
80
|
+
for (const match of code.matchAll(SOURCE_MAPPING_COMMENT))
|
|
81
|
+
last = match;
|
|
82
|
+
const referenced = last?.[1]?.trim();
|
|
83
|
+
if (referenced !== undefined && referenced !== '' && !referenced.startsWith('data:')) {
|
|
84
|
+
const slash = name.lastIndexOf('/');
|
|
85
|
+
const directory = slash === -1 ? '' : name.slice(0, slash + 1);
|
|
86
|
+
const target = normalise(`${directory}${(referenced.split('?')[0] ?? '').split('#')[0] ?? ''}`);
|
|
87
|
+
if (target in assets)
|
|
88
|
+
return target;
|
|
89
|
+
}
|
|
90
|
+
return `${name}.map` in assets ? `${name}.map` : null;
|
|
91
|
+
}
|
|
92
|
+
/** Collapse `a/../b` and `./b`, which a relative sourceMappingURL legitimately contains. */
|
|
93
|
+
function normalise(path) {
|
|
94
|
+
const parts = [];
|
|
95
|
+
for (const segment of path.split('/')) {
|
|
96
|
+
if (segment === '.' || segment === '')
|
|
97
|
+
continue;
|
|
98
|
+
if (segment === '..') {
|
|
99
|
+
parts.pop();
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
parts.push(segment);
|
|
103
|
+
}
|
|
104
|
+
return parts.join('/');
|
|
105
|
+
}
|
|
106
|
+
/** Named for how a config reads: `new vinktarWebpack({ … })`. */
|
|
107
|
+
exports.vinktarWebpack = VinktarWebpackPlugin;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { VERSION } from './version.js';
|
|
2
|
+
export interface Args {
|
|
3
|
+
readonly positional: string[];
|
|
4
|
+
readonly flags: Map<string, string | boolean>;
|
|
5
|
+
/** Values of repeatable flags, in order. `--ignore` and `--header` are the two. */
|
|
6
|
+
readonly repeated: Map<string, string[]>;
|
|
7
|
+
}
|
|
8
|
+
export declare function parse(argv: readonly string[]): Args;
|
|
9
|
+
/**
|
|
10
|
+
* Exit codes: 0 fine, 1 the thing did not work, 2 it worked and something is wrong anyway.
|
|
11
|
+
*
|
|
12
|
+
* Two rather than one for `--strict`, so a pipeline can tell "the upload failed" from "the upload
|
|
13
|
+
* succeeded and half your chunks have no maps" — which are different problems with different
|
|
14
|
+
* owners, and merging them means neither gets fixed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function run(argv: readonly string[], log?: (message?: any, ...optionalParams: any[]) => void, fail?: (message?: any, ...optionalParams: any[]) => void): Promise<number>;
|
|
17
|
+
export { VERSION };
|