@opendatacapture/serve-instrument 0.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/.turbo/turbo-lint.log +5 -0
- package/dist/cli.js +1 -0
- package/package.json +30 -0
- package/scripts/build.js +14 -0
- package/src/cli.ts +100 -0
- package/src/client/index.html +12 -0
- package/src/client/main.tsx +43 -0
- package/tsconfig.json +11 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";import*as s from"node:fs";import*as a from"node:path";import{bundle as l,BUNDLER_FILE_EXT_REGEX as u,inferLoader as f}from"@opendatacapture/instrument-bundler";import{encodeUnicodeToBase64 as h}from"@opendatacapture/runtime-internal";import w from"@opendatacapture/vite-plugin-runtime";import v from"@tailwindcss/vite";import{Command as y,InvalidArgumentError as p}from"commander";import{createServer as b}from"vite";import{name as E,version as B}from"../package.json";const r=new y;r.name(E),r.version(B),r.allowExcessArguments(!1),r.argument("<target>","the directory containing the instrument",e=>{if(e=a.resolve(e),s.existsSync(e)){if(!s.lstatSync(e).isDirectory())throw new p("Not a directory")}else throw new p("Directory does not exist");return e}),r.option("-f --force","force dependency reoptimization"),r.option("-p --port <number>","the port to run the dev server on",e=>{const i=parseInt(e,10);if(Number.isNaN(i))throw new p(`Not a number: ${e}`);return i},3e3),r.action(async e=>{const{force:i,port:c}=r.opts(),d=async()=>{const t=[],m=await s.promises.readdir(e,"utf-8").then(o=>o.filter(n=>u.test(n))).then(o=>o.map(n=>a.resolve(e,n)));for(const o of m){const n=f(o);t.push({content:await s.promises.readFile(o,n==="dataurl"?null:"utf-8"),name:a.basename(o)})}return h(await l({inputs:t}))};await(await b({forceOptimizeDeps:i,plugins:[{configureServer:t=>{t.watcher.add(e)},async handleHotUpdate({file:t,server:m}){t.startsWith(e)&&m.ws.send({data:{encodedBundle:await d()},event:"update-bundle",type:"custom"})},name:"serve-instrument",async transformIndexHtml(t){return t.replace("{{BUNDLE}}",await d())}},w(),v()],root:a.join(import.meta.dirname,"client"),server:{open:!1,port:c}})).listen(),console.log(`Listening on http://localhost:${c}`)}),r.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opendatacapture/serve-instrument",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"bin": {
|
|
7
|
+
"serve-instrument": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "node scripts/build.js",
|
|
11
|
+
"format": "prettier --write src",
|
|
12
|
+
"lint": "tsc && eslint --fix src"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@douglasneuroinformatics/libui": "catalog:",
|
|
16
|
+
"@opendatacapture/instrument-bundler": "workspace:",
|
|
17
|
+
"@opendatacapture/react-core": "workspace:*",
|
|
18
|
+
"@opendatacapture/runtime-internal": "workspace:*",
|
|
19
|
+
"@opendatacapture/runtime-v1": "workspace:*",
|
|
20
|
+
"@opendatacapture/vite-plugin-runtime": "workspace:*",
|
|
21
|
+
"@tailwindcss/vite": "catalog:",
|
|
22
|
+
"commander": "^12.1.0",
|
|
23
|
+
"esbuild": "catalog:",
|
|
24
|
+
"react": "workspace:react__19.x@*",
|
|
25
|
+
"react-dom": "workspace:react-dom__19.x@*",
|
|
26
|
+
"tailwindcss": "catalog:",
|
|
27
|
+
"vite": "catalog:",
|
|
28
|
+
"zod": "workspace:zod__3.x@*"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
import * as esbuild from 'esbuild';
|
|
5
|
+
|
|
6
|
+
const outdir = path.resolve(import.meta.dirname, '../dist');
|
|
7
|
+
|
|
8
|
+
await fs.promises.rm(outdir, { force: true, recursive: true });
|
|
9
|
+
|
|
10
|
+
await esbuild.build({
|
|
11
|
+
entryPoints: [path.resolve(import.meta.dirname, '../src/cli.ts')],
|
|
12
|
+
minify: true,
|
|
13
|
+
outdir
|
|
14
|
+
});
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
|
|
6
|
+
import { bundle, BUNDLER_FILE_EXT_REGEX, inferLoader } from '@opendatacapture/instrument-bundler';
|
|
7
|
+
import type { BundlerInput } from '@opendatacapture/instrument-bundler';
|
|
8
|
+
import { encodeUnicodeToBase64 } from '@opendatacapture/runtime-internal';
|
|
9
|
+
import runtime from '@opendatacapture/vite-plugin-runtime';
|
|
10
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
11
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
12
|
+
import { createServer } from 'vite';
|
|
13
|
+
|
|
14
|
+
import { name, version } from '../package.json';
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
program.name(name);
|
|
18
|
+
program.version(version);
|
|
19
|
+
program.allowExcessArguments(false);
|
|
20
|
+
program.argument('<target>', 'the directory containing the instrument', (target: string) => {
|
|
21
|
+
target = path.resolve(target);
|
|
22
|
+
if (!fs.existsSync(target)) {
|
|
23
|
+
throw new InvalidArgumentError('Directory does not exist');
|
|
24
|
+
} else if (!fs.lstatSync(target).isDirectory()) {
|
|
25
|
+
throw new InvalidArgumentError('Not a directory');
|
|
26
|
+
}
|
|
27
|
+
return target;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
program.option('-f --force', 'force dependency reoptimization');
|
|
31
|
+
|
|
32
|
+
program.option(
|
|
33
|
+
'-p --port <number>',
|
|
34
|
+
'the port to run the dev server on',
|
|
35
|
+
(value) => {
|
|
36
|
+
const port = parseInt(value, 10);
|
|
37
|
+
if (Number.isNaN(port)) {
|
|
38
|
+
throw new InvalidArgumentError(`Not a number: ${value}`);
|
|
39
|
+
}
|
|
40
|
+
return port;
|
|
41
|
+
},
|
|
42
|
+
3000
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
program.action(async (target: string) => {
|
|
46
|
+
const { force, port } = program.opts<{ force?: boolean; port: number }>();
|
|
47
|
+
|
|
48
|
+
const getEncodedBundle = async () => {
|
|
49
|
+
const inputs: BundlerInput[] = [];
|
|
50
|
+
const filepaths = await fs.promises
|
|
51
|
+
.readdir(target, 'utf-8')
|
|
52
|
+
.then((filenames) => filenames.filter((filename) => BUNDLER_FILE_EXT_REGEX.test(filename)))
|
|
53
|
+
.then((filenames) => filenames.map((filename) => path.resolve(target, filename)));
|
|
54
|
+
for (const filepath of filepaths) {
|
|
55
|
+
const loader = inferLoader(filepath);
|
|
56
|
+
inputs.push({
|
|
57
|
+
content: await fs.promises.readFile(filepath, loader === 'dataurl' ? null : 'utf-8'),
|
|
58
|
+
name: path.basename(filepath)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return encodeUnicodeToBase64(await bundle({ inputs }));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const server = await createServer({
|
|
65
|
+
forceOptimizeDeps: force,
|
|
66
|
+
plugins: [
|
|
67
|
+
{
|
|
68
|
+
configureServer: (server): void => {
|
|
69
|
+
server.watcher.add(target);
|
|
70
|
+
},
|
|
71
|
+
async handleHotUpdate({ file, server }) {
|
|
72
|
+
if (file.startsWith(target)) {
|
|
73
|
+
server.ws.send({
|
|
74
|
+
data: {
|
|
75
|
+
encodedBundle: await getEncodedBundle()
|
|
76
|
+
},
|
|
77
|
+
event: 'update-bundle',
|
|
78
|
+
type: 'custom'
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
name: 'serve-instrument',
|
|
83
|
+
async transformIndexHtml(html) {
|
|
84
|
+
return html.replace('{{BUNDLE}}', await getEncodedBundle());
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
runtime(),
|
|
88
|
+
tailwindcss()
|
|
89
|
+
],
|
|
90
|
+
root: path.join(import.meta.dirname, 'client'),
|
|
91
|
+
server: {
|
|
92
|
+
open: false,
|
|
93
|
+
port
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
await server.listen();
|
|
97
|
+
console.log(`Listening on http://localhost:${port}`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
program.parse();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Open Data Capture</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div data-initial-bundle="{{BUNDLE}}" id="root"></div>
|
|
10
|
+
<script type="module" src="./main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { StrictMode, useEffect, useState } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
|
|
4
|
+
import { i18n } from '@douglasneuroinformatics/libui/i18n';
|
|
5
|
+
import { ScalarInstrumentRenderer } from '@opendatacapture/react-core';
|
|
6
|
+
import { decodeBase64ToUnicode } from '@opendatacapture/runtime-internal';
|
|
7
|
+
|
|
8
|
+
import '@opendatacapture/react-core/globals.css';
|
|
9
|
+
|
|
10
|
+
const container = document.getElementById('root')!;
|
|
11
|
+
const initialBundle = container.getAttribute('data-initial-bundle')!;
|
|
12
|
+
const root = createRoot(container);
|
|
13
|
+
|
|
14
|
+
i18n.init({ translations: {} });
|
|
15
|
+
|
|
16
|
+
const App = () => {
|
|
17
|
+
const [encodedBundle, setEncodedBundle] = useState(initialBundle);
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
import.meta.hot!.on('update-bundle', (data: { encodedBundle: string }) => {
|
|
21
|
+
setEncodedBundle(data.encodedBundle);
|
|
22
|
+
});
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="container h-screen py-16">
|
|
27
|
+
<ScalarInstrumentRenderer
|
|
28
|
+
key={encodedBundle}
|
|
29
|
+
target={{ bundle: decodeBase64ToUnicode(encodedBundle), id: null! }}
|
|
30
|
+
onSubmit={({ data }) => {
|
|
31
|
+
// eslint-disable-next-line no-alert
|
|
32
|
+
alert(JSON.stringify({ _message: 'The following data will be submitted', data }, null, 2));
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
root.render(
|
|
40
|
+
<StrictMode>
|
|
41
|
+
<App />
|
|
42
|
+
</StrictMode>
|
|
43
|
+
);
|
package/tsconfig.json
ADDED