@swmansion/popcorn 0.3.2 → 0.4.0-next.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/LICENSE +1 -1
- package/NOTICE +12 -0
- package/README.md +78 -2
- package/dist/beam.d.ts +10 -0
- package/dist/errors.d.ts +96 -39
- package/dist/etf.d.ts +38 -0
- package/dist/events.d.ts +82 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.mjs +1287 -2
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/beam_patcher.ex +184 -0
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/cli.ex +74 -0
- package/dist/plugins/beam_tools/lib/popcorn/beam_tools/packager.ex +540 -0
- package/dist/plugins/beam_tools/mix.exs +16 -0
- package/dist/plugins/beam_tools/patches/kernel/prim_tty.erl +13 -0
- package/dist/plugins/beam_tools/patches/stdlib/beam_lib.erl +27 -0
- package/dist/plugins/esbuild.d.ts +10 -2
- package/dist/plugins/esbuild.mjs +39 -34
- package/dist/plugins/rollup.d.ts +10 -2
- package/dist/plugins/rollup.mjs +46 -25
- package/dist/plugins/shared.d.ts +54 -4
- package/dist/plugins/shared.mjs +207 -0
- package/dist/plugins/vite.d.ts +17 -2
- package/dist/plugins/vite.mjs +201 -75
- package/dist/popcorn.d.ts +237 -110
- package/dist/runtimes/core/beam.emu.mjs +141 -0
- package/dist/runtimes/core/beam.mjs +141 -0
- package/dist/runtimes/core/beam.wasm +0 -0
- package/dist/runtimes/core/manifest.json +1 -0
- package/dist/runtimes/crypto/beam.emu.mjs +520 -0
- package/dist/runtimes/crypto/beam.mjs +520 -0
- package/dist/runtimes/crypto/beam.wasm +0 -0
- package/dist/runtimes/crypto/manifest.json +1 -0
- package/dist/tar.d.ts +4 -0
- package/dist/types.d.ts +108 -63
- package/dist/utils.d.ts +7 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.mjs +765 -0
- package/package.json +20 -28
- package/dist/AtomVM.mjs +0 -7992
- package/dist/AtomVM.wasm +0 -0
- package/dist/bridge.d.ts +0 -22
- package/dist/bridge.mjs +0 -66
- package/dist/errors.mjs +0 -55
- package/dist/iframe.d.ts +0 -1
- package/dist/iframe.mjs +0 -215
- package/dist/popcorn.mjs +0 -381
- package/dist/types.mjs +0 -25
package/dist/plugins/vite.mjs
CHANGED
|
@@ -1,101 +1,227 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname,
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
1
|
+
import { mkdir, cp, rm, readFile } from 'node:fs/promises';
|
|
2
|
+
import { resolve, dirname, join, relative, isAbsolute } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { r as runtimeDirectory, p as popcorn$1 } from './shared.mjs';
|
|
5
|
+
import 'node:child_process';
|
|
6
|
+
import 'node:os';
|
|
7
|
+
import 'node:util';
|
|
8
|
+
import 'node:zlib';
|
|
4
9
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
const CORS_HEADERS = {
|
|
11
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
|
12
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
13
|
+
};
|
|
14
|
+
// Plugin is at dist/plugins/vite.mjs, dist/ is one level up.
|
|
15
|
+
const distDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
16
|
+
/**
|
|
17
|
+
* Serves application assets during developmens.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { defineConfig } from "vite";
|
|
22
|
+
* import { popcorn } from "@swmansion/popcorn/vite";
|
|
23
|
+
*
|
|
24
|
+
* export default defineConfig({
|
|
25
|
+
* plugins: [popcorn({ rootDir: "../my_app", app: "my_app" })],
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @see {@link Options} for production server requirements.
|
|
30
|
+
*/
|
|
8
31
|
function popcorn(options) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
32
|
+
const resolveRuntime = async (source, importer) => {
|
|
33
|
+
if (importer !== undefined &&
|
|
34
|
+
resolve(dirname(importer.split("?")[0]), source) ===
|
|
35
|
+
join(distDir, "beam.mjs")) {
|
|
36
|
+
const { runtimeVariant } = await ensurePrepared();
|
|
37
|
+
const runtimeDir = runtimeDirectory(runtimeVariant);
|
|
38
|
+
return join(runtimeDir, "beam.mjs");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
let prepared;
|
|
42
|
+
let repacking;
|
|
43
|
+
let dirty = false;
|
|
44
|
+
let outDir;
|
|
45
|
+
let assetsDir = "assets";
|
|
46
|
+
// Dev/preview: pack once at first request, re-pack lazily after the app's
|
|
47
|
+
// compiled beams change. A single in-flight promise dedupes concurrent
|
|
48
|
+
// requests; a change during a re-pack re-sets `dirty` so it isn't lost.
|
|
49
|
+
const ensurePrepared = () => {
|
|
50
|
+
if (prepared !== undefined && !dirty)
|
|
51
|
+
return Promise.resolve(prepared);
|
|
52
|
+
if (repacking === undefined) {
|
|
53
|
+
dirty = false;
|
|
54
|
+
repacking = popcorn$1(options)
|
|
55
|
+
.then(async (next) => {
|
|
56
|
+
if (prepared !== undefined) {
|
|
57
|
+
await rm(prepared.dir, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
prepared = next;
|
|
60
|
+
return next;
|
|
61
|
+
})
|
|
62
|
+
.finally(() => {
|
|
63
|
+
repacking = undefined;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return repacking;
|
|
67
|
+
};
|
|
68
|
+
const cleanup = () => {
|
|
69
|
+
if (prepared !== undefined) {
|
|
70
|
+
rm(prepared.dir, { recursive: true, force: true });
|
|
71
|
+
prepared = undefined;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const serve = async (req, res, next) => {
|
|
75
|
+
const url = req.url;
|
|
76
|
+
if (url === undefined) {
|
|
77
|
+
next();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const requestPath = decodeURIComponent(new URL(url, "http://localhost").pathname);
|
|
81
|
+
const requestRoot = [`/${assetsDir}/otp/`, `/@fs${distDir}/otp/`].find((root) => requestPath.startsWith(root));
|
|
82
|
+
if (requestRoot === undefined) {
|
|
83
|
+
next();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let dir;
|
|
87
|
+
try {
|
|
88
|
+
({ dir } = await ensurePrepared());
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
next(error);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const assetsRoot = join(dir, "otp");
|
|
95
|
+
const filePath = resolve(assetsRoot, requestPath.slice(requestRoot.length));
|
|
96
|
+
if (!isUnder(assetsRoot, filePath)) {
|
|
97
|
+
res.statusCode = 403;
|
|
98
|
+
setHeaders(res);
|
|
99
|
+
res.end("Forbidden");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const compressible = isCompressible(filePath);
|
|
104
|
+
const encoding = compressible
|
|
105
|
+
? selectEncoding(req.headers["accept-encoding"]?.toString(), options.brotli ?? false)
|
|
106
|
+
: { name: null, suffix: "" };
|
|
107
|
+
if (compressible && encoding.name === null) {
|
|
108
|
+
res.statusCode = 406;
|
|
109
|
+
setHeaders(res);
|
|
110
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
111
|
+
res.end("No supported content encoding");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const content = await readFile(`${filePath}${encoding.suffix}`);
|
|
115
|
+
setHeaders(res);
|
|
116
|
+
setContentType(res, filePath);
|
|
117
|
+
res.setHeader("Vary", "Accept-Encoding");
|
|
118
|
+
if (encoding.name !== null) {
|
|
119
|
+
res.setHeader("Content-Encoding", encoding.name);
|
|
120
|
+
}
|
|
121
|
+
res.end(content);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
res.statusCode = 404;
|
|
125
|
+
setHeaders(res);
|
|
126
|
+
res.end("Not found");
|
|
127
|
+
}
|
|
128
|
+
};
|
|
16
129
|
return {
|
|
17
|
-
name: "popcorn",
|
|
130
|
+
name: "popcorn-otp",
|
|
131
|
+
enforce: "pre",
|
|
132
|
+
resolveId: resolveRuntime,
|
|
18
133
|
config() {
|
|
19
134
|
return {
|
|
20
|
-
// Exclude
|
|
21
|
-
optimizeDeps: {
|
|
22
|
-
|
|
135
|
+
// Exclude from prebundling so import.meta.url resolves correctly.
|
|
136
|
+
optimizeDeps: { exclude: ["@swmansion/popcorn"] },
|
|
137
|
+
worker: {
|
|
138
|
+
format: "es",
|
|
139
|
+
plugins: () => [
|
|
140
|
+
{
|
|
141
|
+
name: "popcorn-runtime",
|
|
142
|
+
enforce: "pre",
|
|
143
|
+
resolveId: resolveRuntime,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
23
146
|
},
|
|
147
|
+
// COOP/COEP must be on every response (SharedArrayBuffer/pthreads),
|
|
148
|
+
// including the worker/beam files Vite serves from the package itself.
|
|
149
|
+
server: { headers: CORS_HEADERS },
|
|
150
|
+
preview: { headers: CORS_HEADERS },
|
|
24
151
|
};
|
|
25
152
|
},
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (result.status === "rejected") {
|
|
30
|
-
this.error(`[popcorn] Bundle doesn't exist at '${bundles[i].path}'`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
config.server.fs.allow.push(popcornDistDir);
|
|
153
|
+
configResolved(config) {
|
|
154
|
+
config.server.fs.allow.push(distDir);
|
|
155
|
+
outDir = resolve(config.root, config.build.outDir);
|
|
34
156
|
assetsDir = config.build.assetsDir;
|
|
35
157
|
},
|
|
36
158
|
configureServer(server) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (served)
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
next();
|
|
159
|
+
const rootDir = resolve(options.rootDir);
|
|
160
|
+
server.watcher.add(rootDir);
|
|
161
|
+
server.watcher.on("all", (_event, file) => {
|
|
162
|
+
if (isUnder(rootDir, file))
|
|
163
|
+
dirty = true;
|
|
46
164
|
});
|
|
165
|
+
server.middlewares.use(serve);
|
|
166
|
+
server.httpServer?.once("close", cleanup);
|
|
47
167
|
},
|
|
48
168
|
configurePreviewServer(server) {
|
|
49
|
-
server.middlewares.use(
|
|
50
|
-
|
|
51
|
-
for (const bundle of bundles) {
|
|
52
|
-
const opts = { bundleUrl: bundle.url, bundlePath: bundle.path };
|
|
53
|
-
const served = await serveAvmBundle(req, res, opts);
|
|
54
|
-
if (served)
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
next();
|
|
58
|
-
});
|
|
169
|
+
server.middlewares.use(serve);
|
|
170
|
+
server.httpServer?.once("close", cleanup);
|
|
59
171
|
},
|
|
60
|
-
async
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
172
|
+
async closeBundle() {
|
|
173
|
+
assert(outDir !== undefined, "outDir was not resolved");
|
|
174
|
+
const built = await ensurePrepared();
|
|
175
|
+
try {
|
|
176
|
+
const targetDir = join(outDir, assetsDir, "otp");
|
|
177
|
+
await mkdir(dirname(targetDir), { recursive: true });
|
|
178
|
+
await cp(join(built.dir, "otp"), targetDir, { recursive: true });
|
|
68
179
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.emitFile({
|
|
73
|
-
type: "asset",
|
|
74
|
-
fileName: `${assetsDir}/${name}`,
|
|
75
|
-
source: await readFile(resolve(popcornDistDir, name)),
|
|
76
|
-
});
|
|
180
|
+
finally {
|
|
181
|
+
await rm(built.dir, { recursive: true, force: true });
|
|
182
|
+
prepared = undefined;
|
|
77
183
|
}
|
|
78
184
|
},
|
|
79
185
|
};
|
|
80
186
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return true;
|
|
88
|
-
}
|
|
89
|
-
return false;
|
|
187
|
+
function isCompressible(path) {
|
|
188
|
+
return path.endsWith(".tar") || path.endsWith(".wasm");
|
|
189
|
+
}
|
|
190
|
+
function selectEncoding(header, useBrotli) {
|
|
191
|
+
if (useBrotli && header?.includes("br")) {
|
|
192
|
+
return { name: "br", suffix: ".br" };
|
|
90
193
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
194
|
+
if (header?.includes("gzip"))
|
|
195
|
+
return { name: "gzip", suffix: ".gz" };
|
|
196
|
+
return { name: null, suffix: "" };
|
|
197
|
+
}
|
|
198
|
+
function isUnder(dir, file) {
|
|
199
|
+
const rel = relative(dir, file);
|
|
200
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
201
|
+
}
|
|
202
|
+
function setHeaders(res) {
|
|
203
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
204
|
+
res.setHeader(key, value);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function setContentType(res, path) {
|
|
208
|
+
if (path.endsWith(".mjs")) {
|
|
209
|
+
res.setHeader("Content-Type", "text/javascript");
|
|
210
|
+
}
|
|
211
|
+
else if (path.endsWith(".wasm")) {
|
|
212
|
+
res.setHeader("Content-Type", "application/wasm");
|
|
213
|
+
}
|
|
214
|
+
else if (path.endsWith(".json")) {
|
|
215
|
+
res.setHeader("Content-Type", "application/json");
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
res.setHeader("Content-Type", "application/octet-stream");
|
|
94
219
|
}
|
|
95
220
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
221
|
+
function assert(ok, message) {
|
|
222
|
+
if (!ok) {
|
|
223
|
+
throw new Error(`[popcorn-otp] ${message}`);
|
|
224
|
+
}
|
|
99
225
|
}
|
|
100
226
|
|
|
101
227
|
export { popcorn };
|