capybara-simulated 0.0.2
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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +225 -0
- data/lib/capybara/simulated/browser.rb +1012 -0
- data/lib/capybara/simulated/driver.rb +191 -0
- data/lib/capybara/simulated/errors.rb +9 -0
- data/lib/capybara/simulated/node.rb +235 -0
- data/lib/capybara/simulated/version.rb +5 -0
- data/lib/capybara/simulated.rb +10 -0
- data/lib/capybara-simulated.rb +1 -0
- data/vendor/esbuild-wasm/LICENSE.md +21 -0
- data/vendor/esbuild-wasm/bin/esbuild +91 -0
- data/vendor/esbuild-wasm/esbuild.wasm +0 -0
- data/vendor/esbuild-wasm/lib/main.js +2337 -0
- data/vendor/esbuild-wasm/wasm_exec.js +575 -0
- data/vendor/esbuild-wasm/wasm_exec_node.js +40 -0
- data/vendor/js/bundle-modules.mjs +168 -0
- data/vendor/js/csim.bundle.js +101015 -0
- data/vendor/js/entry.mjs +8 -0
- data/vendor/js/prelude.js +186 -0
- data/vendor/js/runtime.js +2054 -0
- metadata +106 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Driver for on-the-fly bundling of a Rails-style importmap'd page.
|
|
2
|
+
//
|
|
3
|
+
// Reads a JSON payload on stdin describing an importmap, the entry
|
|
4
|
+
// scripts (inline source and/or URLs), and a virtual filesystem mapping
|
|
5
|
+
// resolved-URL → source. Resolves bare specifiers via the importmap,
|
|
6
|
+
// follows relative imports, and produces a single IIFE bundle on stdout.
|
|
7
|
+
//
|
|
8
|
+
// Ruby is responsible for pre-fetching all reachable URLs and handing
|
|
9
|
+
// them in via `sources`. Anything not in `sources` is treated as a
|
|
10
|
+
// missing module and aborts the build with a descriptive error so the
|
|
11
|
+
// caller can surface it without a silent partial bundle.
|
|
12
|
+
//
|
|
13
|
+
// Dynamic `import(specifier)` is rewritten to `globalThis.__csim_import`
|
|
14
|
+
// at load-time, and every importmap entry is statically pre-bundled
|
|
15
|
+
// into a `globalThis.__csim_modules` registry so the rewritten lookup
|
|
16
|
+
// can resolve synchronously. mini_racer has no real ES module loader,
|
|
17
|
+
// so without this rewrite, libraries like stimulus-loading silently
|
|
18
|
+
// fail to register controllers.
|
|
19
|
+
//
|
|
20
|
+
// Payload shape:
|
|
21
|
+
// {
|
|
22
|
+
// "importmap": { "imports": { "spec": "url", ... } },
|
|
23
|
+
// "baseUrl": "http://www.example.com/",
|
|
24
|
+
// "entries": [ {"inline": "import 'application'"}, {"src": "/x.js"} ],
|
|
25
|
+
// "sources": { "http://...": "...source..." }
|
|
26
|
+
// }
|
|
27
|
+
//
|
|
28
|
+
// On success: the bundle text on stdout, exit 0.
|
|
29
|
+
// On failure: a JSON `{error: ...}` on stderr, exit 1.
|
|
30
|
+
|
|
31
|
+
// We import the vendored esbuild-wasm so the published gem doesn't need
|
|
32
|
+
// a runtime `npm install` step. Native esbuild is used at gem build time
|
|
33
|
+
// only — see build.mjs. The Node entry (`lib/main.js`) requires its own
|
|
34
|
+
// directory to be named `lib` and looks for sibling `bin/esbuild` +
|
|
35
|
+
// `esbuild.wasm`, so the import path has to follow that layout.
|
|
36
|
+
import * as esbuild from '../esbuild-wasm/lib/main.js';
|
|
37
|
+
await esbuild.initialize({});
|
|
38
|
+
|
|
39
|
+
async function readStdin() {
|
|
40
|
+
let data = '';
|
|
41
|
+
for await (const chunk of process.stdin) data += chunk;
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveBare(importmap, spec) {
|
|
46
|
+
const imports = (importmap && importmap.imports) || {};
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(imports, spec)) return imports[spec];
|
|
48
|
+
for (const k of Object.keys(imports)) {
|
|
49
|
+
if (k.endsWith('/') && spec.startsWith(k)) {
|
|
50
|
+
return imports[k] + spec.slice(k.length);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isUrl(s) {
|
|
57
|
+
return /^https?:\/\//.test(s);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveSpec(spec, importer, importmap, baseUrl) {
|
|
61
|
+
if (isUrl(spec)) return spec;
|
|
62
|
+
if (spec.startsWith('/')) return new URL(spec, baseUrl).href;
|
|
63
|
+
if (spec.startsWith('./') || spec.startsWith('../')) {
|
|
64
|
+
return new URL(spec, importer || baseUrl).href;
|
|
65
|
+
}
|
|
66
|
+
const mapped = resolveBare(importmap, spec);
|
|
67
|
+
if (mapped) return resolveSpec(mapped, importer, importmap, baseUrl);
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const payload = JSON.parse(await readStdin());
|
|
72
|
+
const {importmap = {}, baseUrl, entries = [], sources = {}} = payload;
|
|
73
|
+
const NS = 'csim-rack';
|
|
74
|
+
|
|
75
|
+
// Rewriting `import(...)` → `globalThis.__csim_import(...)`. The regex
|
|
76
|
+
// avoids matching `import` statements (followed by whitespace/quote/name)
|
|
77
|
+
// by requiring the next non-space character to be `(`. Comments and
|
|
78
|
+
// strings can produce false positives in pathological code, but the
|
|
79
|
+
// pattern is conservative enough for the conventions used by Turbo,
|
|
80
|
+
// Stimulus, stimulus-loading, and the Rails import-map ecosystem.
|
|
81
|
+
const DYNAMIC_IMPORT_RX = /\bimport\s*\(/g;
|
|
82
|
+
|
|
83
|
+
function rewriteDynamicImports(source) {
|
|
84
|
+
return source.replace(DYNAMIC_IMPORT_RX, 'globalThis.__csim_import(');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Modules listed in the importmap that should be pre-bundled even when
|
|
88
|
+
// no static `import` reaches them, so dynamic `import("controllers/...")`
|
|
89
|
+
// can find them. Skip aliases that don't have a corresponding source —
|
|
90
|
+
// e.g. importmap pins to a CDN URL we never fetched.
|
|
91
|
+
function preloadEntries(importmap, sources, baseUrl) {
|
|
92
|
+
const imports = (importmap && importmap.imports) || {};
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const spec of Object.keys(imports)) {
|
|
95
|
+
if (spec.endsWith('/')) continue;
|
|
96
|
+
const resolved = resolveSpec(spec, baseUrl, importmap, baseUrl);
|
|
97
|
+
if (!resolved) continue;
|
|
98
|
+
if (!Object.prototype.hasOwnProperty.call(sources, resolved)) continue;
|
|
99
|
+
out.push({spec, resolved});
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const preload = preloadEntries(importmap, sources, baseUrl);
|
|
105
|
+
// Statically `import * as` each importmap entry so esbuild bundles them
|
|
106
|
+
// all, then register under the original specifier. The runtime side
|
|
107
|
+
// installs `globalThis.__csim_modules` and `__csim_import` *before* the
|
|
108
|
+
// bundle evaluates, so the registry already exists by the time these
|
|
109
|
+
// lines execute. (Import declarations hoist above any executable code,
|
|
110
|
+
// so we can't define the registry from within the bundle itself.)
|
|
111
|
+
const preludeLines = [];
|
|
112
|
+
preload.forEach((p, i) => {
|
|
113
|
+
const local = `__csim_m_${i}`;
|
|
114
|
+
preludeLines.push(`import * as ${local} from ${JSON.stringify(p.spec)};`);
|
|
115
|
+
});
|
|
116
|
+
preload.forEach((p, i) => {
|
|
117
|
+
const local = `__csim_m_${i}`;
|
|
118
|
+
preludeLines.push(`globalThis.__csim_modules[${JSON.stringify(p.spec)}] = ${local};`);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const userEntry = entries.map((e) => {
|
|
122
|
+
if (e.src) return `import ${JSON.stringify(e.src)};`;
|
|
123
|
+
if (e.inline != null) return e.inline;
|
|
124
|
+
return '';
|
|
125
|
+
}).join('\n');
|
|
126
|
+
|
|
127
|
+
const entrySource = preludeLines.join('\n') + '\n' + userEntry;
|
|
128
|
+
|
|
129
|
+
const plugin = {
|
|
130
|
+
name: 'csim-rack',
|
|
131
|
+
setup(build) {
|
|
132
|
+
build.onResolve({filter: /.*/}, (args) => {
|
|
133
|
+
if (args.kind === 'entry-point') return null;
|
|
134
|
+
const importer = args.importer && args.namespace === NS ? args.importer : baseUrl;
|
|
135
|
+
const resolved = resolveSpec(args.path, importer, importmap, baseUrl);
|
|
136
|
+
if (!resolved) {
|
|
137
|
+
return {errors: [{text: `unresolved import "${args.path}" from ${args.importer || '<entry>'}`}]};
|
|
138
|
+
}
|
|
139
|
+
if (!Object.prototype.hasOwnProperty.call(sources, resolved)) {
|
|
140
|
+
return {errors: [{text: `module not pre-fetched: ${resolved} (imported by ${args.importer || '<entry>'})`}]};
|
|
141
|
+
}
|
|
142
|
+
return {path: resolved, namespace: NS};
|
|
143
|
+
});
|
|
144
|
+
build.onLoad({filter: /.*/, namespace: NS}, (args) => {
|
|
145
|
+
return {contents: rewriteDynamicImports(sources[args.path]), loader: 'js'};
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const result = await esbuild.build({
|
|
152
|
+
stdin: {contents: entrySource, resolveDir: '/', loader: 'js'},
|
|
153
|
+
bundle: true,
|
|
154
|
+
format: 'iife',
|
|
155
|
+
target: 'es2022',
|
|
156
|
+
write: false,
|
|
157
|
+
plugins: [plugin],
|
|
158
|
+
logLevel: 'silent'
|
|
159
|
+
});
|
|
160
|
+
if (result.errors && result.errors.length) {
|
|
161
|
+
process.stderr.write(JSON.stringify({error: result.errors.map((e) => e.text).join('; ')}));
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
process.stdout.write(result.outputFiles[0].text);
|
|
165
|
+
} catch (e) {
|
|
166
|
+
process.stderr.write(JSON.stringify({error: String((e && e.message) || e)}));
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|