@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.
@@ -0,0 +1,5 @@
1
+
2
+ 
3
+ > @opendatacapture/serve-instrument@0.0.0 lint /Users/joshua/Developer/DouglasNeuroinformatics/OpenDataCapture/packages/serve-instrument
4
+ > tsc && eslint --fix src
5
+
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
+ }
@@ -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
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
5
+ "paths": {
6
+ "/runtime/v1/*": ["../../runtime/v1/dist/*"]
7
+ },
8
+ "types": ["vite/client"]
9
+ },
10
+ "include": ["scripts/*", "src/**/*"]
11
+ }