clojurific 0.12.6 → 0.12.8
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/README.md +6 -6
- package/package.json +1 -1
- package/vite-plugin-cljs/index.js +89 -34
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ counts Clojurific's releases, starting from 1.
|
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
|
-
`npm create clojurific@latest` scaffolds a project (Vanilla, Reagent or
|
|
20
|
-
like the one below, installs it and starts the dev server.
|
|
19
|
+
`npm create clojurific@latest` scaffolds a project (Vanilla, Reagent, UIx, or
|
|
20
|
+
a Hono backend) like the one below, installs it and starts the dev server.
|
|
21
21
|
|
|
22
22
|
Dependencies and source paths come from the project's `deps.edn`:
|
|
23
23
|
|
|
@@ -28,10 +28,10 @@ Dependencies and source paths come from the project's `deps.edn`:
|
|
|
28
28
|
|
|
29
29
|
```js
|
|
30
30
|
// vite.config.mjs
|
|
31
|
-
import
|
|
31
|
+
import cljf from 'clojurific/vite';
|
|
32
32
|
|
|
33
33
|
export default {
|
|
34
|
-
plugins: [
|
|
34
|
+
plugins: [cljf({ outputDir: 'target/cljs' })],
|
|
35
35
|
};
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -57,7 +57,7 @@ npx cljf repl
|
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
`cljf repl` is a ClojureScript REPL evaluating in the pages the dev server
|
|
60
|
-
runs, through its nREPL server: `
|
|
60
|
+
runs, through its nREPL server: `cljf({ outputDir: 'target/cljs',
|
|
61
61
|
compilerOptions: { repl: { 'nrepl-port': 0 } } })` with `nrepl/nrepl` in
|
|
62
62
|
`deps.edn`.
|
|
63
63
|
|
|
@@ -87,7 +87,7 @@ npx cljf setup-java
|
|
|
87
87
|
| `CLJF_MAVEN_REPO` | a Maven Central mirror to download the dependency resolver from; other dependencies use `:mvn/repos` |
|
|
88
88
|
| `CLJF_CACHE_DIR` | where `setup-java` installs Java |
|
|
89
89
|
|
|
90
|
-
See [
|
|
90
|
+
See [README.md](https://github.com/WhimsicalCode/clojurific/blob/main/README.md) for
|
|
91
91
|
compiler options, the Vite plugin's options, the REPL and how the launcher
|
|
92
92
|
works.
|
|
93
93
|
|
package/package.json
CHANGED
|
@@ -247,12 +247,16 @@ export default module.exports;
|
|
|
247
247
|
* true
|
|
248
248
|
* @param {string[]} [options.entries] the :js-entries this build bundles,
|
|
249
249
|
* defaults to all; each is self-contained when it's the only one
|
|
250
|
+
* @param {string[]} [options.ssrEntries] :js-entries the dev server runs in
|
|
251
|
+
* its ssr environment once they're compiled, i.e. a server's (which a
|
|
252
|
+
* plugin like @hono/vite-dev-server imports from the same runner): the REPL
|
|
253
|
+
* evaluates in them before their first request
|
|
250
254
|
* @param {boolean} [options.replRemoteConsole] accept cljs_eval (forms the
|
|
251
255
|
* compiler compiles, running its macros) from pages on other hosts too,
|
|
252
256
|
* defaults to false: only from this machine. A proxy in front of the dev
|
|
253
257
|
* server must set X-Real-IP to the client's address.
|
|
254
258
|
*/
|
|
255
|
-
export default function
|
|
259
|
+
export default function cljf(options) {
|
|
256
260
|
const manifestName = options.manifest ?? 'manifest.json';
|
|
257
261
|
let config, proc, server, minify, root, cwd, outputDir, compiler;
|
|
258
262
|
// the namespaces of the pages' scripts, compiled as main namespaces
|
|
@@ -324,8 +328,8 @@ export default function cljs(options) {
|
|
|
324
328
|
} else if (event.type === 'compiled' || event.type === 'error') {
|
|
325
329
|
compiled();
|
|
326
330
|
const logger = config?.logger ?? console;
|
|
327
|
-
if (event.type === 'error') logger.error(`[
|
|
328
|
-
else logger.info(`[
|
|
331
|
+
if (event.type === 'error') logger.error(`[cljf] ${event.message}`);
|
|
332
|
+
else logger.info(`[cljf] compiled ${event.namespaces} namespace(s) in ${event.ms}ms`);
|
|
329
333
|
if (first && event.type === 'compiled') {
|
|
330
334
|
first = false;
|
|
331
335
|
resolve();
|
|
@@ -398,7 +402,7 @@ export default function cljs(options) {
|
|
|
398
402
|
}
|
|
399
403
|
|
|
400
404
|
return {
|
|
401
|
-
name: '
|
|
405
|
+
name: 'cljf',
|
|
402
406
|
// before Vite's resolver, which would take cljs-npm-as: specifiers
|
|
403
407
|
enforce: 'pre',
|
|
404
408
|
|
|
@@ -619,7 +623,7 @@ export default function cljs(options) {
|
|
|
619
623
|
this.warn('nothing was pruned: package.json has "type": "commonjs", so the bundler treats the project\'s ' +
|
|
620
624
|
'.js files as CommonJS and wraps the ClojureScript they import in initializers. Use "type": "module".');
|
|
621
625
|
}
|
|
622
|
-
config.logger.info(`[
|
|
626
|
+
config.logger.info(`[cljf] removed ${pruned.removed} unused statements (${pruneMs}ms), ` +
|
|
623
627
|
`renamed ${Object.keys(renames).length} properties, ` +
|
|
624
628
|
`minified (${Date.now() - start - pruneMs}ms)`);
|
|
625
629
|
}
|
|
@@ -682,56 +686,106 @@ export default function cljs(options) {
|
|
|
682
686
|
// since: a reloaded page runs the output with warnings
|
|
683
687
|
let problem = null;
|
|
684
688
|
const showProblem = err => {
|
|
685
|
-
problem = { type: 'error', err: { stack: '', plugin: '
|
|
689
|
+
problem = { type: 'error', err: { stack: '', plugin: 'cljf', ...err } };
|
|
686
690
|
server.ws.send(problem);
|
|
687
691
|
};
|
|
688
692
|
server.ws.on('connection', socket => problem && socket.send(JSON.stringify(problem)));
|
|
693
|
+
// options.ssrEntries run in the ssr environment's runner once compiled,
|
|
694
|
+
// until they ran
|
|
695
|
+
let ssrEntries = options.ssrEntries ?? [];
|
|
696
|
+
const runSsrEntries = async () => {
|
|
697
|
+
if (!ssrEntries.length) return;
|
|
698
|
+
// created when it's first read
|
|
699
|
+
const runner = server.environments.ssr?.runner;
|
|
700
|
+
if (!runner) return;
|
|
701
|
+
const info = await buildInfo();
|
|
702
|
+
for (const name of ssrEntries) {
|
|
703
|
+
const file = info?.entries?.[name];
|
|
704
|
+
if (!file) {
|
|
705
|
+
config.logger.error(`[cljf] ssrEntries: no :js-entries ${name}`, { timestamp: true });
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
try {
|
|
709
|
+
await runner.import(path.join(outputDir, file));
|
|
710
|
+
ssrEntries = ssrEntries.filter(n => n !== name);
|
|
711
|
+
} catch (e) {
|
|
712
|
+
config.logger.error(`[cljf] running ${name} (${file}): ${e.stack ?? e}`, { timestamp: true });
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
};
|
|
689
716
|
// The REPL (cljf.esm.repl): pages running the build (cljf.esm.repl-runtime)
|
|
690
|
-
// say hello over Vite's websocket,
|
|
691
|
-
//
|
|
692
|
-
//
|
|
717
|
+
// say hello over Vite's websocket, and modules the ssr environment's
|
|
718
|
+
// runner runs (a server's :js-entries) over its hot channel; each
|
|
719
|
+
// connection gets a runtime id. Their cljs:repl messages go to the
|
|
720
|
+
// compiler as `repl <json>` lines, the compiler's repl-send events to the
|
|
721
|
+
// runtime of their runtime id, runtimes[id](data).
|
|
693
722
|
const runtimes = new Map();
|
|
694
723
|
let lastRuntimeId = 0;
|
|
695
724
|
const toRepl = msg => proc.stdin.writable && proc.stdin.write(`repl ${JSON.stringify(msg)}\n`);
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
const consoleAllowed = options.replRemoteConsole || isLocalConnection(req);
|
|
725
|
+
// A runtime's connection: receive takes the data of its cljs:repl events
|
|
726
|
+
// for the REPL, close says bye. send replies to it, consoleAllowed whether
|
|
727
|
+
// it may cljs_eval.
|
|
728
|
+
const connectRuntime = (send, consoleAllowed) => {
|
|
701
729
|
let id;
|
|
702
|
-
|
|
703
|
-
let msg;
|
|
704
|
-
try {
|
|
705
|
-
msg = JSON.parse(String(raw));
|
|
706
|
-
} catch {
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
if (msg?.type !== 'custom' || msg.event !== 'cljs:repl' || !msg.data) return;
|
|
710
|
-
const data = msg.data;
|
|
730
|
+
const receive = data => {
|
|
711
731
|
if (data.op === 'hello' && id === undefined) {
|
|
712
732
|
id = ++lastRuntimeId;
|
|
713
|
-
runtimes.set(id,
|
|
733
|
+
runtimes.set(id, send);
|
|
714
734
|
}
|
|
715
735
|
if (id === undefined) return;
|
|
716
|
-
if (data.op === 'hello') send(
|
|
736
|
+
if (data.op === 'hello') send({ op: 'welcome', runtime: id });
|
|
717
737
|
if (data.op === 'console-eval' && !consoleAllowed) {
|
|
718
|
-
send(
|
|
738
|
+
send({
|
|
719
739
|
op: 'console-result',
|
|
720
740
|
rid: data.rid,
|
|
721
|
-
result: { error: 'cljs_eval only runs in pages on the dev server\'s machine, see the
|
|
741
|
+
result: { error: 'cljs_eval only runs in pages on the dev server\'s machine, see the cljf plugin\'s replRemoteConsole' },
|
|
722
742
|
});
|
|
723
743
|
return;
|
|
724
744
|
}
|
|
725
745
|
toRepl({ ...data, runtime: id });
|
|
726
|
-
}
|
|
727
|
-
|
|
746
|
+
};
|
|
747
|
+
const close = () => {
|
|
728
748
|
if (id !== undefined && runtimes.delete(id)) toRepl({ op: 'bye', runtime: id });
|
|
749
|
+
};
|
|
750
|
+
return { receive, close };
|
|
751
|
+
};
|
|
752
|
+
server.ws.on('connection', (socket, req) => {
|
|
753
|
+
// cljs_eval compiles forms in the compiler's JVM, which runs their macros:
|
|
754
|
+
// only for pages on this machine, unless replRemoteConsole
|
|
755
|
+
const runtime = connectRuntime(
|
|
756
|
+
data => socket.send(JSON.stringify({ type: 'custom', event: 'cljs:repl', data })),
|
|
757
|
+
options.replRemoteConsole || isLocalConnection(req));
|
|
758
|
+
socket.on('message', raw => {
|
|
759
|
+
let msg;
|
|
760
|
+
try {
|
|
761
|
+
msg = JSON.parse(String(raw));
|
|
762
|
+
} catch {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (msg?.type !== 'custom' || msg.event !== 'cljs:repl' || !msg.data) return;
|
|
766
|
+
runtime.receive(msg.data);
|
|
729
767
|
});
|
|
768
|
+
socket.on('close', runtime.close);
|
|
730
769
|
});
|
|
770
|
+
// the ssr runner's modules run in this process, each runner is a client
|
|
771
|
+
// of the environment's hot channel
|
|
772
|
+
const ssrHot = server.environments.ssr?.hot;
|
|
773
|
+
if (ssrHot) {
|
|
774
|
+
const ssrRuntimes = new Map();
|
|
775
|
+
const ssrRuntime = client => {
|
|
776
|
+
if (!ssrRuntimes.has(client)) ssrRuntimes.set(client, connectRuntime(data => client.send('cljs:repl', data), true));
|
|
777
|
+
return ssrRuntimes.get(client);
|
|
778
|
+
};
|
|
779
|
+
ssrHot.on('cljs:repl', (data, client) => data && ssrRuntime(client).receive(data));
|
|
780
|
+
ssrHot.on('vite:client:disconnect', (data, client) => {
|
|
781
|
+
ssrRuntimes.get(client)?.close();
|
|
782
|
+
ssrRuntimes.delete(client);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
731
785
|
proc = run(compilerArgs('watch', { ':esm-hmr': true, ':exit-with-parent': true, ':watch-events': ':stdin' }), async event => {
|
|
732
786
|
if (event.type === 'repl-send') {
|
|
733
|
-
const
|
|
734
|
-
if (
|
|
787
|
+
const send = runtimes.get(event.runtime);
|
|
788
|
+
if (send) send(event.msg);
|
|
735
789
|
else toRepl({ op: 'bye', runtime: event.runtime });
|
|
736
790
|
return;
|
|
737
791
|
}
|
|
@@ -775,7 +829,7 @@ export default function cljs(options) {
|
|
|
775
829
|
const outstanding = [...warnings.values()].flat();
|
|
776
830
|
if (event.type === 'compiled' && outstanding.length) {
|
|
777
831
|
const [first] = outstanding;
|
|
778
|
-
config.logger.warn(`[
|
|
832
|
+
config.logger.warn(`[cljf] compiled ${event.namespaces} namespace(s) in ${event.ms}ms, ` +
|
|
779
833
|
`hot reload paused by ${outstanding.length} warning(s)`, { timestamp: true });
|
|
780
834
|
showProblem({
|
|
781
835
|
message: outstanding
|
|
@@ -786,16 +840,17 @@ export default function cljs(options) {
|
|
|
786
840
|
});
|
|
787
841
|
ready();
|
|
788
842
|
} else if (event.type === 'compiled') {
|
|
789
|
-
config.logger.info(`[
|
|
843
|
+
config.logger.info(`[cljf] compiled ${event.namespaces} namespace(s) in ${event.ms}ms`, { timestamp: true });
|
|
790
844
|
problem = null;
|
|
791
845
|
// the namespaces held back that this compile didn't update
|
|
792
846
|
const client = server.environments.client;
|
|
793
847
|
for (const file of released) {
|
|
794
848
|
for (const mod of client.moduleGraph.getModulesByFile(file) ?? []) client.reloadModule(mod);
|
|
795
849
|
}
|
|
850
|
+
runSsrEntries();
|
|
796
851
|
ready();
|
|
797
852
|
} else if (event.type === 'error') {
|
|
798
|
-
config.logger.error(`[
|
|
853
|
+
config.logger.error(`[cljf] ${event.message}`, { timestamp: true });
|
|
799
854
|
showProblem({
|
|
800
855
|
message: event.message,
|
|
801
856
|
id: event.file,
|