@druid-ui/build 2.0.0 → 2.1.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/bin/index.js CHANGED
@@ -3,10 +3,10 @@
3
3
  // src/index.ts
4
4
  import { componentize } from "@bytecodealliance/componentize-js";
5
5
  import { build } from "esbuild";
6
- import fs3 from "node:fs/promises";
6
+ import fs2 from "node:fs/promises";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { createRequire } from "node:module";
9
- import { basename as basename2 } from "node:path";
9
+ import { dirname, basename as basename2 } from "node:path";
10
10
 
11
11
  // src/utils.ts
12
12
  import path from "node:path";
@@ -39,37 +39,18 @@ async function getWitFolder(withFiles, witPath = "wit-out") {
39
39
  }
40
40
 
41
41
  // src/esbuild-plugin.ts
42
- import fs2 from "fs/promises";
43
42
  function druidExtensionPlugin() {
44
43
  return {
45
44
  name: "druid-extension",
46
45
  setup(build2) {
47
- build2.onLoad({ filter: /\.[jt]sx?$/ }, async (args2) => {
48
- let code = await fs2.readFile(args2.path, "utf8");
49
- if (!code.includes("druid:ui/")) return;
50
- const transformNamed = (_, vars, name) => {
51
- const mapped = vars.split(",").map((v) => {
52
- const [left, right] = v.split(/\s+as\s+/).map((s) => s.trim());
53
- return right ? `${left}: ${right}` : left;
54
- }).join(", ");
55
- return `const { ${mapped} } = window["druid-extension"]["${name}"];`;
56
- };
57
- code = code.replace(
58
- /import\s*{\s*([^}]+)\s*}\s*from\s*["'](druid:ui\/[^"']+)["'];?/g,
59
- transformNamed
60
- ).replace(
61
- /import\s+\*\s+as\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
62
- (_, id, name) => `const ${id} = window["druid-extension"]["${name}"];`
63
- ).replace(
64
- /import\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
65
- (_, id, name) => `const ${id} = window["druid-extension"]["${name}"];`
66
- );
67
- let loader = "js";
68
- if (args2.path.endsWith(".ts")) loader = "ts";
69
- else if (args2.path.endsWith(".tsx")) loader = "tsx";
70
- else if (args2.path.endsWith(".jsx")) loader = "jsx";
71
- return { contents: code, loader };
72
- });
46
+ build2.onResolve({ filter: /^druid:ui\// }, (args2) => ({
47
+ path: args2.path,
48
+ namespace: "druid-ext"
49
+ }));
50
+ build2.onLoad({ filter: /.*/, namespace: "druid-ext" }, (args2) => ({
51
+ contents: `module.exports = window["druid-extension"]["${args2.path}"];`,
52
+ loader: "js"
53
+ }));
73
54
  }
74
55
  };
75
56
  }
@@ -85,7 +66,7 @@ var getBundleFileName = (entryFile2, suffix = "") => {
85
66
  return outfilename;
86
67
  };
87
68
  async function buildWasm(entryFile2, outfolder2 = "./dist", witExtension) {
88
- await fs3.mkdir(outfolder2, { recursive: true });
69
+ await fs2.mkdir(outfolder2, { recursive: true });
89
70
  const outfilename = getBundleFileName(entryFile2);
90
71
  const outfile = outfolder2 + "/" + outfilename;
91
72
  console.log("Building", entryFile2, "to", outfile);
@@ -112,12 +93,12 @@ async function buildWasm(entryFile2, outfolder2 = "./dist", witExtension) {
112
93
  disableFeatures: ["clocks", "http", "random", "stdio", "fetch-event"]
113
94
  });
114
95
  const name = basename2(entryFile2, ".tsx");
115
- await fs3.writeFile(outfolder2 + "/" + name + ".wasm", result.component);
96
+ await fs2.writeFile(outfolder2 + "/" + name + ".wasm", result.component);
116
97
  }
117
98
  async function buildRaw(entryFile2, outfolder2 = "./dist") {
118
99
  const outfilename = getBundleFileName(entryFile2, "-raw");
119
100
  const outfile = outfolder2 + "/" + outfilename;
120
- const rawPath = require2.resolve("@druid-ui/component/raw");
101
+ const componentDir = dirname(require2.resolve("@druid-ui/component"));
121
102
  await build({
122
103
  entryPoints: [entryFile2],
123
104
  bundle: true,
@@ -126,22 +107,29 @@ async function buildRaw(entryFile2, outfolder2 = "./dist") {
126
107
  outfile,
127
108
  plugins: [druidExtensionPlugin()],
128
109
  alias: {
129
- "@druid-ui/component": rawPath
110
+ "@druid-ui/component": componentDir
130
111
  }
131
112
  });
132
113
  }
133
114
 
134
115
  // src/cmd/index.ts
116
+ import { watch } from "node:fs";
117
+ import { resolve as resolve2 } from "node:path";
135
118
  console.log("Druid UI Build Tool");
136
119
  var args = process.argv;
137
120
  var duBuildRaw = false;
138
121
  var worldName = "druid-ui";
122
+ var watchMode = false;
139
123
  var witFiles = [];
140
124
  args = args.filter((arg) => {
141
125
  if (arg === "--raw") {
142
126
  duBuildRaw = true;
143
127
  return false;
144
128
  }
129
+ if (arg === "--watch" || arg === "-w") {
130
+ watchMode = true;
131
+ return false;
132
+ }
145
133
  if (arg.startsWith("--wit=")) {
146
134
  witFiles.push(arg.slice(6));
147
135
  return false;
@@ -155,18 +143,42 @@ args = args.filter((arg) => {
155
143
  var entryFile = args[2];
156
144
  if (!entryFile) {
157
145
  console.error(
158
- "Usage: ./cli <entry-file> [out-folder] [--world-name=<name>] [--wit=<file>] [--raw]"
146
+ "Usage: ./cli <entry-file> [out-folder] [--world-name=<name>] [--wit=<file>] [--raw] [--watch|-w]"
159
147
  );
160
148
  process.exit(1);
161
149
  }
162
150
  var outfolder = args[3] || "./dist";
163
- console.log(`Building ${entryFile} to ${outfolder}...`);
164
- if (duBuildRaw) {
165
- await buildRaw(entryFile, outfolder);
166
- } else {
167
- await buildWasm(entryFile, outfolder, {
168
- worldName,
169
- files: witFiles
151
+ async function doBuild() {
152
+ console.log(`Building ${entryFile} to ${outfolder}...`);
153
+ try {
154
+ if (duBuildRaw) {
155
+ await buildRaw(entryFile, outfolder);
156
+ } else {
157
+ await buildWasm(entryFile, outfolder, {
158
+ worldName,
159
+ files: witFiles
160
+ });
161
+ }
162
+ console.log("Build complete.");
163
+ } catch (error) {
164
+ console.error("Build failed:", error);
165
+ }
166
+ }
167
+ await doBuild();
168
+ if (watchMode) {
169
+ console.log("Watching for changes...");
170
+ const srcDir = resolve2(entryFile, "..");
171
+ let building = false;
172
+ watch(srcDir, { recursive: true }, async (_eventType, filename) => {
173
+ if (filename && (filename.endsWith(".tsx") || filename.endsWith(".ts") || filename.endsWith(".jsx") || filename.endsWith(".js"))) {
174
+ if (building) return;
175
+ building = true;
176
+ console.log(`
177
+ File changed: ${filename}`);
178
+ await doBuild();
179
+ building = false;
180
+ }
181
+ });
182
+ await new Promise(() => {
170
183
  });
171
184
  }
172
- console.log("Build complete.");
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // src/index.ts
2
2
  import { componentize } from "@bytecodealliance/componentize-js";
3
3
  import { build } from "esbuild";
4
- import fs3 from "node:fs/promises";
4
+ import fs2 from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createRequire } from "node:module";
7
- import { basename as basename2 } from "node:path";
7
+ import { dirname as dirname2, basename as basename2 } from "node:path";
8
8
 
9
9
  // src/utils.ts
10
10
  import path from "node:path";
@@ -37,37 +37,18 @@ async function getWitFolder(withFiles, witPath = "wit-out") {
37
37
  }
38
38
 
39
39
  // src/esbuild-plugin.ts
40
- import fs2 from "fs/promises";
41
40
  function druidExtensionPlugin() {
42
41
  return {
43
42
  name: "druid-extension",
44
43
  setup(build2) {
45
- build2.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
46
- let code = await fs2.readFile(args.path, "utf8");
47
- if (!code.includes("druid:ui/")) return;
48
- const transformNamed = (_, vars, name) => {
49
- const mapped = vars.split(",").map((v) => {
50
- const [left, right] = v.split(/\s+as\s+/).map((s) => s.trim());
51
- return right ? `${left}: ${right}` : left;
52
- }).join(", ");
53
- return `const { ${mapped} } = window["druid-extension"]["${name}"];`;
54
- };
55
- code = code.replace(
56
- /import\s*{\s*([^}]+)\s*}\s*from\s*["'](druid:ui\/[^"']+)["'];?/g,
57
- transformNamed
58
- ).replace(
59
- /import\s+\*\s+as\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
60
- (_, id, name) => `const ${id} = window["druid-extension"]["${name}"];`
61
- ).replace(
62
- /import\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
63
- (_, id, name) => `const ${id} = window["druid-extension"]["${name}"];`
64
- );
65
- let loader = "js";
66
- if (args.path.endsWith(".ts")) loader = "ts";
67
- else if (args.path.endsWith(".tsx")) loader = "tsx";
68
- else if (args.path.endsWith(".jsx")) loader = "jsx";
69
- return { contents: code, loader };
70
- });
44
+ build2.onResolve({ filter: /^druid:ui\// }, (args) => ({
45
+ path: args.path,
46
+ namespace: "druid-ext"
47
+ }));
48
+ build2.onLoad({ filter: /.*/, namespace: "druid-ext" }, (args) => ({
49
+ contents: `module.exports = window["druid-extension"]["${args.path}"];`,
50
+ loader: "js"
51
+ }));
71
52
  }
72
53
  };
73
54
  }
@@ -105,7 +86,7 @@ var getBundleFileName = (entryFile, suffix = "") => {
105
86
  return outfilename;
106
87
  };
107
88
  async function buildWasm(entryFile, outfolder = "./dist", witExtension) {
108
- await fs3.mkdir(outfolder, { recursive: true });
89
+ await fs2.mkdir(outfolder, { recursive: true });
109
90
  const outfilename = getBundleFileName(entryFile);
110
91
  const outfile = outfolder + "/" + outfilename;
111
92
  console.log("Building", entryFile, "to", outfile);
@@ -132,12 +113,12 @@ async function buildWasm(entryFile, outfolder = "./dist", witExtension) {
132
113
  disableFeatures: ["clocks", "http", "random", "stdio", "fetch-event"]
133
114
  });
134
115
  const name = basename2(entryFile, ".tsx");
135
- await fs3.writeFile(outfolder + "/" + name + ".wasm", result.component);
116
+ await fs2.writeFile(outfolder + "/" + name + ".wasm", result.component);
136
117
  }
137
118
  async function buildRaw(entryFile, outfolder = "./dist") {
138
119
  const outfilename = getBundleFileName(entryFile, "-raw");
139
120
  const outfile = outfolder + "/" + outfilename;
140
- const rawPath = require2.resolve("@druid-ui/component/raw");
121
+ const componentDir = dirname2(require2.resolve("@druid-ui/component"));
141
122
  await build({
142
123
  entryPoints: [entryFile],
143
124
  bundle: true,
@@ -146,7 +127,7 @@ async function buildRaw(entryFile, outfolder = "./dist") {
146
127
  outfile,
147
128
  plugins: [druidExtensionPlugin()],
148
129
  alias: {
149
- "@druid-ui/component": rawPath
130
+ "@druid-ui/component": componentDir
150
131
  }
151
132
  });
152
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../src/esbuild-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,wBAAgB,oBAAoB,IAAI,MAAM,CAgD7C"}
1
+ {"version":3,"file":"esbuild-plugin.d.ts","sourceRoot":"","sources":["../../src/esbuild-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,wBAAgB,oBAAoB,IAAI,MAAM,CAuB7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AASD,wBAAsB,SAAS,CAC7B,SAAS,KAAA,EACT,SAAS,SAAW,EACpB,YAAY,CAAC,EAAE,YAAY,iBAsC5B;AAED,wBAAsB,QAAQ,CAAC,SAAS,KAAA,EAAE,SAAS,SAAW,iBAiB7D;AAED,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AASD,wBAAsB,SAAS,CAC7B,SAAS,KAAA,EACT,SAAS,SAAW,EACpB,YAAY,CAAC,EAAE,YAAY,iBAsC5B;AAED,wBAAsB,QAAQ,CAAC,SAAS,KAAA,EAAE,SAAS,SAAW,iBAoB7D;AAED,cAAc,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@druid-ui/build",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/types/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@bytecodealliance/componentize-js": "^0.19.3",
32
- "@bytecodealliance/jco": "^1.15.3",
33
- "@bytecodealliance/preview2-shim": "^0.17.5",
32
+ "@bytecodealliance/jco": "^1.17.5",
33
+ "@bytecodealliance/preview2-shim": "^0.17.7",
34
34
  "esbuild": "^0.25.11"
35
35
  },
36
36
  "devDependencies": {
package/src/cmd/index.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { buildWasm, buildRaw } from "../";
4
+ import { watch } from "node:fs";
5
+ import { resolve } from "node:path";
6
+
4
7
  console.log("Druid UI Build Tool");
5
8
  let args = process.argv;
6
9
 
7
10
  let duBuildRaw = false;
8
11
  let worldName = "druid-ui";
12
+ let watchMode = false;
9
13
  const witFiles: string[] = [];
10
14
 
11
15
  args = args.filter((arg) => {
@@ -13,6 +17,10 @@ args = args.filter((arg) => {
13
17
  duBuildRaw = true;
14
18
  return false;
15
19
  }
20
+ if (arg === "--watch" || arg === "-w") {
21
+ watchMode = true;
22
+ return false;
23
+ }
16
24
  if (arg.startsWith("--wit=")) {
17
25
  witFiles.push(arg.slice(6));
18
26
  return false;
@@ -28,20 +36,53 @@ const entryFile = args[2];
28
36
 
29
37
  if (!entryFile) {
30
38
  console.error(
31
- "Usage: ./cli <entry-file> [out-folder] [--world-name=<name>] [--wit=<file>] [--raw]",
39
+ "Usage: ./cli <entry-file> [out-folder] [--world-name=<name>] [--wit=<file>] [--raw] [--watch|-w]",
32
40
  );
33
41
  process.exit(1);
34
42
  }
35
43
 
36
44
  const outfolder = args[3] || "./dist";
37
- console.log(`Building ${entryFile} to ${outfolder}...`);
38
- if (duBuildRaw) {
39
- await buildRaw(entryFile, outfolder);
40
- } else {
41
- await buildWasm(entryFile, outfolder, {
42
- worldName,
43
- files: witFiles,
44
- });
45
+
46
+ async function doBuild() {
47
+ console.log(`Building ${entryFile} to ${outfolder}...`);
48
+ try {
49
+ if (duBuildRaw) {
50
+ await buildRaw(entryFile, outfolder);
51
+ } else {
52
+ await buildWasm(entryFile, outfolder, {
53
+ worldName,
54
+ files: witFiles,
55
+ });
56
+ }
57
+ console.log("Build complete.");
58
+ } catch (error) {
59
+ console.error("Build failed:", error);
60
+ }
45
61
  }
46
62
 
47
- console.log("Build complete.");
63
+ await doBuild();
64
+
65
+ if (watchMode) {
66
+ console.log("Watching for changes...");
67
+ const srcDir = resolve(entryFile, "..");
68
+ let building = false;
69
+
70
+ watch(srcDir, { recursive: true }, async (_eventType, filename) => {
71
+ if (
72
+ filename &&
73
+ (filename.endsWith(".tsx") ||
74
+ filename.endsWith(".ts") ||
75
+ filename.endsWith(".jsx") ||
76
+ filename.endsWith(".js"))
77
+ ) {
78
+ if (building) return;
79
+ building = true;
80
+ console.log(`\nFile changed: ${filename}`);
81
+ await doBuild();
82
+ building = false;
83
+ }
84
+ });
85
+
86
+ // Keep the process running
87
+ await new Promise(() => {});
88
+ }
@@ -1,52 +1,26 @@
1
1
  import type { Plugin } from "esbuild";
2
- import fs from "fs/promises";
3
2
 
4
3
  export function druidExtensionPlugin(): Plugin {
5
4
  return {
6
5
  name: "druid-extension",
7
6
  setup(build) {
8
- build.onLoad({ filter: /\.[jt]sx?$/ }, async (args) => {
9
- let code = await fs.readFile(args.path, "utf8");
10
- if (!code.includes("druid:ui/")) return;
7
+ // Intercept all druid:ui/* imports at the resolution level.
8
+ // This is much more robust than regex-replacing file content,
9
+ // because it doesn't depend on reading source files from disk.
10
+ build.onResolve({ filter: /^druid:ui\// }, (args) => ({
11
+ path: args.path,
12
+ namespace: "druid-ext",
13
+ }));
11
14
 
12
- const transformNamed = (_: string, vars: string, name: string) => {
13
- const mapped = vars
14
- .split(",")
15
- .map((v) => {
16
- const [left, right] = v.split(/\s+as\s+/).map((s) => s.trim());
17
- return right ? `${left}: ${right}` : left;
18
- })
19
- .join(", ");
20
- return `const { ${mapped} } = window["druid-extension"]["${name}"];`;
21
- };
22
-
23
- code = code
24
- // named imports
25
- .replace(
26
- /import\s*{\s*([^}]+)\s*}\s*from\s*["'](druid:ui\/[^"']+)["'];?/g,
27
- transformNamed
28
- )
29
- // namespace imports
30
- .replace(
31
- /import\s+\*\s+as\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
32
- (_, id: string, name: string) =>
33
- `const ${id} = window["druid-extension"]["${name}"];`
34
- )
35
- // default imports
36
- .replace(
37
- /import\s+([\w$]+)\s+from\s*["'](druid:ui\/[^"']+)["'];?/g,
38
- (_, id: string, name: string) =>
39
- `const ${id} = window["druid-extension"]["${name}"];`
40
- );
41
-
42
- // choose loader based on file extension
43
- let loader: "ts" | "tsx" | "js" | "jsx" = "js";
44
- if (args.path.endsWith(".ts")) loader = "ts";
45
- else if (args.path.endsWith(".tsx")) loader = "tsx";
46
- else if (args.path.endsWith(".jsx")) loader = "jsx";
47
-
48
- return { contents: code, loader };
49
- });
15
+ // Provide a virtual CJS module that re-exports everything from
16
+ // window["druid-extension"]["<module-path>"].
17
+ // esbuild handles the CJS→ESM interop automatically, so
18
+ // `import { d } from "druid:ui/ui"` correctly maps to
19
+ // `window["druid-extension"]["druid:ui/ui"].d`.
20
+ build.onLoad({ filter: /.*/, namespace: "druid-ext" }, (args) => ({
21
+ contents: `module.exports = window["druid-extension"]["${args.path}"];`,
22
+ loader: "js",
23
+ }));
50
24
  },
51
25
  };
52
26
  }
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ const getBundleFileName = (entryFile, suffix = "") => {
25
25
  export async function buildWasm(
26
26
  entryFile,
27
27
  outfolder = "./dist",
28
- witExtension?: WitExtension
28
+ witExtension?: WitExtension,
29
29
  ) {
30
30
  await fs.mkdir(outfolder, { recursive: true });
31
31
 
@@ -70,7 +70,10 @@ export async function buildRaw(entryFile, outfolder = "./dist") {
70
70
 
71
71
  const outfile = outfolder + "/" + outfilename;
72
72
 
73
- const rawPath = require.resolve("@druid-ui/component/raw");
73
+ // Alias to the dist/ directory (not a specific file) so subpath imports like
74
+ // @druid-ui/component/jsx-runtime also resolve correctly.
75
+ // This resolves to dist/ which contains index.js, jsx-runtime.js, etc.
76
+ const componentDir = dirname(require.resolve("@druid-ui/component"));
74
77
  await build({
75
78
  entryPoints: [entryFile],
76
79
  bundle: true,
@@ -79,7 +82,7 @@ export async function buildRaw(entryFile, outfolder = "./dist") {
79
82
  outfile: outfile,
80
83
  plugins: [druidExtensionPlugin()],
81
84
  alias: {
82
- "@druid-ui/component": rawPath,
85
+ "@druid-ui/component": componentDir,
83
86
  },
84
87
  });
85
88
  }
package/wit/druid-ui.wit CHANGED
@@ -30,8 +30,6 @@ interface utils {
30
30
  checked: func() -> bool;
31
31
  }
32
32
 
33
- record context {
34
- path: string
35
- }
33
+ type context = list<tuple<string, string>>;
36
34
 
37
35
  }