@freshpack/webpack 0.0.0-stage → 1.0.1
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 +10 -0
- package/LICENSE +21 -0
- package/denoEnv.d.ts +12 -0
- package/denoEnv.js +61 -0
- package/fresh.d.ts +39 -0
- package/fresh.js +268 -0
- package/mod.d.ts +7 -0
- package/mod.js +7 -0
- package/package.json +71 -3
- package/prefresh-forked/index.d.ts +15 -0
- package/prefresh-forked/index.js +57 -0
- package/prefresh-forked/loader/index.d.ts +2 -0
- package/prefresh-forked/loader/index.js +4 -0
- package/prefresh-forked/loader/runtime.js +70 -0
- package/prefresh-forked/utils/Runtime.d.ts +5 -0
- package/prefresh-forked/utils/Runtime.js +52 -0
- package/prefresh-forked/utils/constants.d.ts +8 -0
- package/prefresh-forked/utils/constants.js +23 -0
- package/prefresh-forked/utils/prefresh.d.ts +5 -0
- package/prefresh-forked/utils/prefresh.js +47 -0
- package/quasiquote.d.ts +9 -0
- package/quasiquote.js +30 -0
- package/rawBytesLoader.d.ts +5 -0
- package/rawBytesLoader.js +12 -0
- package/rawImports.d.ts +13 -0
- package/rawImports.js +44 -0
- package/requireHook.d.ts +1 -0
- package/requireHook.js +16 -0
- package/resolve.d.ts +11 -0
- package/resolve.js +405 -0
- package/transpileLoader.d.ts +9 -0
- package/transpileLoader.js +38 -0
- package/unmap.d.ts +9 -0
- package/unmap.js +96 -0
- package/util.d.ts +9 -0
- package/util.js +54 -0
- package/viteComment.d.ts +8 -0
- package/viteComment.js +30 -0
- package/README.md +0 -3
package/resolve.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { RequestedModuleType, ResolutionMode, Workspace, } from "@deno/loader";
|
|
2
|
+
import { assert, unreachable } from "@std/assert";
|
|
3
|
+
import Webpack from "webpack";
|
|
4
|
+
import { Buffer } from "node:buffer";
|
|
5
|
+
import { ensureModPath, stripFileUrl } from "./util.js";
|
|
6
|
+
import { dirname } from "@std/path";
|
|
7
|
+
import { defineRequire } from "./requireHook.js";
|
|
8
|
+
import { registerDenoLoader } from "./transpileLoader.js";
|
|
9
|
+
const internalTranspileLoader = defineRequire("@freshpack/webpack/internal-deno-transpile-loader", await import("./transpileLoader.js"));
|
|
10
|
+
function resolverSpan(logSuccess, name, ...args) {
|
|
11
|
+
let isResolved;
|
|
12
|
+
let timeoutId = setTimeout(() => {
|
|
13
|
+
console.error("[RESOLVE]", "possibly stuck at", name, ...args);
|
|
14
|
+
if (isResolved !== undefined) {
|
|
15
|
+
console.error(" ", "...but it was resolved:", ...isResolved);
|
|
16
|
+
}
|
|
17
|
+
}, 5000);
|
|
18
|
+
const cancelStuck = () => {
|
|
19
|
+
if (timeoutId !== undefined) {
|
|
20
|
+
clearTimeout(timeoutId);
|
|
21
|
+
timeoutId = undefined;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
let postponed = false;
|
|
25
|
+
return {
|
|
26
|
+
simplify(...newArgs) {
|
|
27
|
+
args = newArgs;
|
|
28
|
+
},
|
|
29
|
+
errored(e, ...err) {
|
|
30
|
+
cancelStuck();
|
|
31
|
+
console.error("[RESOLVE]", "errored", name, ...args, "=>", e, ...err);
|
|
32
|
+
},
|
|
33
|
+
resolved(...out) {
|
|
34
|
+
cancelStuck();
|
|
35
|
+
if (isResolved === undefined) {
|
|
36
|
+
isResolved = out;
|
|
37
|
+
if (logSuccess) {
|
|
38
|
+
console.info("[RESOLVE]", name, ...args, "=>", ...out);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.error("[RESOLVE]", "resolved twice", name, ...args, "=>", ...out);
|
|
43
|
+
console.error(" ", "...previously resolved to:", ...isResolved);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[Symbol.dispose]() {
|
|
47
|
+
if (postponed)
|
|
48
|
+
return;
|
|
49
|
+
cancelStuck();
|
|
50
|
+
if (isResolved === undefined) {
|
|
51
|
+
console.error("[RESOLVE]", "missing resolved() call!", name, ...args);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
postpone() {
|
|
55
|
+
postponed = true;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const MATCH_RESOURCE = /^([^!]+)!=!/;
|
|
60
|
+
function requestedModuleType(type) {
|
|
61
|
+
if (type === "asset/source")
|
|
62
|
+
return RequestedModuleType.Text;
|
|
63
|
+
if (type.startsWith("asset/"))
|
|
64
|
+
return RequestedModuleType.Bytes;
|
|
65
|
+
return RequestedModuleType.Default;
|
|
66
|
+
}
|
|
67
|
+
async function simpleDenoResolve(loader, issuer, request, esm) {
|
|
68
|
+
const matchResource = MATCH_RESOURCE.exec(request);
|
|
69
|
+
if (matchResource) {
|
|
70
|
+
const resolved = await simpleDenoResolve(loader, issuer, request.slice(matchResource[0].length), esm);
|
|
71
|
+
return `${matchResource[0]}${resolved}`;
|
|
72
|
+
}
|
|
73
|
+
let resolution;
|
|
74
|
+
if (request.startsWith("/") && issuer.startsWith("https://")) {
|
|
75
|
+
resolution = new URL(request, new URL(issuer).origin).href;
|
|
76
|
+
}
|
|
77
|
+
else if (request.startsWith("/")) {
|
|
78
|
+
resolution = `file://${request}`;
|
|
79
|
+
}
|
|
80
|
+
else if (request === ".") {
|
|
81
|
+
resolution = `file://${dirname(issuer)}`;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
resolution = await loader.resolve(request, issuer, esm ? ResolutionMode.Import : ResolutionMode.Require);
|
|
85
|
+
}
|
|
86
|
+
if (resolution.startsWith("node:")) {
|
|
87
|
+
// PASS: Fallback or external
|
|
88
|
+
}
|
|
89
|
+
else if (resolution.startsWith("https:")) {
|
|
90
|
+
// PASS: https loader
|
|
91
|
+
}
|
|
92
|
+
else if (resolution.startsWith("file:")) {
|
|
93
|
+
if (issuer.startsWith("https://")) {
|
|
94
|
+
// PASS: Disambiguate origin-relative,
|
|
95
|
+
// we don't want to convert /home/user/.cache/deno/npm/.../file.ts imported from
|
|
96
|
+
// https://esm.sh to become https://esm.sh/home/user/...
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
resolution = stripFileUrl(resolution);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
assert(false, `unexpected resolution: ${resolution}`);
|
|
104
|
+
}
|
|
105
|
+
return resolution;
|
|
106
|
+
}
|
|
107
|
+
class FixupResolvePlugin {
|
|
108
|
+
apply(resolver) {
|
|
109
|
+
const resolved = resolver.getHook("resolved");
|
|
110
|
+
for (const hook of ["resolve", "internal-resolve"]) {
|
|
111
|
+
resolver.getHook(hook).tapAsync({
|
|
112
|
+
name: "Ignore https requests",
|
|
113
|
+
stage: -100000,
|
|
114
|
+
}, (req, ctx, cb) => {
|
|
115
|
+
if (req.path && req.path.startsWith("https://") && req.request === "." ||
|
|
116
|
+
req.request && req.request.startsWith("https://")) {
|
|
117
|
+
return resolver.doResolve(resolved, req, "context resolve", ctx, cb);
|
|
118
|
+
}
|
|
119
|
+
// console.log("DIR RESOLVE!!!", req);
|
|
120
|
+
return cb(null);
|
|
121
|
+
// return cb(null, ...args);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// resolver.hooks.result.tap({
|
|
125
|
+
// name: "resolvers in https requests",
|
|
126
|
+
// stage: -100000,
|
|
127
|
+
// }, (req, ctx) => {
|
|
128
|
+
// console.log("post resolve", req);
|
|
129
|
+
// return;
|
|
130
|
+
// });
|
|
131
|
+
// resolver.hooks.resolve.tap({ name: "Fixup" }, (req) => {
|
|
132
|
+
// console.log('fixup resolver', req.request);
|
|
133
|
+
// return req;
|
|
134
|
+
// });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
class DenoResolvePlugin {
|
|
138
|
+
jsr;
|
|
139
|
+
constructor(jsr) {
|
|
140
|
+
this.jsr = jsr;
|
|
141
|
+
}
|
|
142
|
+
apply(resolver) {
|
|
143
|
+
// console.log("HOOKS", resolver.hooks);
|
|
144
|
+
const source = resolver.getHook("resolve");
|
|
145
|
+
const target = resolver.getHook("resolved");
|
|
146
|
+
source.tapAsync({ name: "Deno" }, async (request, ctx, cb) => {
|
|
147
|
+
using span = resolverSpan(this.jsr.opts.debug, "deno resolver", request, ctx);
|
|
148
|
+
try {
|
|
149
|
+
const loader = await this.jsr.loader;
|
|
150
|
+
let requestIssuer;
|
|
151
|
+
if (request.context && "issuer" in request.context) {
|
|
152
|
+
const issuer = request.context.issuer;
|
|
153
|
+
assert(typeof issuer === "string" || issuer === null, "invalid context issuer");
|
|
154
|
+
if (issuer !== "" && issuer !== null) {
|
|
155
|
+
requestIssuer = issuer;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (requestIssuer === undefined && request.path) {
|
|
159
|
+
requestIssuer = ensureModPath(request.path);
|
|
160
|
+
}
|
|
161
|
+
assert(requestIssuer !== undefined, "failed to determine requestIssuer");
|
|
162
|
+
const requestRequest = request.request;
|
|
163
|
+
assert(requestRequest !== undefined, "failed to determine requestRequest");
|
|
164
|
+
span.simplify(requestIssuer, requestRequest);
|
|
165
|
+
const resolution = await simpleDenoResolve(loader, requestIssuer, requestRequest, request.module ?? true);
|
|
166
|
+
const resolvedPath = resolution.startsWith("file://")
|
|
167
|
+
? stripFileUrl(resolution)
|
|
168
|
+
: resolution;
|
|
169
|
+
return resolver.doResolve(target, Object.assign({}, request, {
|
|
170
|
+
path: resolvedPath,
|
|
171
|
+
request: undefined,
|
|
172
|
+
}), `resolved by deno ${request.request} (from ${request.path})`, ctx, (err, result) => {
|
|
173
|
+
span.resolved("doResolve result", resolution, err, result);
|
|
174
|
+
if (err !== undefined || result !== undefined) {
|
|
175
|
+
return cb(err, result);
|
|
176
|
+
}
|
|
177
|
+
cb(null, null);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
span.errored(e, request, ctx);
|
|
182
|
+
cb(e, null);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function fillResourceData(data, loader, url) {
|
|
188
|
+
data.__resolvedByFreshpack = { data, url, loaded: undefined };
|
|
189
|
+
if (url.protocol === "file:") {
|
|
190
|
+
data.resource = url.pathname;
|
|
191
|
+
data.path = url.pathname;
|
|
192
|
+
data.data.path = url.pathname;
|
|
193
|
+
data.context = dirname(url.pathname);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
const loaded = await loader.load(url.href, RequestedModuleType.Default);
|
|
197
|
+
data.__resolvedByFreshpack.loaded = loaded;
|
|
198
|
+
data.resource = loaded.specifier;
|
|
199
|
+
data.path = loaded.specifier;
|
|
200
|
+
data.data.path = loaded.specifier;
|
|
201
|
+
data.context = new URL(".", url).href.slice(0, -1);
|
|
202
|
+
}
|
|
203
|
+
data.query = url.search;
|
|
204
|
+
data.data.query = url.search;
|
|
205
|
+
data.fragment = url.hash;
|
|
206
|
+
// data.type = "application/javascript";
|
|
207
|
+
}
|
|
208
|
+
function breakResourceData(data) {
|
|
209
|
+
data.path = "error";
|
|
210
|
+
data.context = "error";
|
|
211
|
+
data.resource = "error";
|
|
212
|
+
}
|
|
213
|
+
function ensurePluginList(resolver, list, append) {
|
|
214
|
+
if (resolver[list] === undefined) {
|
|
215
|
+
resolver[list] = [];
|
|
216
|
+
}
|
|
217
|
+
assert(Array.isArray(resolver[list]));
|
|
218
|
+
resolver[list].push(append);
|
|
219
|
+
}
|
|
220
|
+
export class DenoLoaderPlugin {
|
|
221
|
+
opts;
|
|
222
|
+
patchImport;
|
|
223
|
+
workspace;
|
|
224
|
+
#transpileLoaderId;
|
|
225
|
+
#loader;
|
|
226
|
+
get loader() {
|
|
227
|
+
return (this.#loader ??= this.workspace.createLoader());
|
|
228
|
+
}
|
|
229
|
+
constructor(opts, patchImport = (_, v) => v) {
|
|
230
|
+
this.opts = opts;
|
|
231
|
+
this.patchImport = patchImport;
|
|
232
|
+
opts.noTranspile = false;
|
|
233
|
+
opts.preserveJsx = false;
|
|
234
|
+
opts.platform = "browser";
|
|
235
|
+
this.workspace = new Workspace(opts);
|
|
236
|
+
this.#transpileLoaderId = registerDenoLoader(() => this.loader);
|
|
237
|
+
}
|
|
238
|
+
apply(compiler) {
|
|
239
|
+
if (compiler.options.resolve.plugins === undefined) {
|
|
240
|
+
compiler.options.resolve.plugins = [];
|
|
241
|
+
}
|
|
242
|
+
assert(Array.isArray(compiler.options.resolve.plugins));
|
|
243
|
+
compiler.options.resolve.plugins.push(new FixupResolvePlugin());
|
|
244
|
+
if (compiler.options.resolveLoader.plugins === undefined) {
|
|
245
|
+
compiler.options.resolveLoader.plugins = [];
|
|
246
|
+
}
|
|
247
|
+
assert(Array.isArray(compiler.options.resolveLoader.plugins));
|
|
248
|
+
compiler.options.resolveLoader.plugins.push(new DenoResolvePlugin(this));
|
|
249
|
+
compiler.options.resolve.modules = [];
|
|
250
|
+
compiler.options.resolve.roots = [];
|
|
251
|
+
compiler.options.resolve.descriptionFiles = [];
|
|
252
|
+
compiler.options.resolveLoader.modules = [];
|
|
253
|
+
compiler.options.resolveLoader.descriptionFiles = [];
|
|
254
|
+
compiler.options.externalsPresets.web = false;
|
|
255
|
+
compiler.options.externalsPresets.webAsync = false;
|
|
256
|
+
if (!compiler.options.output.enabledLibraryTypes) {
|
|
257
|
+
compiler.options.output.enabledLibraryTypes = [];
|
|
258
|
+
}
|
|
259
|
+
if (!compiler.options.output.enabledLibraryTypes.includes("module")) {
|
|
260
|
+
compiler.options.output.enabledLibraryTypes.push("module");
|
|
261
|
+
}
|
|
262
|
+
compiler.options.output.library = { type: "module" };
|
|
263
|
+
compiler.options.output.module = true;
|
|
264
|
+
compiler.options.experiments.outputModule =
|
|
265
|
+
true;
|
|
266
|
+
// Deno provides its own type-stripping when no ts loader is present
|
|
267
|
+
// It is not required since deno-loader handles typescript.
|
|
268
|
+
compiler.options.experiments.typescript = false;
|
|
269
|
+
compiler.hooks.compilation.tap("JsrPlugin", (compilation, { normalModuleFactory }) => {
|
|
270
|
+
normalModuleFactory.hooks.afterResolve.tap("JsrPlugin", ({ createData: data }) => {
|
|
271
|
+
const resource = data.resource;
|
|
272
|
+
if (resource === undefined || resource.includes("://") ||
|
|
273
|
+
!/\.[cm]?[jt]sx?$/.test(resource)) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
data.loaders.push({
|
|
277
|
+
loader: internalTranspileLoader,
|
|
278
|
+
options: { id: this.#transpileLoaderId },
|
|
279
|
+
ident: null,
|
|
280
|
+
type: null,
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
normalModuleFactory.hooks.beforeResolve.tapPromise("JsrPlugin", async (resolveData) => {
|
|
284
|
+
using span = resolverSpan(this.opts.debug, "jsr beforeResolve", resolveData);
|
|
285
|
+
try {
|
|
286
|
+
// try {
|
|
287
|
+
const loader = await this.loader;
|
|
288
|
+
let requestIssuer;
|
|
289
|
+
if (resolveData.contextInfo.issuer !== "") {
|
|
290
|
+
requestIssuer = resolveData.contextInfo.issuer;
|
|
291
|
+
}
|
|
292
|
+
if (requestIssuer === undefined || requestIssuer === null) {
|
|
293
|
+
requestIssuer = ensureModPath(resolveData.context);
|
|
294
|
+
}
|
|
295
|
+
assert(requestIssuer != undefined, "failed to determine request issuer");
|
|
296
|
+
let requestRequest = resolveData.request;
|
|
297
|
+
const dependencyType = resolveData.dependencyType;
|
|
298
|
+
span.simplify({
|
|
299
|
+
requestIssuer,
|
|
300
|
+
requestRequest,
|
|
301
|
+
dependencyType,
|
|
302
|
+
});
|
|
303
|
+
if (requestRequest.startsWith("data:") ||
|
|
304
|
+
requestRequest.startsWith("https:") &&
|
|
305
|
+
!requestRequest.startsWith("https://jsr.io/")) {
|
|
306
|
+
// IDK why those requests even land in this method in some circumstances, returning false
|
|
307
|
+
// here makes them being processed by the handlers below. Most probably I'm missing something,
|
|
308
|
+
// or this webpack api is not intended for such huge loaded replacements.
|
|
309
|
+
span.resolved("delegate-builtin");
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
if (dependencyType === "url" &&
|
|
313
|
+
// new URL() references are always relative, but import.meta.resolve() takes import specifiers.
|
|
314
|
+
resolveData.dependencies[0]?.type !== "import.meta.resolve()") {
|
|
315
|
+
requestRequest = "./" + requestRequest;
|
|
316
|
+
}
|
|
317
|
+
requestRequest = this.patchImport(requestIssuer, requestRequest);
|
|
318
|
+
const resolution = await simpleDenoResolve(loader, requestIssuer, requestRequest, true);
|
|
319
|
+
span.resolved(resolution);
|
|
320
|
+
resolveData.request = this.patchImport(requestIssuer, resolution);
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
span.errored(e);
|
|
324
|
+
throw e;
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
normalModuleFactory.hooks.resolveForScheme.for("node").tapPromise("JsrPlugin", async (resourceData, resolveData) => {
|
|
328
|
+
using span = resolverSpan(this.opts.debug, "node resolveForScheme", resolveData.request);
|
|
329
|
+
resourceData.path = false;
|
|
330
|
+
resourceData.data.path = false;
|
|
331
|
+
span.resolved("stubbed: empty module");
|
|
332
|
+
return true;
|
|
333
|
+
});
|
|
334
|
+
normalModuleFactory.hooks.resolveForScheme.for("jsr").tapPromise("JsrPlugin", async (resourceData, resolveData) => {
|
|
335
|
+
using span = resolverSpan(this.opts.debug, "jsr resolveForScheme", resolveData);
|
|
336
|
+
breakResourceData(resourceData);
|
|
337
|
+
unreachable("jsr should be resolved to https by deno resolver");
|
|
338
|
+
});
|
|
339
|
+
normalModuleFactory.hooks.resolveForScheme.for("npm").tapPromise("JsrPlugin", async (resourceData, resolveData) => {
|
|
340
|
+
using span = resolverSpan(this.opts.debug, "npm resolveForScheme", resolveData);
|
|
341
|
+
breakResourceData(resourceData);
|
|
342
|
+
unreachable("npm should be resolved to files by deno resolver");
|
|
343
|
+
});
|
|
344
|
+
normalModuleFactory.hooks.resolveForScheme.for("https").tapPromise("JsrPlugin", async (resourceData, resolveData) => {
|
|
345
|
+
using span = resolverSpan(this.opts.debug, "https resolveForScheme", resolveData.request);
|
|
346
|
+
const loader = await this.loader;
|
|
347
|
+
// const newResourceData = {...resourceData};
|
|
348
|
+
await fillResourceData(resourceData, loader, new URL(resolveData.request));
|
|
349
|
+
span.resolved("done");
|
|
350
|
+
return true;
|
|
351
|
+
});
|
|
352
|
+
normalModuleFactory.hooks.resolveInScheme.for("https").tapPromise("JsrPlugin", async (resourceData, data) => {
|
|
353
|
+
using span = resolverSpan(this.opts.debug, "https resolveInScheme", resourceData, data.contextInfo, data.request);
|
|
354
|
+
try {
|
|
355
|
+
const loader = await this.loader;
|
|
356
|
+
const requestRequest = data.request;
|
|
357
|
+
if (requestRequest.startsWith("/")) {
|
|
358
|
+
const requestIssuer = data.contextInfo.issuer;
|
|
359
|
+
assert(requestIssuer && requestIssuer.startsWith("https://"), `unexpected /-prefixed request without https:// issuer: ${requestRequest}`);
|
|
360
|
+
const issuerUrl = new URL(requestIssuer);
|
|
361
|
+
const resolved = new URL(requestRequest, issuerUrl.origin);
|
|
362
|
+
span.resolved("origin-relative url", resolved.href);
|
|
363
|
+
await fillResourceData(resourceData, loader, resolved);
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
const requestIssuer = data.contextInfo.issuer;
|
|
368
|
+
assert(requestIssuer !== "");
|
|
369
|
+
const resolved = await loader.resolve(requestRequest, requestIssuer, ResolutionMode.Import);
|
|
370
|
+
span.resolved("url resolved", resolved);
|
|
371
|
+
assert(resolved.startsWith("https:"));
|
|
372
|
+
await fillResourceData(resourceData, loader, new URL(resolved));
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
span.errored(e);
|
|
378
|
+
breakResourceData(resourceData);
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
const hooks = Webpack.NormalModule.getCompilationHooks(compilation);
|
|
382
|
+
hooks.readResourceForScheme.for("node").tapAsync("JsrPlugin", async (_resource, _module, cb) => {
|
|
383
|
+
cb(null, Buffer.from("module.exports = {};"));
|
|
384
|
+
});
|
|
385
|
+
hooks.readResourceForScheme.for("jsr").tapPromise("JsrPlugin", async (resource, module) => {
|
|
386
|
+
using span = resolverSpan(this.opts.debug, "jsr loadResourceForScheme", resource);
|
|
387
|
+
unreachable("jsr should be resolved to https by deno resolver");
|
|
388
|
+
});
|
|
389
|
+
hooks.readResourceForScheme.for("https").tapAsync("JsrPlugin", async (resource, module, cb) => {
|
|
390
|
+
using span = resolverSpan(this.opts.debug, "https readResourceForScheme", resource, module.type);
|
|
391
|
+
try {
|
|
392
|
+
const loader = await this.loader;
|
|
393
|
+
const loaded = await loader.load(resource, requestedModuleType(module.type));
|
|
394
|
+
// TODO: Should import assertions be handled somehow?
|
|
395
|
+
span.resolved(loaded);
|
|
396
|
+
cb(null, Buffer.from(loaded.code));
|
|
397
|
+
}
|
|
398
|
+
catch (e) {
|
|
399
|
+
span.errored(e);
|
|
400
|
+
cb(e);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Loader } from "@deno/loader";
|
|
2
|
+
import type { LoaderContext } from "webpack";
|
|
3
|
+
type Options = {
|
|
4
|
+
id: number;
|
|
5
|
+
};
|
|
6
|
+
type Context = LoaderContext<Options>;
|
|
7
|
+
export declare function registerDenoLoader(get: () => Promise<Loader>): number;
|
|
8
|
+
export default function denoTranspileLoader(this: Context, _source: string): void;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { RequestedModuleType } from "@deno/loader";
|
|
2
|
+
import { toFileUrl } from "@std/path";
|
|
3
|
+
const registry = new Map();
|
|
4
|
+
let nextId = 0;
|
|
5
|
+
export function registerDenoLoader(get) {
|
|
6
|
+
const id = nextId++;
|
|
7
|
+
registry.set(id, get);
|
|
8
|
+
return id;
|
|
9
|
+
}
|
|
10
|
+
const INLINE_SOURCE_MAP = /\n?\/\/# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)[\t ]*$/;
|
|
11
|
+
function splitSourceMap(code) {
|
|
12
|
+
const match = INLINE_SOURCE_MAP.exec(code);
|
|
13
|
+
if (!match)
|
|
14
|
+
return { code, map: undefined };
|
|
15
|
+
return {
|
|
16
|
+
code: code.slice(0, match.index),
|
|
17
|
+
map: JSON.parse(atob(match[1])),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export default function denoTranspileLoader(_source) {
|
|
21
|
+
const callback = this.async();
|
|
22
|
+
const { id } = this.getOptions();
|
|
23
|
+
const get = registry.get(id);
|
|
24
|
+
if (!get) {
|
|
25
|
+
callback(new Error(`deno loader ${id} is not registered`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const resourcePath = this.resourcePath;
|
|
29
|
+
(async () => {
|
|
30
|
+
const loader = await get();
|
|
31
|
+
const specifier = toFileUrl(resourcePath).href;
|
|
32
|
+
const loaded = await loader.load(specifier, RequestedModuleType.Default);
|
|
33
|
+
if (loaded.kind !== "module") {
|
|
34
|
+
throw new Error(`deno loader returned ${loaded.kind} for ${specifier}`);
|
|
35
|
+
}
|
|
36
|
+
return splitSourceMap(new TextDecoder().decode(loaded.code));
|
|
37
|
+
})().then(({ code, map }) => callback(null, code, map), (e) => callback(e));
|
|
38
|
+
}
|
package/unmap.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type * as Webpack from "webpack";
|
|
2
|
+
interface UnmapCompilation extends Webpack.Compilation {
|
|
3
|
+
unmapping: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
export declare function assertUnmapCompilation(compilation: Webpack.Compilation): asserts compilation is UnmapCompilation;
|
|
6
|
+
export declare class UnmapPlugin {
|
|
7
|
+
apply(compiler: Webpack.Compiler): void;
|
|
8
|
+
}
|
|
9
|
+
export {};
|
package/unmap.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { assert } from "@std/assert";
|
|
2
|
+
export function assertUnmapCompilation(compilation) {
|
|
3
|
+
assert("unmapping" in compilation, "missing fresh data in compilation, did you add WebpackFreshEntries plugin?");
|
|
4
|
+
}
|
|
5
|
+
export class UnmapPlugin {
|
|
6
|
+
apply(compiler) {
|
|
7
|
+
const mappings = new Map();
|
|
8
|
+
const nameToRequest = new Map();
|
|
9
|
+
const nameToAsset = new Map();
|
|
10
|
+
compiler.hooks.compilation.tap(UnmapPlugin.name, (compilation, { normalModuleFactory }) => {
|
|
11
|
+
normalModuleFactory.hooks.parser.for("javascript/auto").tap(UnmapPlugin.name, (parser) => {
|
|
12
|
+
parser.hooks.call.for("unmap").tap(UnmapPlugin.name, (expr) => {
|
|
13
|
+
const error = (msg) => {
|
|
14
|
+
const err = new compiler.webpack.WebpackError(msg);
|
|
15
|
+
err.loc = expr.loc;
|
|
16
|
+
parser.state.module.addError(err);
|
|
17
|
+
};
|
|
18
|
+
if (expr.arguments.length !== 2) {
|
|
19
|
+
return error(`unmap requires (name, new URL()) arguments`);
|
|
20
|
+
}
|
|
21
|
+
const nameArg = parser.evaluateExpression(expr.arguments[0]);
|
|
22
|
+
if (!nameArg.isString()) {
|
|
23
|
+
return error(`first argument for unmap should be a static string`);
|
|
24
|
+
}
|
|
25
|
+
const urlExpr = expr.arguments[1];
|
|
26
|
+
if (urlExpr.type !== "NewExpression" ||
|
|
27
|
+
urlExpr.callee.type !== "Identifier" ||
|
|
28
|
+
urlExpr.callee.name !== "URL") {
|
|
29
|
+
return error(`second argument for unmap should be new URL() expression`);
|
|
30
|
+
}
|
|
31
|
+
const key = `${parser.state.module.identifier()}:${urlExpr.range[0]}`;
|
|
32
|
+
mappings.set(key, {
|
|
33
|
+
name: nameArg.string,
|
|
34
|
+
module: parser.state.module,
|
|
35
|
+
loc: expr.loc,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
compilation.hooks.afterOptimizeModules.tap(UnmapPlugin.name, () => {
|
|
40
|
+
for (const module of compilation.modules) {
|
|
41
|
+
if (!module.dependencies)
|
|
42
|
+
return;
|
|
43
|
+
for (const dep of module.dependencies) {
|
|
44
|
+
if (dep.constructor.name !== "URLDependency")
|
|
45
|
+
continue;
|
|
46
|
+
const dep_ = dep;
|
|
47
|
+
const key = `${module.identifier()}:${dep_.outerRange[0]}`;
|
|
48
|
+
const name = mappings.get(key);
|
|
49
|
+
if (!name)
|
|
50
|
+
continue;
|
|
51
|
+
const error = (msg) => {
|
|
52
|
+
const err = new compiler.webpack.WebpackError(msg);
|
|
53
|
+
err.module = name.module;
|
|
54
|
+
err.loc = name.loc;
|
|
55
|
+
compilation.errors.push(err);
|
|
56
|
+
};
|
|
57
|
+
const connection = compilation.moduleGraph.getConnection(dep);
|
|
58
|
+
if (!connection || !connection.module) {
|
|
59
|
+
error("unable to find connection for url dependency");
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const mod = connection.module;
|
|
63
|
+
const request = mod.resource ?? mod.userRequest;
|
|
64
|
+
if (!request) {
|
|
65
|
+
error(`unable to resolve unmap request`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
{
|
|
69
|
+
const oldV = nameToRequest.get(name.name);
|
|
70
|
+
if (oldV !== undefined && oldV !== request) {
|
|
71
|
+
error(`ResolutionConflict: unmap(${JSON.stringify(name.name)}, ${JSON.stringify(request)}) was previously resolved to ${oldV}, use a different unmapping`);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
nameToRequest.set(name.name, request);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
compilation.hooks.processAssets.tap(UnmapPlugin.name, (assets) => {
|
|
80
|
+
for (const [name, request] of nameToRequest.entries()) {
|
|
81
|
+
for (const [assetName, _asset] of Object.entries(assets)) {
|
|
82
|
+
const info = compilation.assetsInfo.get(assetName);
|
|
83
|
+
if (info?.sourceFilename && request.endsWith(info.sourceFilename)) {
|
|
84
|
+
nameToAsset.set(name, assetName);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const unmapping = Object.fromEntries(nameToAsset);
|
|
90
|
+
const json = JSON.stringify(unmapping);
|
|
91
|
+
compilation.emitAsset("mapped.json", new compiler.webpack.sources.RawSource(json));
|
|
92
|
+
compilation.unmapping = unmapping;
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
package/util.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function stripFileUrl(u: string): string;
|
|
2
|
+
export declare function resolveNpm(u: string): string;
|
|
3
|
+
export declare function manglePath(path: string): string;
|
|
4
|
+
export declare function assertAbsolutePath(path: string): void;
|
|
5
|
+
export declare function assertDirURL(path: URL): void;
|
|
6
|
+
export declare function ensureModPath(path: string): string;
|
|
7
|
+
export declare function ensureFilePath(path: string): string;
|
|
8
|
+
export declare function assertResolved(path: string): void;
|
|
9
|
+
export declare function ensureFileUrl(path: string): URL;
|
package/util.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { assert } from "@std/assert";
|
|
2
|
+
export function stripFileUrl(u) {
|
|
3
|
+
assert(u.startsWith("file://"), `not a file url: ${u}`);
|
|
4
|
+
return u.slice(7);
|
|
5
|
+
}
|
|
6
|
+
export function resolveNpm(u) {
|
|
7
|
+
const path = import.meta.resolve(`npm:${u}`);
|
|
8
|
+
return stripFileUrl(path);
|
|
9
|
+
}
|
|
10
|
+
export function manglePath(path) {
|
|
11
|
+
const tokens = path.replaceAll(/\\/g, "/").split(/([^a-zA-Z0-9_]+)/).filter((s) => s);
|
|
12
|
+
return "_" + tokens.map((s) => {
|
|
13
|
+
if (/^[a-zA-Z0-9_]+$/.test(s)) {
|
|
14
|
+
return s.length + s;
|
|
15
|
+
}
|
|
16
|
+
return s.split("").map((c) => "_" + c.charCodeAt(0).toString(16)).join("");
|
|
17
|
+
}).join("");
|
|
18
|
+
}
|
|
19
|
+
export function assertAbsolutePath(path) {
|
|
20
|
+
// TODO: Windows
|
|
21
|
+
assert(path.startsWith("/"), `path is not absolute: ${path}`);
|
|
22
|
+
}
|
|
23
|
+
export function assertDirURL(path) {
|
|
24
|
+
const stat = Deno.statSync(path);
|
|
25
|
+
assert(stat.isDirectory, `path is not a directory: ${path}`);
|
|
26
|
+
}
|
|
27
|
+
export function ensureModPath(path) {
|
|
28
|
+
if (path.endsWith("/fakeMod.ts"))
|
|
29
|
+
return path;
|
|
30
|
+
if (!path.startsWith("https://")) {
|
|
31
|
+
const stat = Deno.statSync(path);
|
|
32
|
+
if (stat.isFile)
|
|
33
|
+
return path;
|
|
34
|
+
assert(stat.isDirectory, `path is not a directory: ${path}`);
|
|
35
|
+
}
|
|
36
|
+
if (path.endsWith(".ts") || path.endsWith(".js") || path.endsWith(".mjs")) {
|
|
37
|
+
return path;
|
|
38
|
+
}
|
|
39
|
+
if (!path.endsWith("/"))
|
|
40
|
+
path += "/fakeMod.ts";
|
|
41
|
+
return path;
|
|
42
|
+
}
|
|
43
|
+
export function ensureFilePath(path) {
|
|
44
|
+
const stat = Deno.statSync(path);
|
|
45
|
+
assert(stat.isFile, `path is not a file: ${path}`);
|
|
46
|
+
assert(!path.endsWith("/"), "file paths shouldn't end with /");
|
|
47
|
+
return path;
|
|
48
|
+
}
|
|
49
|
+
export function assertResolved(path) {
|
|
50
|
+
assert(path.startsWith("https://"), `path is not https: ${path}`);
|
|
51
|
+
}
|
|
52
|
+
export function ensureFileUrl(path) {
|
|
53
|
+
return new URL(`file://${ensureFilePath(path)}`);
|
|
54
|
+
}
|
package/viteComment.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Compiler } from "webpack";
|
|
2
|
+
/**
|
|
3
|
+
* ViteIgnorePlugin skips parsing of `import` calls that contain `@vite-inore` in inline comment, similarly
|
|
4
|
+
* to webpack built-in `webpackIgnore: true` annotation
|
|
5
|
+
*/
|
|
6
|
+
export declare class ViteIgnorePlugin {
|
|
7
|
+
apply(compiler: Compiler): void;
|
|
8
|
+
}
|
package/viteComment.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const NAME = "ViteIgnorePlugin";
|
|
2
|
+
const PARSER_TYPES = [
|
|
3
|
+
"javascript/auto",
|
|
4
|
+
"javascript/dynamic",
|
|
5
|
+
"javascript/esm",
|
|
6
|
+
];
|
|
7
|
+
/**
|
|
8
|
+
* ViteIgnorePlugin skips parsing of `import` calls that contain `@vite-inore` in inline comment, similarly
|
|
9
|
+
* to webpack built-in `webpackIgnore: true` annotation
|
|
10
|
+
*/
|
|
11
|
+
export class ViteIgnorePlugin {
|
|
12
|
+
apply(compiler) {
|
|
13
|
+
compiler.hooks.compilation.tap(NAME, (_compilation, { normalModuleFactory }) => {
|
|
14
|
+
const handler = (parser) => {
|
|
15
|
+
parser.hooks.importCall.tap({ name: NAME, stage: -10 }, (expr) => {
|
|
16
|
+
const ignored = parser.getComments([
|
|
17
|
+
expr.range[0],
|
|
18
|
+
expr.source.range[1],
|
|
19
|
+
]).some((comment) => comment.type === "Block" &&
|
|
20
|
+
comment.value.includes("@vite-ignore"));
|
|
21
|
+
if (ignored)
|
|
22
|
+
return false;
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
for (const type of PARSER_TYPES) {
|
|
26
|
+
normalModuleFactory.hooks.parser.for(type).tap(NAME, handler);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
package/README.md
DELETED